File indexing completed on 2025-02-02 05:49:15
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 }