File indexing completed on 2024-05-12 06:02:43

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Loader
0017  * @subpackage Autoloader
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @version    $Id$
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  */
0022 
0023 /** Zend_Loader */
0024 require_once 'Zend/Loader.php';
0025 
0026 /**
0027  * Autoloader stack and namespace autoloader
0028  *
0029  * @uses       Zend_Loader_Autoloader
0030  * @package    Zend_Loader
0031  * @subpackage Autoloader
0032  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0033  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0034  */
0035 class Zend_Loader_Autoloader
0036 {
0037     /**
0038      * @var Zend_Loader_Autoloader Singleton instance
0039      */
0040     protected static $_instance;
0041 
0042     /**
0043      * @var array Concrete autoloader callback implementations
0044      */
0045     protected $_autoloaders = array();
0046 
0047     /**
0048      * @var array Default autoloader callback
0049      */
0050     protected $_defaultAutoloader = array('Zend_Loader', 'loadClass');
0051 
0052     /**
0053      * @var bool Whether or not to act as a fallback autoloader
0054      */
0055     protected $_fallbackAutoloader = false;
0056 
0057     /**
0058      * @var array Callback for internal autoloader implementation
0059      */
0060     protected $_internalAutoloader;
0061 
0062     /**
0063      * @var array Supported namespaces 'Zend' and 'ZendX' by default.
0064      */
0065     protected $_namespaces = array(
0066         'Zend_'  => true,
0067         'ZendX_' => true,
0068     );
0069 
0070     /**
0071      * @var array Namespace-specific autoloaders
0072      */
0073     protected $_namespaceAutoloaders = array();
0074 
0075     /**
0076      * @var bool Whether or not to suppress file not found warnings
0077      */
0078     protected $_suppressNotFoundWarnings = false;
0079 
0080     /**
0081      * @var null|string
0082      */
0083     protected $_zfPath;
0084 
0085     /**
0086      * Retrieve singleton instance
0087      *
0088      * @return Zend_Loader_Autoloader
0089      */
0090     public static function getInstance()
0091     {
0092         if (null === self::$_instance) {
0093             self::$_instance = new self();
0094         }
0095         return self::$_instance;
0096     }
0097 
0098     /**
0099      * Reset the singleton instance
0100      *
0101      * @return void
0102      */
0103     public static function resetInstance()
0104     {
0105         self::$_instance = null;
0106     }
0107 
0108     /**
0109      * Autoload a class
0110      *
0111      * @param  string $class
0112      * @return bool
0113      */
0114     public static function autoload($class)
0115     {
0116         $self = self::getInstance();
0117 
0118         foreach ($self->getClassAutoloaders($class) as $autoloader) {
0119             if ($autoloader instanceof Zend_Loader_Autoloader_Interface) {
0120                 if ($autoloader->autoload($class)) {
0121                     return true;
0122                 }
0123             } elseif (is_array($autoloader)) {
0124                 if (call_user_func($autoloader, $class)) {
0125                     return true;
0126                 }
0127             } elseif (is_string($autoloader) || is_callable($autoloader)) {
0128                 if ($autoloader($class)) {
0129                     return true;
0130                 }
0131             }
0132         }
0133 
0134         return false;
0135     }
0136 
0137     /**
0138      * Set the default autoloader implementation
0139      *
0140      * @param  string|array $callback PHP callback
0141      * @return void
0142      */
0143     public function setDefaultAutoloader($callback)
0144     {
0145         if (!is_callable($callback)) {
0146             throw new Zend_Loader_Exception('Invalid callback specified for default autoloader');
0147         }
0148 
0149         $this->_defaultAutoloader = $callback;
0150         return $this;
0151     }
0152 
0153     /**
0154      * Retrieve the default autoloader callback
0155      *
0156      * @return string|array PHP Callback
0157      */
0158     public function getDefaultAutoloader()
0159     {
0160         return $this->_defaultAutoloader;
0161     }
0162 
0163     /**
0164      * Set several autoloader callbacks at once
0165      *
0166      * @param  array $autoloaders Array of PHP callbacks (or Zend_Loader_Autoloader_Interface implementations) to act as autoloaders
0167      * @return Zend_Loader_Autoloader
0168      */
0169     public function setAutoloaders(array $autoloaders)
0170     {
0171         $this->_autoloaders = $autoloaders;
0172         return $this;
0173     }
0174 
0175     /**
0176      * Get attached autoloader implementations
0177      *
0178      * @return array
0179      */
0180     public function getAutoloaders()
0181     {
0182         return $this->_autoloaders;
0183     }
0184 
0185     /**
0186      * Return all autoloaders for a given namespace
0187      *
0188      * @param  string $namespace
0189      * @return array
0190      */
0191     public function getNamespaceAutoloaders($namespace)
0192     {
0193         $namespace = (string) $namespace;
0194         if (!array_key_exists($namespace, $this->_namespaceAutoloaders)) {
0195             return array();
0196         }
0197         return $this->_namespaceAutoloaders[$namespace];
0198     }
0199 
0200     /**
0201      * Register a namespace to autoload
0202      *
0203      * @param  string|array $namespace
0204      * @return Zend_Loader_Autoloader
0205      */
0206     public function registerNamespace($namespace)
0207     {
0208         if (is_string($namespace)) {
0209             $namespace = (array) $namespace;
0210         } elseif (!is_array($namespace)) {
0211             throw new Zend_Loader_Exception('Invalid namespace provided');
0212         }
0213 
0214         foreach ($namespace as $ns) {
0215             if (!isset($this->_namespaces[$ns])) {
0216                 $this->_namespaces[$ns] = true;
0217             }
0218         }
0219         return $this;
0220     }
0221 
0222     /**
0223      * Unload a registered autoload namespace
0224      *
0225      * @param  string|array $namespace
0226      * @return Zend_Loader_Autoloader
0227      */
0228     public function unregisterNamespace($namespace)
0229     {
0230         if (is_string($namespace)) {
0231             $namespace = (array) $namespace;
0232         } elseif (!is_array($namespace)) {
0233             throw new Zend_Loader_Exception('Invalid namespace provided');
0234         }
0235 
0236         foreach ($namespace as $ns) {
0237             if (isset($this->_namespaces[$ns])) {
0238                 unset($this->_namespaces[$ns]);
0239             }
0240         }
0241         return $this;
0242     }
0243 
0244     /**
0245      * Get a list of registered autoload namespaces
0246      *
0247      * @return array
0248      */
0249     public function getRegisteredNamespaces()
0250     {
0251         return array_keys($this->_namespaces);
0252     }
0253 
0254     public function setZfPath($spec, $version = 'latest')
0255     {
0256         $path = $spec;
0257         if (is_array($spec)) {
0258             if (!isset($spec['path'])) {
0259                 throw new Zend_Loader_Exception('No path specified for ZF');
0260             }
0261             $path = $spec['path'];
0262             if (isset($spec['version'])) {
0263                 $version = $spec['version'];
0264             }
0265         }
0266 
0267         $this->_zfPath = $this->_getVersionPath($path, $version);
0268         set_include_path(implode(PATH_SEPARATOR, array(
0269             $this->_zfPath,
0270             get_include_path(),
0271         )));
0272         return $this;
0273     }
0274 
0275     public function getZfPath()
0276     {
0277         return $this->_zfPath;
0278     }
0279 
0280     /**
0281      * Get or set the value of the "suppress not found warnings" flag
0282      *
0283      * @param  null|bool $flag
0284      * @return bool|Zend_Loader_Autoloader Returns boolean if no argument is passed, object instance otherwise
0285      */
0286     public function suppressNotFoundWarnings($flag = null)
0287     {
0288         if (null === $flag) {
0289             return $this->_suppressNotFoundWarnings;
0290         }
0291         $this->_suppressNotFoundWarnings = (bool) $flag;
0292         return $this;
0293     }
0294 
0295     /**
0296      * Indicate whether or not this autoloader should be a fallback autoloader
0297      *
0298      * @param  bool $flag
0299      * @return Zend_Loader_Autoloader
0300      */
0301     public function setFallbackAutoloader($flag)
0302     {
0303         $this->_fallbackAutoloader = (bool) $flag;
0304         return $this;
0305     }
0306 
0307     /**
0308      * Is this instance acting as a fallback autoloader?
0309      *
0310      * @return bool
0311      */
0312     public function isFallbackAutoloader()
0313     {
0314         return $this->_fallbackAutoloader;
0315     }
0316 
0317     /**
0318      * Get autoloaders to use when matching class
0319      *
0320      * Determines if the class matches a registered namespace, and, if so,
0321      * returns only the autoloaders for that namespace. Otherwise, it returns
0322      * all non-namespaced autoloaders.
0323      *
0324      * @param  string $class
0325      * @return array Array of autoloaders to use
0326      */
0327     public function getClassAutoloaders($class)
0328     {
0329         $namespace   = false;
0330         $autoloaders = array();
0331 
0332         // Add concrete namespaced autoloaders
0333         foreach (array_keys($this->_namespaceAutoloaders) as $ns) {
0334             if ('' == $ns) {
0335                 continue;
0336             }
0337             if (0 === strpos($class, $ns)) {
0338                 if ((false === $namespace) || (strlen($ns) > strlen($namespace))) {
0339                     $namespace = $ns;
0340                     $autoloaders = $this->getNamespaceAutoloaders($ns);
0341                 }
0342             }
0343         }
0344 
0345         // Add internal namespaced autoloader
0346         foreach ($this->getRegisteredNamespaces() as $ns) {
0347             if (0 === strpos($class, $ns)) {
0348                 $namespace     = $ns;
0349                 $autoloaders[] = $this->_internalAutoloader;
0350                 break;
0351             }
0352         }
0353 
0354         // Add non-namespaced autoloaders
0355         $autoloadersNonNamespace = $this->getNamespaceAutoloaders('');
0356         if (count($autoloadersNonNamespace)) {
0357             foreach ($autoloadersNonNamespace as $ns) {
0358                 $autoloaders[] = $ns;
0359             }
0360             unset($autoloadersNonNamespace);
0361         }
0362 
0363         // Add fallback autoloader
0364         if (!$namespace && $this->isFallbackAutoloader()) {
0365             $autoloaders[] = $this->_internalAutoloader;
0366         }
0367 
0368         return $autoloaders;
0369     }
0370 
0371     /**
0372      * Add an autoloader to the beginning of the stack
0373      *
0374      * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
0375      * @param  string|array $namespace Specific namespace(s) under which to register callback
0376      * @return Zend_Loader_Autoloader
0377      */
0378     public function unshiftAutoloader($callback, $namespace = '')
0379     {
0380         $autoloaders = $this->getAutoloaders();
0381         array_unshift($autoloaders, $callback);
0382         $this->setAutoloaders($autoloaders);
0383 
0384         $namespace = (array) $namespace;
0385         foreach ($namespace as $ns) {
0386             $autoloaders = $this->getNamespaceAutoloaders($ns);
0387             array_unshift($autoloaders, $callback);
0388             $this->_setNamespaceAutoloaders($autoloaders, $ns);
0389         }
0390 
0391         return $this;
0392     }
0393 
0394     /**
0395      * Append an autoloader to the autoloader stack
0396      *
0397      * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
0398      * @param  string|array $namespace Specific namespace(s) under which to register callback
0399      * @return Zend_Loader_Autoloader
0400      */
0401     public function pushAutoloader($callback, $namespace = '')
0402     {
0403         $autoloaders = $this->getAutoloaders();
0404         array_push($autoloaders, $callback);
0405         $this->setAutoloaders($autoloaders);
0406 
0407         $namespace = (array) $namespace;
0408         foreach ($namespace as $ns) {
0409             $autoloaders = $this->getNamespaceAutoloaders($ns);
0410             array_push($autoloaders, $callback);
0411             $this->_setNamespaceAutoloaders($autoloaders, $ns);
0412         }
0413 
0414         return $this;
0415     }
0416 
0417     /**
0418      * Remove an autoloader from the autoloader stack
0419      *
0420      * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
0421      * @param  null|string|array $namespace Specific namespace(s) from which to remove autoloader
0422      * @return Zend_Loader_Autoloader
0423      */
0424     public function removeAutoloader($callback, $namespace = null)
0425     {
0426         if (null === $namespace) {
0427             $autoloaders = $this->getAutoloaders();
0428             if (false !== ($index = array_search($callback, $autoloaders, true))) {
0429                 unset($autoloaders[$index]);
0430                 $this->setAutoloaders($autoloaders);
0431             }
0432 
0433             foreach ($this->_namespaceAutoloaders as $ns => $autoloaders) {
0434                 if (false !== ($index = array_search($callback, $autoloaders, true))) {
0435                     unset($autoloaders[$index]);
0436                     $this->_setNamespaceAutoloaders($autoloaders, $ns);
0437                 }
0438             }
0439         } else {
0440             $namespace = (array) $namespace;
0441             foreach ($namespace as $ns) {
0442                 $autoloaders = $this->getNamespaceAutoloaders($ns);
0443                 if (false !== ($index = array_search($callback, $autoloaders, true))) {
0444                     unset($autoloaders[$index]);
0445                     $this->_setNamespaceAutoloaders($autoloaders, $ns);
0446                 }
0447             }
0448         }
0449 
0450         return $this;
0451     }
0452 
0453     /**
0454      * Constructor
0455      *
0456      * Registers instance with spl_autoload stack
0457      *
0458      * @return void
0459      */
0460     protected function __construct()
0461     {
0462         spl_autoload_register(array(__CLASS__, 'autoload'));
0463         $this->_internalAutoloader = array($this, '_autoload');
0464     }
0465 
0466     /**
0467      * Internal autoloader implementation
0468      *
0469      * @param  string $class
0470      * @return bool
0471      */
0472     protected function _autoload($class)
0473     {
0474         $callback = $this->getDefaultAutoloader();
0475         try {
0476             if ($this->suppressNotFoundWarnings()) {
0477                 @call_user_func($callback, $class);
0478             } else {
0479                 call_user_func($callback, $class);
0480             }
0481             return $class;
0482         } catch (Zend_Exception $e) {
0483             return false;
0484         }
0485     }
0486 
0487     /**
0488      * Set autoloaders for a specific namespace
0489      *
0490      * @param  array $autoloaders
0491      * @param  string $namespace
0492      * @return Zend_Loader_Autoloader
0493      */
0494     protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '')
0495     {
0496         $namespace = (string) $namespace;
0497         $this->_namespaceAutoloaders[$namespace] = $autoloaders;
0498         return $this;
0499     }
0500 
0501     /**
0502      * Retrieve the filesystem path for the requested ZF version
0503      *
0504      * @param  string $path
0505      * @param  string $version
0506      * @return void
0507      */
0508     protected function _getVersionPath($path, $version)
0509     {
0510         $type = $this->_getVersionType($version);
0511 
0512         if ($type == 'latest') {
0513             $version = 'latest';
0514         }
0515 
0516         $availableVersions = $this->_getAvailableVersions($path, $version);
0517         if (empty($availableVersions)) {
0518             throw new Zend_Loader_Exception('No valid ZF installations discovered');
0519         }
0520 
0521         $matchedVersion = array_pop($availableVersions);
0522         return $matchedVersion;
0523     }
0524 
0525     /**
0526      * Retrieve the ZF version type
0527      *
0528      * @param  string $version
0529      * @return string "latest", "major", "minor", or "specific"
0530      * @throws Zend_Loader_Exception if version string contains too many dots
0531      */
0532     protected function _getVersionType($version)
0533     {
0534         if (strtolower($version) == 'latest') {
0535             return 'latest';
0536         }
0537 
0538         $parts = explode('.', $version);
0539         $count = count($parts);
0540         if (1 == $count) {
0541             return 'major';
0542         }
0543         if (2 == $count) {
0544             return 'minor';
0545         }
0546         if (3 < $count) {
0547             throw new Zend_Loader_Exception('Invalid version string provided');
0548         }
0549         return 'specific';
0550     }
0551 
0552     /**
0553      * Get available versions for the version type requested
0554      *
0555      * @param  string $path
0556      * @param  string $version
0557      * @return array
0558      */
0559     protected function _getAvailableVersions($path, $version)
0560     {
0561         if (!is_dir($path)) {
0562             throw new Zend_Loader_Exception('Invalid ZF path provided');
0563         }
0564 
0565         $path       = rtrim($path, '/');
0566         $path       = rtrim($path, '\\');
0567         $versionLen = strlen($version);
0568         $versions   = array();
0569         $dirs       = glob("$path/*", GLOB_ONLYDIR);
0570         foreach ((array) $dirs as $dir) {
0571             $dirName = substr($dir, strlen($path) + 1);
0572             if (!preg_match('/^(?:ZendFramework-)?(\d+\.\d+\.\d+((a|b|pl|pr|p|rc)\d+)?)(?:-minimal)?$/i', $dirName, $matches)) {
0573                 continue;
0574             }
0575 
0576             $matchedVersion = $matches[1];
0577 
0578             if (('latest' == $version)
0579                 || ((strlen($matchedVersion) >= $versionLen)
0580                     && (0 === strpos($matchedVersion, $version)))
0581             ) {
0582                 $versions[$matchedVersion] = $dir . '/library';
0583             }
0584         }
0585 
0586         uksort($versions, 'version_compare');
0587         return $versions;
0588     }
0589 }