web/lib/Zend/Loader/Autoloader.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 Autoloader
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @version    $Id: Autoloader.php 23161 2010-10-19 16:08:36Z matthew $
       
    20  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    21  */
       
    22 
       
    23 /** Zend_Loader */
       
    24 require_once 'Zend/Loader.php';
       
    25 
       
    26 /**
       
    27  * Autoloader stack and namespace autoloader
       
    28  *
       
    29  * @uses       Zend_Loader_Autoloader
       
    30  * @package    Zend_Loader
       
    31  * @subpackage Autoloader
       
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    33  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    34  */
       
    35 class Zend_Loader_Autoloader
       
    36 {
       
    37     /**
       
    38      * @var Zend_Loader_Autoloader Singleton instance
       
    39      */
       
    40     protected static $_instance;
       
    41 
       
    42     /**
       
    43      * @var array Concrete autoloader callback implementations
       
    44      */
       
    45     protected $_autoloaders = array();
       
    46 
       
    47     /**
       
    48      * @var array Default autoloader callback
       
    49      */
       
    50     protected $_defaultAutoloader = array('Zend_Loader', 'loadClass');
       
    51 
       
    52     /**
       
    53      * @var bool Whether or not to act as a fallback autoloader
       
    54      */
       
    55     protected $_fallbackAutoloader = false;
       
    56 
       
    57     /**
       
    58      * @var array Callback for internal autoloader implementation
       
    59      */
       
    60     protected $_internalAutoloader;
       
    61 
       
    62     /**
       
    63      * @var array Supported namespaces 'Zend' and 'ZendX' by default.
       
    64      */
       
    65     protected $_namespaces = array(
       
    66         'Zend_'  => true,
       
    67         'ZendX_' => true,
       
    68     );
       
    69 
       
    70     /**
       
    71      * @var array Namespace-specific autoloaders
       
    72      */
       
    73     protected $_namespaceAutoloaders = array();
       
    74 
       
    75     /**
       
    76      * @var bool Whether or not to suppress file not found warnings
       
    77      */
       
    78     protected $_suppressNotFoundWarnings = false;
       
    79 
       
    80     /**
       
    81      * @var null|string
       
    82      */
       
    83     protected $_zfPath;
       
    84 
       
    85     /**
       
    86      * Retrieve singleton instance
       
    87      *
       
    88      * @return Zend_Loader_Autoloader
       
    89      */
       
    90     public static function getInstance()
       
    91     {
       
    92         if (null === self::$_instance) {
       
    93             self::$_instance = new self();
       
    94         }
       
    95         return self::$_instance;
       
    96     }
       
    97 
       
    98     /**
       
    99      * Reset the singleton instance
       
   100      *
       
   101      * @return void
       
   102      */
       
   103     public static function resetInstance()
       
   104     {
       
   105         self::$_instance = null;
       
   106     }
       
   107 
       
   108     /**
       
   109      * Autoload a class
       
   110      *
       
   111      * @param  string $class
       
   112      * @return bool
       
   113      */
       
   114     public static function autoload($class)
       
   115     {
       
   116         $self = self::getInstance();
       
   117 
       
   118         foreach ($self->getClassAutoloaders($class) as $autoloader) {
       
   119             if ($autoloader instanceof Zend_Loader_Autoloader_Interface) {
       
   120                 if ($autoloader->autoload($class)) {
       
   121                     return true;
       
   122                 }
       
   123             } elseif (is_array($autoloader)) {
       
   124                 if (call_user_func($autoloader, $class)) {
       
   125                     return true;
       
   126                 }
       
   127             } elseif (is_string($autoloader) || is_callable($autoloader)) {
       
   128                 if ($autoloader($class)) {
       
   129                     return true;
       
   130                 }
       
   131             }
       
   132         }
       
   133 
       
   134         return false;
       
   135     }
       
   136 
       
   137     /**
       
   138      * Set the default autoloader implementation
       
   139      *
       
   140      * @param  string|array $callback PHP callback
       
   141      * @return void
       
   142      */
       
   143     public function setDefaultAutoloader($callback)
       
   144     {
       
   145         if (!is_callable($callback)) {
       
   146             throw new Zend_Loader_Exception('Invalid callback specified for default autoloader');
       
   147         }
       
   148 
       
   149         $this->_defaultAutoloader = $callback;
       
   150         return $this;
       
   151     }
       
   152 
       
   153     /**
       
   154      * Retrieve the default autoloader callback
       
   155      *
       
   156      * @return string|array PHP Callback
       
   157      */
       
   158     public function getDefaultAutoloader()
       
   159     {
       
   160         return $this->_defaultAutoloader;
       
   161     }
       
   162 
       
   163     /**
       
   164      * Set several autoloader callbacks at once
       
   165      *
       
   166      * @param  array $autoloaders Array of PHP callbacks (or Zend_Loader_Autoloader_Interface implementations) to act as autoloaders
       
   167      * @return Zend_Loader_Autoloader
       
   168      */
       
   169     public function setAutoloaders(array $autoloaders)
       
   170     {
       
   171         $this->_autoloaders = $autoloaders;
       
   172         return $this;
       
   173     }
       
   174 
       
   175     /**
       
   176      * Get attached autoloader implementations
       
   177      *
       
   178      * @return array
       
   179      */
       
   180     public function getAutoloaders()
       
   181     {
       
   182         return $this->_autoloaders;
       
   183     }
       
   184 
       
   185     /**
       
   186      * Return all autoloaders for a given namespace
       
   187      *
       
   188      * @param  string $namespace
       
   189      * @return array
       
   190      */
       
   191     public function getNamespaceAutoloaders($namespace)
       
   192     {
       
   193         $namespace = (string) $namespace;
       
   194         if (!array_key_exists($namespace, $this->_namespaceAutoloaders)) {
       
   195             return array();
       
   196         }
       
   197         return $this->_namespaceAutoloaders[$namespace];
       
   198     }
       
   199 
       
   200     /**
       
   201      * Register a namespace to autoload
       
   202      *
       
   203      * @param  string|array $namespace
       
   204      * @return Zend_Loader_Autoloader
       
   205      */
       
   206     public function registerNamespace($namespace)
       
   207     {
       
   208         if (is_string($namespace)) {
       
   209             $namespace = (array) $namespace;
       
   210         } elseif (!is_array($namespace)) {
       
   211             throw new Zend_Loader_Exception('Invalid namespace provided');
       
   212         }
       
   213 
       
   214         foreach ($namespace as $ns) {
       
   215             if (!isset($this->_namespaces[$ns])) {
       
   216                 $this->_namespaces[$ns] = true;
       
   217             }
       
   218         }
       
   219         return $this;
       
   220     }
       
   221 
       
   222     /**
       
   223      * Unload a registered autoload namespace
       
   224      *
       
   225      * @param  string|array $namespace
       
   226      * @return Zend_Loader_Autoloader
       
   227      */
       
   228     public function unregisterNamespace($namespace)
       
   229     {
       
   230         if (is_string($namespace)) {
       
   231             $namespace = (array) $namespace;
       
   232         } elseif (!is_array($namespace)) {
       
   233             throw new Zend_Loader_Exception('Invalid namespace provided');
       
   234         }
       
   235 
       
   236         foreach ($namespace as $ns) {
       
   237             if (isset($this->_namespaces[$ns])) {
       
   238                 unset($this->_namespaces[$ns]);
       
   239             }
       
   240         }
       
   241         return $this;
       
   242     }
       
   243 
       
   244     /**
       
   245      * Get a list of registered autoload namespaces
       
   246      *
       
   247      * @return array
       
   248      */
       
   249     public function getRegisteredNamespaces()
       
   250     {
       
   251         return array_keys($this->_namespaces);
       
   252     }
       
   253 
       
   254     public function setZfPath($spec, $version = 'latest')
       
   255     {
       
   256         $path = $spec;
       
   257         if (is_array($spec)) {
       
   258             if (!isset($spec['path'])) {
       
   259                 throw new Zend_Loader_Exception('No path specified for ZF');
       
   260             }
       
   261             $path = $spec['path'];
       
   262             if (isset($spec['version'])) {
       
   263                 $version = $spec['version'];
       
   264             }
       
   265         }
       
   266 
       
   267         $this->_zfPath = $this->_getVersionPath($path, $version);
       
   268         set_include_path(implode(PATH_SEPARATOR, array(
       
   269             $this->_zfPath,
       
   270             get_include_path(),
       
   271         )));
       
   272         return $this;
       
   273     }
       
   274 
       
   275     public function getZfPath()
       
   276     {
       
   277         return $this->_zfPath;
       
   278     }
       
   279 
       
   280     /**
       
   281      * Get or set the value of the "suppress not found warnings" flag
       
   282      *
       
   283      * @param  null|bool $flag
       
   284      * @return bool|Zend_Loader_Autoloader Returns boolean if no argument is passed, object instance otherwise
       
   285      */
       
   286     public function suppressNotFoundWarnings($flag = null)
       
   287     {
       
   288         if (null === $flag) {
       
   289             return $this->_suppressNotFoundWarnings;
       
   290         }
       
   291         $this->_suppressNotFoundWarnings = (bool) $flag;
       
   292         return $this;
       
   293     }
       
   294 
       
   295     /**
       
   296      * Indicate whether or not this autoloader should be a fallback autoloader
       
   297      *
       
   298      * @param  bool $flag
       
   299      * @return Zend_Loader_Autoloader
       
   300      */
       
   301     public function setFallbackAutoloader($flag)
       
   302     {
       
   303         $this->_fallbackAutoloader = (bool) $flag;
       
   304         return $this;
       
   305     }
       
   306 
       
   307     /**
       
   308      * Is this instance acting as a fallback autoloader?
       
   309      *
       
   310      * @return bool
       
   311      */
       
   312     public function isFallbackAutoloader()
       
   313     {
       
   314         return $this->_fallbackAutoloader;
       
   315     }
       
   316 
       
   317     /**
       
   318      * Get autoloaders to use when matching class
       
   319      *
       
   320      * Determines if the class matches a registered namespace, and, if so,
       
   321      * returns only the autoloaders for that namespace. Otherwise, it returns
       
   322      * all non-namespaced autoloaders.
       
   323      *
       
   324      * @param  string $class
       
   325      * @return array Array of autoloaders to use
       
   326      */
       
   327     public function getClassAutoloaders($class)
       
   328     {
       
   329         $namespace   = false;
       
   330         $autoloaders = array();
       
   331 
       
   332         // Add concrete namespaced autoloaders
       
   333         foreach (array_keys($this->_namespaceAutoloaders) as $ns) {
       
   334             if ('' == $ns) {
       
   335                 continue;
       
   336             }
       
   337             if (0 === strpos($class, $ns)) {
       
   338                 $namespace   = $ns;
       
   339                 $autoloaders = $autoloaders + $this->getNamespaceAutoloaders($ns);
       
   340                 break;
       
   341             }
       
   342         }
       
   343 
       
   344         // Add internal namespaced autoloader
       
   345         foreach ($this->getRegisteredNamespaces() as $ns) {
       
   346             if (0 === strpos($class, $ns)) {
       
   347                 $namespace     = $ns;
       
   348                 $autoloaders[] = $this->_internalAutoloader;
       
   349                 break;
       
   350             }
       
   351         }
       
   352 
       
   353         // Add non-namespaced autoloaders
       
   354         $autoloaders = $autoloaders + $this->getNamespaceAutoloaders('');
       
   355 
       
   356         // Add fallback autoloader
       
   357         if (!$namespace && $this->isFallbackAutoloader()) {
       
   358             $autoloaders[] = $this->_internalAutoloader;
       
   359         }
       
   360 
       
   361         return $autoloaders;
       
   362     }
       
   363 
       
   364     /**
       
   365      * Add an autoloader to the beginning of the stack
       
   366      *
       
   367      * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
       
   368      * @param  string|array $namespace Specific namespace(s) under which to register callback
       
   369      * @return Zend_Loader_Autoloader
       
   370      */
       
   371     public function unshiftAutoloader($callback, $namespace = '')
       
   372     {
       
   373         $autoloaders = $this->getAutoloaders();
       
   374         array_unshift($autoloaders, $callback);
       
   375         $this->setAutoloaders($autoloaders);
       
   376 
       
   377         $namespace = (array) $namespace;
       
   378         foreach ($namespace as $ns) {
       
   379             $autoloaders = $this->getNamespaceAutoloaders($ns);
       
   380             array_unshift($autoloaders, $callback);
       
   381             $this->_setNamespaceAutoloaders($autoloaders, $ns);
       
   382         }
       
   383 
       
   384         return $this;
       
   385     }
       
   386 
       
   387     /**
       
   388      * Append an autoloader to the autoloader stack
       
   389      *
       
   390      * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
       
   391      * @param  string|array $namespace Specific namespace(s) under which to register callback
       
   392      * @return Zend_Loader_Autoloader
       
   393      */
       
   394     public function pushAutoloader($callback, $namespace = '')
       
   395     {
       
   396         $autoloaders = $this->getAutoloaders();
       
   397         array_push($autoloaders, $callback);
       
   398         $this->setAutoloaders($autoloaders);
       
   399 
       
   400         $namespace = (array) $namespace;
       
   401         foreach ($namespace as $ns) {
       
   402             $autoloaders = $this->getNamespaceAutoloaders($ns);
       
   403             array_push($autoloaders, $callback);
       
   404             $this->_setNamespaceAutoloaders($autoloaders, $ns);
       
   405         }
       
   406 
       
   407         return $this;
       
   408     }
       
   409 
       
   410     /**
       
   411      * Remove an autoloader from the autoloader stack
       
   412      *
       
   413      * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
       
   414      * @param  null|string|array $namespace Specific namespace(s) from which to remove autoloader
       
   415      * @return Zend_Loader_Autoloader
       
   416      */
       
   417     public function removeAutoloader($callback, $namespace = null)
       
   418     {
       
   419         if (null === $namespace) {
       
   420             $autoloaders = $this->getAutoloaders();
       
   421             if (false !== ($index = array_search($callback, $autoloaders, true))) {
       
   422                 unset($autoloaders[$index]);
       
   423                 $this->setAutoloaders($autoloaders);
       
   424             }
       
   425 
       
   426             foreach ($this->_namespaceAutoloaders as $ns => $autoloaders) {
       
   427                 if (false !== ($index = array_search($callback, $autoloaders, true))) {
       
   428                     unset($autoloaders[$index]);
       
   429                     $this->_setNamespaceAutoloaders($autoloaders, $ns);
       
   430                 }
       
   431             }
       
   432         } else {
       
   433             $namespace = (array) $namespace;
       
   434             foreach ($namespace as $ns) {
       
   435                 $autoloaders = $this->getNamespaceAutoloaders($ns);
       
   436                 if (false !== ($index = array_search($callback, $autoloaders, true))) {
       
   437                     unset($autoloaders[$index]);
       
   438                     $this->_setNamespaceAutoloaders($autoloaders, $ns);
       
   439                 }
       
   440             }
       
   441         }
       
   442 
       
   443         return $this;
       
   444     }
       
   445 
       
   446     /**
       
   447      * Constructor
       
   448      *
       
   449      * Registers instance with spl_autoload stack
       
   450      *
       
   451      * @return void
       
   452      */
       
   453     protected function __construct()
       
   454     {
       
   455         spl_autoload_register(array(__CLASS__, 'autoload'));
       
   456         $this->_internalAutoloader = array($this, '_autoload');
       
   457     }
       
   458 
       
   459     /**
       
   460      * Internal autoloader implementation
       
   461      *
       
   462      * @param  string $class
       
   463      * @return bool
       
   464      */
       
   465     protected function _autoload($class)
       
   466     {
       
   467         $callback = $this->getDefaultAutoloader();
       
   468         try {
       
   469             if ($this->suppressNotFoundWarnings()) {
       
   470                 @call_user_func($callback, $class);
       
   471             } else {
       
   472                 call_user_func($callback, $class);
       
   473             }
       
   474             return $class;
       
   475         } catch (Zend_Exception $e) {
       
   476             return false;
       
   477         }
       
   478     }
       
   479 
       
   480     /**
       
   481      * Set autoloaders for a specific namespace
       
   482      *
       
   483      * @param  array $autoloaders
       
   484      * @param  string $namespace
       
   485      * @return Zend_Loader_Autoloader
       
   486      */
       
   487     protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '')
       
   488     {
       
   489         $namespace = (string) $namespace;
       
   490         $this->_namespaceAutoloaders[$namespace] = $autoloaders;
       
   491         return $this;
       
   492     }
       
   493 
       
   494     /**
       
   495      * Retrieve the filesystem path for the requested ZF version
       
   496      *
       
   497      * @param  string $path
       
   498      * @param  string $version
       
   499      * @return void
       
   500      */
       
   501     protected function _getVersionPath($path, $version)
       
   502     {
       
   503         $type = $this->_getVersionType($version);
       
   504 
       
   505         if ($type == 'latest') {
       
   506             $version = 'latest';
       
   507         }
       
   508 
       
   509         $availableVersions = $this->_getAvailableVersions($path, $version);
       
   510         if (empty($availableVersions)) {
       
   511             throw new Zend_Loader_Exception('No valid ZF installations discovered');
       
   512         }
       
   513 
       
   514         $matchedVersion = array_pop($availableVersions);
       
   515         return $matchedVersion;
       
   516     }
       
   517 
       
   518     /**
       
   519      * Retrieve the ZF version type
       
   520      *
       
   521      * @param  string $version
       
   522      * @return string "latest", "major", "minor", or "specific"
       
   523      * @throws Zend_Loader_Exception if version string contains too many dots
       
   524      */
       
   525     protected function _getVersionType($version)
       
   526     {
       
   527         if (strtolower($version) == 'latest') {
       
   528             return 'latest';
       
   529         }
       
   530 
       
   531         $parts = explode('.', $version);
       
   532         $count = count($parts);
       
   533         if (1 == $count) {
       
   534             return 'major';
       
   535         }
       
   536         if (2 == $count) {
       
   537             return 'minor';
       
   538         }
       
   539         if (3 < $count) {
       
   540             throw new Zend_Loader_Exception('Invalid version string provided');
       
   541         }
       
   542         return 'specific';
       
   543     }
       
   544 
       
   545     /**
       
   546      * Get available versions for the version type requested
       
   547      *
       
   548      * @param  string $path
       
   549      * @param  string $version
       
   550      * @return array
       
   551      */
       
   552     protected function _getAvailableVersions($path, $version)
       
   553     {
       
   554         if (!is_dir($path)) {
       
   555             throw new Zend_Loader_Exception('Invalid ZF path provided');
       
   556         }
       
   557 
       
   558         $path       = rtrim($path, '/');
       
   559         $path       = rtrim($path, '\\');
       
   560         $versionLen = strlen($version);
       
   561         $versions   = array();
       
   562         $dirs       = glob("$path/*", GLOB_ONLYDIR);
       
   563         foreach ((array) $dirs as $dir) {
       
   564             $dirName = substr($dir, strlen($path) + 1);
       
   565             if (!preg_match('/^(?:ZendFramework-)?(\d+\.\d+\.\d+((a|b|pl|pr|p|rc)\d+)?)(?:-minimal)?$/i', $dirName, $matches)) {
       
   566                 continue;
       
   567             }
       
   568 
       
   569             $matchedVersion = $matches[1];
       
   570 
       
   571             if (('latest' == $version)
       
   572                 || ((strlen($matchedVersion) >= $versionLen)
       
   573                     && (0 === strpos($matchedVersion, $version)))
       
   574             ) {
       
   575                 $versions[$matchedVersion] = $dir . '/library';
       
   576             }
       
   577         }
       
   578 
       
   579         uksort($versions, 'version_compare');
       
   580         return $versions;
       
   581     }
       
   582 }