Location: PHPKode > projects > SithTemplate > SithTemplate-1.1/lib/Plugins.php
<?php
/** @file Plugins.php
 Contains plugin machinery.

 @since 1.1a0
 @author PiotrLegnica
 @license{Unlicense}
*/

/**
 Handles discovery, registration and utilization of plugins.
*/
class TemplatePlugins {
 /**
  Already loaded plugins.
 */
 private $plugins    = array();
 /**
  Elements registry.
 */
 private $elements   = array();
 /**
  Plugins' search paths. Reference to @c 'pluginsPaths' key of @ref TemplateEnviron::$settings.
 */
 public $searchPaths = array();
 
 /**
  Constructor. Also handles registration of built-ins.
  
  @param[in] $builtins Built-in elements to register
 */
 public function __construct(array $builtins) {
  $this->elements = $builtins;
 }
 
 /**
  Load single plugin.
  
  @param[in] $compiler @ref TemplateCompilerEx instance
  @param[in] $node Optional @ref TemplateNodeEx instance
  @param[in] $plugin Plugin name
  @param[in] $pluginFile Plugin's full filename (optional)
  @param[in] $noThrow If @c true, no exception will be thrown on error (boolean @c false will be returned)
  @retval true Plugin has been loaded
  @retval false Plugin is invalid/non-existant
 */
 public function load(TemplateCompilerEx $compiler, $node, $plugin, $pluginFile = null, $noThrow = false) {
  // if already loaded, then abort
  if (isset($this->plugins[$plugin])) return;
  
  // sanity check
  TemplateUtils::checkIfAllowed($compiler, 'plugin', $plugin, $node);
  if (!$pluginFile) $pluginFile = $this->findPlugin($plugin);
  $className = 'Template'.$plugin.'Plugin';
  
  if (
   $pluginFile === false     || (!include_once $pluginFile) ||
   !class_exists($className) || !TemplateUtils::doesImplement($className, 'ITemplatePlugin')
  ) {
   if ($noThrow) return false;
   
   throw new TemplateError(
    'Could not load plugin: "'.$plugin.'". Either file or main class does not exists, or is invalid',
    TemplateError::E_INVALID_PLUGIN
   );
  }
  
  // create instance and register handlers
  $pluginObj = new $className;
  $handlers  = $pluginObj->providedHandlers();
  if (!isset($handlers['tags']))    $handlers['tags']    = array();
  if (!isset($handlers['filters'])) $handlers['filters'] = array();
  if (!isset($handlers['hooks']))   $handlers['hooks']   = array();
  
  $this->register($plugin, $pluginObj, 'tag',    $handlers['tags']);
  $this->register($plugin, $pluginObj, 'filter', $handlers['filters']);
  // too special case after all
  $this->registerHooks($plugin, $pluginObj, $handlers['hooks']);
  
  $this->plugins[$plugin] = $pluginObj;
 }
 
 /**
  Load multiple plugins.
  
  @param[in] $compiler @ref TemplateCompilerEx instance
  @param[in] $node Optional @ref TemplateNodeEx instance
  @param[in] $plugins Array (plugins to look for) or boolean ('load all plugins' mode)
 */
 public function loadMultiple(TemplateCompilerEx $compiler, $node, $plugins) {
  foreach ($this->findPlugins($plugins) as $plugin => $pluginFile) {
   $this->load($compiler, $node, $plugin, $pluginFile);
  }
 }
 
 /**
  Check whether given element is registered.
  
  @param[in] $type Element type (@c 'tag', @c 'filter', @c 'hook' or @c 'plugin')
  @param[in] $name Element name
  @retval true Element is registered
  @retval false Element is not registered
 */
 public function known($type, $name) {
  if ($type == 'plugin') return isset($this->plugins[$name]);
  return isset($this->elements[$type.'s'][$name]);
 }
 
 /**
  Returns handler(s) information for given element.
  Doesn't check for element's existence.
  
  @param[in] $type Element type (@c 'tag', @c 'filter' or @c 'hook')
  @param[in] $name Element name
  @return Assoc. array
 */
 public function &get($type, $name) {
  return $this->elements[$type.'s'][$name];
 }
 
 /**
  Looks for plugin file on all search paths.
  
  @param[in] $plugin Plugin name (as string)
  @return Filename or @c false
  @sa TemplatePlugins::load
 */
 private function findPlugin($plugin) {
  $found = false;
  
  foreach ($this->searchPaths as &$path) {
   if (file_exists($path.$plugin.'.plugin.php')) {
    $found = $path.$plugin.'.plugin.php';
    break;
   }
  }
  
  return $found;
 }
 
 /**
  Looks for multiple plugins' files on all search paths.
  
  @param[in] $plugins Plugins (array or boolean)
  @return Assoc. array (plugin => file)
  @sa TemplatePlugins::loadMultiple
 */
 private function findPlugins($plugins) {
  $found = array();
 
  foreach ($this->searchPaths as &$path) {
   if (is_bool($plugins)) {
    foreach (glob($path.'*.plugin.php') as $file) {
     $plugin = mb_substr(pathinfo($file, PATHINFO_BASENAME), 0, -11);
     $found[$plugin] = $file;
    }
   } else {
    foreach ($plugins as &$plugin) {
     if (isset($found[$plugin])) continue;
    
     if (file_exists($path.$plugin.'.plugin.php')) {
      $found[$plugin] = $path.$plugin.'.plugin.php';
     }
    }
   }
  }
  
  
  if (is_array($plugins) && ($notFound = array_diff($plugins, array_keys($found)))) {
   throw new TemplateError(
    'Several of requested plugins have not been found: "'.implode(', ', $notFound).'"',
    TemplateError::E_UNKNOWN_PLUGIN
   );
  }
  return $found;
 }
 
 /**
  Registers given elements.
  
  @param[in] $plugin Plugin name
  @param[in] $pluginObj Plugin instance
  @param[in] $type Element type (@c 'tag' or @c 'filter')
  @param[in,out] $handlers Array of handlers
 */
 private function register($plugin, ITemplatePlugin $pluginObj, $type, array &$handlers) {
  foreach ($handlers as $element => &$elementInfo) {
   if (isset($this->elements[$type.'s'][$element])) {
    throw new TemplateError(
     'Element collision while loading plugin "'.$plugin.'" - '.$type.' "'.$element.'" already exists',
     TemplateError::E_INVALID_HANDLER
    );
   }
   
   $elementInfo['plugin'] = $plugin;
   if (!is_array($elementInfo['handler']) && !isset($elementInfo['standalone'])) {
    $elementInfo['handler'] = array($pluginObj, $elementInfo['handler']);
   }
   
   $this->elements[$type.'s'][$element] = $elementInfo;
  }
 }
 
 /**
  Register given hooks.
  
  @param[in] $plugin Plugin name
  @param[in] $pluginObj Plugin instance
  @param[in,out] $hooks Array of hooks
 */
 private function registerHooks($plugin, ITemplatePlugin $pluginObj, array &$hooks) {
  $allHooks = &$this->elements['hooks'];
  foreach ($hooks as $hook => &$handlers) {
   if (!isset($allHooks[$hook])) $allHooks[$hook] = array();
   
   foreach ($handlers as &$handler) {
    $handler['plugin'] = $plugin;
    
    if (!is_array($handler['handler']) && !isset($handler['standalone'])) {
     $handler['handler'] = array($pluginObj, $handler['handler']);
    }
    
    $allHooks[$hook][] = $handler;
   }
  }
 }
}
Return current item: SithTemplate