web/lib/Zend/Loader/PluginLoader.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Loader
       
    17  * @subpackage PluginLoader
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: PluginLoader.php 22603 2010-07-17 00:02:10Z ramon $
       
    21  */
       
    22 
       
    23 /** Zend_Loader_PluginLoader_Interface */
       
    24 require_once 'Zend/Loader/PluginLoader/Interface.php';
       
    25 
       
    26 /** Zend_Loader */
       
    27 require_once 'Zend/Loader.php';
       
    28 
       
    29 /**
       
    30  * Generic plugin class loader
       
    31  *
       
    32  * @category   Zend
       
    33  * @package    Zend_Loader
       
    34  * @subpackage PluginLoader
       
    35  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    36  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    37  */
       
    38 class Zend_Loader_PluginLoader implements Zend_Loader_PluginLoader_Interface
       
    39 {
       
    40     /**
       
    41      * Class map cache file
       
    42      * @var string
       
    43      */
       
    44     protected static $_includeFileCache;
       
    45 
       
    46     /**
       
    47      * Instance loaded plugin paths
       
    48      *
       
    49      * @var array
       
    50      */
       
    51     protected $_loadedPluginPaths = array();
       
    52 
       
    53     /**
       
    54      * Instance loaded plugins
       
    55      *
       
    56      * @var array
       
    57      */
       
    58     protected $_loadedPlugins = array();
       
    59 
       
    60     /**
       
    61      * Instance registry property
       
    62      *
       
    63      * @var array
       
    64      */
       
    65     protected $_prefixToPaths = array();
       
    66 
       
    67     /**
       
    68      * Statically loaded plugin path mappings
       
    69      *
       
    70      * @var array
       
    71      */
       
    72     protected static $_staticLoadedPluginPaths = array();
       
    73 
       
    74     /**
       
    75      * Statically loaded plugins
       
    76      *
       
    77      * @var array
       
    78      */
       
    79     protected static $_staticLoadedPlugins = array();
       
    80 
       
    81     /**
       
    82      * Static registry property
       
    83      *
       
    84      * @var array
       
    85      */
       
    86     protected static $_staticPrefixToPaths = array();
       
    87 
       
    88     /**
       
    89      * Whether to use a statically named registry for loading plugins
       
    90      *
       
    91      * @var string|null
       
    92      */
       
    93     protected $_useStaticRegistry = null;
       
    94 
       
    95     /**
       
    96      * Constructor
       
    97      *
       
    98      * @param array $prefixToPaths
       
    99      * @param string $staticRegistryName OPTIONAL
       
   100      */
       
   101     public function __construct(Array $prefixToPaths = array(), $staticRegistryName = null)
       
   102     {
       
   103         if (is_string($staticRegistryName) && !empty($staticRegistryName)) {
       
   104             $this->_useStaticRegistry = $staticRegistryName;
       
   105             if(!isset(self::$_staticPrefixToPaths[$staticRegistryName])) {
       
   106                 self::$_staticPrefixToPaths[$staticRegistryName] = array();
       
   107             }
       
   108             if(!isset(self::$_staticLoadedPlugins[$staticRegistryName])) {
       
   109                 self::$_staticLoadedPlugins[$staticRegistryName] = array();
       
   110             }
       
   111         }
       
   112 
       
   113         foreach ($prefixToPaths as $prefix => $path) {
       
   114             $this->addPrefixPath($prefix, $path);
       
   115         }
       
   116     }
       
   117 
       
   118     /**
       
   119      * Format prefix for internal use
       
   120      *
       
   121      * @param  string $prefix
       
   122      * @return string
       
   123      */
       
   124     protected function _formatPrefix($prefix)
       
   125     {
       
   126         if($prefix == "") {
       
   127             return $prefix;
       
   128         }
       
   129 
       
   130         $last = strlen($prefix) - 1;
       
   131         if ($prefix{$last} == '\\') {
       
   132             return $prefix;
       
   133         }
       
   134 
       
   135         return rtrim($prefix, '_') . '_';
       
   136     }
       
   137 
       
   138     /**
       
   139      * Add prefixed paths to the registry of paths
       
   140      *
       
   141      * @param string $prefix
       
   142      * @param string $path
       
   143      * @return Zend_Loader_PluginLoader
       
   144      */
       
   145     public function addPrefixPath($prefix, $path)
       
   146     {
       
   147         if (!is_string($prefix) || !is_string($path)) {
       
   148             require_once 'Zend/Loader/PluginLoader/Exception.php';
       
   149             throw new Zend_Loader_PluginLoader_Exception('Zend_Loader_PluginLoader::addPrefixPath() method only takes strings for prefix and path.');
       
   150         }
       
   151 
       
   152         $prefix = $this->_formatPrefix($prefix);
       
   153         $path   = rtrim($path, '/\\') . '/';
       
   154 
       
   155         if ($this->_useStaticRegistry) {
       
   156             self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix][] = $path;
       
   157         } else {
       
   158             if (!isset($this->_prefixToPaths[$prefix])) {
       
   159                 $this->_prefixToPaths[$prefix] = array();
       
   160             }
       
   161             if (!in_array($path, $this->_prefixToPaths[$prefix])) {
       
   162                 $this->_prefixToPaths[$prefix][] = $path;
       
   163             }
       
   164         }
       
   165         return $this;
       
   166     }
       
   167 
       
   168     /**
       
   169      * Get path stack
       
   170      *
       
   171      * @param  string $prefix
       
   172      * @return false|array False if prefix does not exist, array otherwise
       
   173      */
       
   174     public function getPaths($prefix = null)
       
   175     {
       
   176         if ((null !== $prefix) && is_string($prefix)) {
       
   177             $prefix = $this->_formatPrefix($prefix);
       
   178             if ($this->_useStaticRegistry) {
       
   179                 if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
       
   180                     return self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix];
       
   181                 }
       
   182 
       
   183                 return false;
       
   184             }
       
   185 
       
   186             if (isset($this->_prefixToPaths[$prefix])) {
       
   187                 return $this->_prefixToPaths[$prefix];
       
   188             }
       
   189 
       
   190             return false;
       
   191         }
       
   192 
       
   193         if ($this->_useStaticRegistry) {
       
   194             return self::$_staticPrefixToPaths[$this->_useStaticRegistry];
       
   195         }
       
   196 
       
   197         return $this->_prefixToPaths;
       
   198     }
       
   199 
       
   200     /**
       
   201      * Clear path stack
       
   202      *
       
   203      * @param  string $prefix
       
   204      * @return bool False only if $prefix does not exist
       
   205      */
       
   206     public function clearPaths($prefix = null)
       
   207     {
       
   208         if ((null !== $prefix) && is_string($prefix)) {
       
   209             $prefix = $this->_formatPrefix($prefix);
       
   210             if ($this->_useStaticRegistry) {
       
   211                 if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
       
   212                     unset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix]);
       
   213                     return true;
       
   214                 }
       
   215 
       
   216                 return false;
       
   217             }
       
   218 
       
   219             if (isset($this->_prefixToPaths[$prefix])) {
       
   220                 unset($this->_prefixToPaths[$prefix]);
       
   221                 return true;
       
   222             }
       
   223 
       
   224             return false;
       
   225         }
       
   226 
       
   227         if ($this->_useStaticRegistry) {
       
   228             self::$_staticPrefixToPaths[$this->_useStaticRegistry] = array();
       
   229         } else {
       
   230             $this->_prefixToPaths = array();
       
   231         }
       
   232 
       
   233         return true;
       
   234     }
       
   235 
       
   236     /**
       
   237      * Remove a prefix (or prefixed-path) from the registry
       
   238      *
       
   239      * @param string $prefix
       
   240      * @param string $path OPTIONAL
       
   241      * @return Zend_Loader_PluginLoader
       
   242      */
       
   243     public function removePrefixPath($prefix, $path = null)
       
   244     {
       
   245         $prefix = $this->_formatPrefix($prefix);
       
   246         if ($this->_useStaticRegistry) {
       
   247             $registry =& self::$_staticPrefixToPaths[$this->_useStaticRegistry];
       
   248         } else {
       
   249             $registry =& $this->_prefixToPaths;
       
   250         }
       
   251 
       
   252         if (!isset($registry[$prefix])) {
       
   253             require_once 'Zend/Loader/PluginLoader/Exception.php';
       
   254             throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' was not found in the PluginLoader.');
       
   255         }
       
   256 
       
   257         if ($path != null) {
       
   258             $pos = array_search($path, $registry[$prefix]);
       
   259             if (false === $pos) {
       
   260                 require_once 'Zend/Loader/PluginLoader/Exception.php';
       
   261                 throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' / Path ' . $path . ' was not found in the PluginLoader.');
       
   262             }
       
   263             unset($registry[$prefix][$pos]);
       
   264         } else {
       
   265             unset($registry[$prefix]);
       
   266         }
       
   267 
       
   268         return $this;
       
   269     }
       
   270 
       
   271     /**
       
   272      * Normalize plugin name
       
   273      *
       
   274      * @param  string $name
       
   275      * @return string
       
   276      */
       
   277     protected function _formatName($name)
       
   278     {
       
   279         return ucfirst((string) $name);
       
   280     }
       
   281 
       
   282     /**
       
   283      * Whether or not a Plugin by a specific name is loaded
       
   284      *
       
   285      * @param string $name
       
   286      * @return Zend_Loader_PluginLoader
       
   287      */
       
   288     public function isLoaded($name)
       
   289     {
       
   290         $name = $this->_formatName($name);
       
   291         if ($this->_useStaticRegistry) {
       
   292             return isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]);
       
   293         }
       
   294 
       
   295         return isset($this->_loadedPlugins[$name]);
       
   296     }
       
   297 
       
   298     /**
       
   299      * Return full class name for a named plugin
       
   300      *
       
   301      * @param string $name
       
   302      * @return string|false False if class not found, class name otherwise
       
   303      */
       
   304     public function getClassName($name)
       
   305     {
       
   306         $name = $this->_formatName($name);
       
   307         if ($this->_useStaticRegistry
       
   308             && isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name])
       
   309         ) {
       
   310             return self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name];
       
   311         } elseif (isset($this->_loadedPlugins[$name])) {
       
   312             return $this->_loadedPlugins[$name];
       
   313         }
       
   314 
       
   315         return false;
       
   316     }
       
   317 
       
   318     /**
       
   319      * Get path to plugin class
       
   320      *
       
   321      * @param  mixed $name
       
   322      * @return string|false False if not found
       
   323      */
       
   324     public function getClassPath($name)
       
   325     {
       
   326         $name = $this->_formatName($name);
       
   327         if ($this->_useStaticRegistry
       
   328             && !empty(self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name])
       
   329         ) {
       
   330             return self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name];
       
   331         } elseif (!empty($this->_loadedPluginPaths[$name])) {
       
   332             return $this->_loadedPluginPaths[$name];
       
   333         }
       
   334 
       
   335         if ($this->isLoaded($name)) {
       
   336             $class = $this->getClassName($name);
       
   337             $r     = new ReflectionClass($class);
       
   338             $path  = $r->getFileName();
       
   339             if ($this->_useStaticRegistry) {
       
   340                 self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = $path;
       
   341             } else {
       
   342                 $this->_loadedPluginPaths[$name] = $path;
       
   343             }
       
   344             return $path;
       
   345         }
       
   346 
       
   347         return false;
       
   348     }
       
   349 
       
   350     /**
       
   351      * Load a plugin via the name provided
       
   352      *
       
   353      * @param  string $name
       
   354      * @param  bool $throwExceptions Whether or not to throw exceptions if the
       
   355      * class is not resolved
       
   356      * @return string|false Class name of loaded class; false if $throwExceptions
       
   357      * if false and no class found
       
   358      * @throws Zend_Loader_Exception if class not found
       
   359      */
       
   360     public function load($name, $throwExceptions = true)
       
   361     {
       
   362         $name = $this->_formatName($name);
       
   363         if ($this->isLoaded($name)) {
       
   364             return $this->getClassName($name);
       
   365         }
       
   366 
       
   367         if ($this->_useStaticRegistry) {
       
   368             $registry = self::$_staticPrefixToPaths[$this->_useStaticRegistry];
       
   369         } else {
       
   370             $registry = $this->_prefixToPaths;
       
   371         }
       
   372 
       
   373         $registry  = array_reverse($registry, true);
       
   374         $found     = false;
       
   375         $classFile = str_replace('_', DIRECTORY_SEPARATOR, $name) . '.php';
       
   376         $incFile   = self::getIncludeFileCache();
       
   377         foreach ($registry as $prefix => $paths) {
       
   378             $className = $prefix . $name;
       
   379 
       
   380             if (class_exists($className, false)) {
       
   381                 $found = true;
       
   382                 break;
       
   383             }
       
   384 
       
   385             $paths     = array_reverse($paths, true);
       
   386 
       
   387             foreach ($paths as $path) {
       
   388                 $loadFile = $path . $classFile;
       
   389                 if (Zend_Loader::isReadable($loadFile)) {
       
   390                     include_once $loadFile;
       
   391                     if (class_exists($className, false)) {
       
   392                         if (null !== $incFile) {
       
   393                             self::_appendIncFile($loadFile);
       
   394                         }
       
   395                         $found = true;
       
   396                         break 2;
       
   397                     }
       
   398                 }
       
   399             }
       
   400         }
       
   401 
       
   402         if (!$found) {
       
   403             if (!$throwExceptions) {
       
   404                 return false;
       
   405             }
       
   406 
       
   407             $message = "Plugin by name '$name' was not found in the registry; used paths:";
       
   408             foreach ($registry as $prefix => $paths) {
       
   409                 $message .= "\n$prefix: " . implode(PATH_SEPARATOR, $paths);
       
   410             }
       
   411             require_once 'Zend/Loader/PluginLoader/Exception.php';
       
   412             throw new Zend_Loader_PluginLoader_Exception($message);
       
   413        }
       
   414 
       
   415         if ($this->_useStaticRegistry) {
       
   416             self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]     = $className;
       
   417         } else {
       
   418             $this->_loadedPlugins[$name]     = $className;
       
   419         }
       
   420         return $className;
       
   421     }
       
   422 
       
   423     /**
       
   424      * Set path to class file cache
       
   425      *
       
   426      * Specify a path to a file that will add include_once statements for each
       
   427      * plugin class loaded. This is an opt-in feature for performance purposes.
       
   428      *
       
   429      * @param  string $file
       
   430      * @return void
       
   431      * @throws Zend_Loader_PluginLoader_Exception if file is not writeable or path does not exist
       
   432      */
       
   433     public static function setIncludeFileCache($file)
       
   434     {
       
   435         if (null === $file) {
       
   436             self::$_includeFileCache = null;
       
   437             return;
       
   438         }
       
   439 
       
   440         if (!file_exists($file) && !file_exists(dirname($file))) {
       
   441             require_once 'Zend/Loader/PluginLoader/Exception.php';
       
   442             throw new Zend_Loader_PluginLoader_Exception('Specified file does not exist and/or directory does not exist (' . $file . ')');
       
   443         }
       
   444         if (file_exists($file) && !is_writable($file)) {
       
   445             require_once 'Zend/Loader/PluginLoader/Exception.php';
       
   446             throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
       
   447         }
       
   448         if (!file_exists($file) && file_exists(dirname($file)) && !is_writable(dirname($file))) {
       
   449             require_once 'Zend/Loader/PluginLoader/Exception.php';
       
   450             throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
       
   451         }
       
   452 
       
   453         self::$_includeFileCache = $file;
       
   454     }
       
   455 
       
   456     /**
       
   457      * Retrieve class file cache path
       
   458      *
       
   459      * @return string|null
       
   460      */
       
   461     public static function getIncludeFileCache()
       
   462     {
       
   463         return self::$_includeFileCache;
       
   464     }
       
   465 
       
   466     /**
       
   467      * Append an include_once statement to the class file cache
       
   468      *
       
   469      * @param  string $incFile
       
   470      * @return void
       
   471      */
       
   472     protected static function _appendIncFile($incFile)
       
   473     {
       
   474         if (!file_exists(self::$_includeFileCache)) {
       
   475             $file = '<?php';
       
   476         } else {
       
   477             $file = file_get_contents(self::$_includeFileCache);
       
   478         }
       
   479         if (!strstr($file, $incFile)) {
       
   480             $file .= "\ninclude_once '$incFile';";
       
   481             file_put_contents(self::$_includeFileCache, $file);
       
   482         }
       
   483     }
       
   484 }