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.
16 * @package Zend_Loader
17 * @subpackage Autoloader
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
20 * @license http://framework.zend.com/license/new-bsd New BSD License
24 require_once 'Zend/Loader.php';
27 * Autoloader stack and namespace autoloader
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
35 class Zend_Loader_Autoloader
38 * @var Zend_Loader_Autoloader Singleton instance
40 protected static $_instance;
43 * @var array Concrete autoloader callback implementations
45 protected $_autoloaders = array();
48 * @var array Default autoloader callback
50 protected $_defaultAutoloader = array('Zend_Loader', 'loadClass');
53 * @var bool Whether or not to act as a fallback autoloader
55 protected $_fallbackAutoloader = false;
58 * @var array Callback for internal autoloader implementation
60 protected $_internalAutoloader;
63 * @var array Supported namespaces 'Zend' and 'ZendX' by default.
65 protected $_namespaces = array(
71 * @var array Namespace-specific autoloaders
73 protected $_namespaceAutoloaders = array();
76 * @var bool Whether or not to suppress file not found warnings
78 protected $_suppressNotFoundWarnings = false;
86 * Retrieve singleton instance
88 * @return Zend_Loader_Autoloader
90 public static function getInstance()
92 if (null === self
::$_instance) {
93 self
::$_instance = new self();
95 return self
::$_instance;
99 * Reset the singleton instance
103 public static function resetInstance()
105 self
::$_instance = null;
111 * @param string $class
114 public static function autoload($class)
116 $self = self
::getInstance();
118 foreach ($self->getClassAutoloaders($class) as $autoloader) {
119 if ($autoloader instanceof Zend_Loader_Autoloader_Interface
) {
120 if ($autoloader->autoload($class)) {
123 } elseif (is_array($autoloader)) {
124 if (call_user_func($autoloader, $class)) {
127 } elseif (is_string($autoloader) ||
is_callable($autoloader)) {
128 if ($autoloader($class)) {
138 * Set the default autoloader implementation
140 * @param string|array $callback PHP callback
143 public function setDefaultAutoloader($callback)
145 if (!is_callable($callback)) {
146 throw new Zend_Loader_Exception('Invalid callback specified for default autoloader');
149 $this->_defaultAutoloader
= $callback;
154 * Retrieve the default autoloader callback
156 * @return string|array PHP Callback
158 public function getDefaultAutoloader()
160 return $this->_defaultAutoloader
;
164 * Set several autoloader callbacks at once
166 * @param array $autoloaders Array of PHP callbacks (or Zend_Loader_Autoloader_Interface implementations) to act as autoloaders
167 * @return Zend_Loader_Autoloader
169 public function setAutoloaders(array $autoloaders)
171 $this->_autoloaders
= $autoloaders;
176 * Get attached autoloader implementations
180 public function getAutoloaders()
182 return $this->_autoloaders
;
186 * Return all autoloaders for a given namespace
188 * @param string $namespace
191 public function getNamespaceAutoloaders($namespace)
193 $namespace = (string) $namespace;
194 if (!array_key_exists($namespace, $this->_namespaceAutoloaders
)) {
197 return $this->_namespaceAutoloaders
[$namespace];
201 * Register a namespace to autoload
203 * @param string|array $namespace
204 * @return Zend_Loader_Autoloader
206 public function registerNamespace($namespace)
208 if (is_string($namespace)) {
209 $namespace = (array) $namespace;
210 } elseif (!is_array($namespace)) {
211 throw new Zend_Loader_Exception('Invalid namespace provided');
214 foreach ($namespace as $ns) {
215 if (!isset($this->_namespaces
[$ns])) {
216 $this->_namespaces
[$ns] = true;
223 * Unload a registered autoload namespace
225 * @param string|array $namespace
226 * @return Zend_Loader_Autoloader
228 public function unregisterNamespace($namespace)
230 if (is_string($namespace)) {
231 $namespace = (array) $namespace;
232 } elseif (!is_array($namespace)) {
233 throw new Zend_Loader_Exception('Invalid namespace provided');
236 foreach ($namespace as $ns) {
237 if (isset($this->_namespaces
[$ns])) {
238 unset($this->_namespaces
[$ns]);
245 * Get a list of registered autoload namespaces
249 public function getRegisteredNamespaces()
251 return array_keys($this->_namespaces
);
254 public function setZfPath($spec, $version = 'latest')
257 if (is_array($spec)) {
258 if (!isset($spec['path'])) {
259 throw new Zend_Loader_Exception('No path specified for ZF');
261 $path = $spec['path'];
262 if (isset($spec['version'])) {
263 $version = $spec['version'];
267 $this->_zfPath
= $this->_getVersionPath($path, $version);
268 set_include_path(implode(PATH_SEPARATOR
, array(
275 public function getZfPath()
277 return $this->_zfPath
;
281 * Get or set the value of the "suppress not found warnings" flag
283 * @param null|bool $flag
284 * @return bool|Zend_Loader_Autoloader Returns boolean if no argument is passed, object instance otherwise
286 public function suppressNotFoundWarnings($flag = null)
288 if (null === $flag) {
289 return $this->_suppressNotFoundWarnings
;
291 $this->_suppressNotFoundWarnings
= (bool) $flag;
296 * Indicate whether or not this autoloader should be a fallback autoloader
299 * @return Zend_Loader_Autoloader
301 public function setFallbackAutoloader($flag)
303 $this->_fallbackAutoloader
= (bool) $flag;
308 * Is this instance acting as a fallback autoloader?
312 public function isFallbackAutoloader()
314 return $this->_fallbackAutoloader
;
318 * Get autoloaders to use when matching class
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.
324 * @param string $class
325 * @return array Array of autoloaders to use
327 public function getClassAutoloaders($class)
330 $autoloaders = array();
332 // Add concrete namespaced autoloaders
333 foreach (array_keys($this->_namespaceAutoloaders
) as $ns) {
337 if (0 === strpos($class, $ns)) {
339 $autoloaders = $autoloaders +
$this->getNamespaceAutoloaders($ns);
344 // Add internal namespaced autoloader
345 foreach ($this->getRegisteredNamespaces() as $ns) {
346 if (0 === strpos($class, $ns)) {
348 $autoloaders[] = $this->_internalAutoloader
;
353 // Add non-namespaced autoloaders
354 $autoloaders = $autoloaders +
$this->getNamespaceAutoloaders('');
356 // Add fallback autoloader
357 if (!$namespace && $this->isFallbackAutoloader()) {
358 $autoloaders[] = $this->_internalAutoloader
;
365 * Add an autoloader to the beginning of the stack
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
371 public function unshiftAutoloader($callback, $namespace = '')
373 $autoloaders = $this->getAutoloaders();
374 array_unshift($autoloaders, $callback);
375 $this->setAutoloaders($autoloaders);
377 $namespace = (array) $namespace;
378 foreach ($namespace as $ns) {
379 $autoloaders = $this->getNamespaceAutoloaders($ns);
380 array_unshift($autoloaders, $callback);
381 $this->_setNamespaceAutoloaders($autoloaders, $ns);
388 * Append an autoloader to the autoloader stack
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
394 public function pushAutoloader($callback, $namespace = '')
396 $autoloaders = $this->getAutoloaders();
397 array_push($autoloaders, $callback);
398 $this->setAutoloaders($autoloaders);
400 $namespace = (array) $namespace;
401 foreach ($namespace as $ns) {
402 $autoloaders = $this->getNamespaceAutoloaders($ns);
403 array_push($autoloaders, $callback);
404 $this->_setNamespaceAutoloaders($autoloaders, $ns);
411 * Remove an autoloader from the autoloader stack
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
417 public function removeAutoloader($callback, $namespace = null)
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);
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);
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);
449 * Registers instance with spl_autoload stack
453 protected function __construct()
455 spl_autoload_register(array(__CLASS__
, 'autoload'));
456 $this->_internalAutoloader
= array($this, '_autoload');
460 * Internal autoloader implementation
462 * @param string $class
465 protected function _autoload($class)
467 $callback = $this->getDefaultAutoloader();
469 if ($this->suppressNotFoundWarnings()) {
470 @call_user_func
($callback, $class);
472 call_user_func($callback, $class);
475 } catch (Zend_Exception
$e) {
481 * Set autoloaders for a specific namespace
483 * @param array $autoloaders
484 * @param string $namespace
485 * @return Zend_Loader_Autoloader
487 protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '')
489 $namespace = (string) $namespace;
490 $this->_namespaceAutoloaders
[$namespace] = $autoloaders;
495 * Retrieve the filesystem path for the requested ZF version
497 * @param string $path
498 * @param string $version
501 protected function _getVersionPath($path, $version)
503 $type = $this->_getVersionType($version);
505 if ($type == 'latest') {
509 $availableVersions = $this->_getAvailableVersions($path, $version);
510 if (empty($availableVersions)) {
511 throw new Zend_Loader_Exception('No valid ZF installations discovered');
514 $matchedVersion = array_pop($availableVersions);
515 return $matchedVersion;
519 * Retrieve the ZF version type
521 * @param string $version
522 * @return string "latest", "major", "minor", or "specific"
523 * @throws Zend_Loader_Exception if version string contains too many dots
525 protected function _getVersionType($version)
527 if (strtolower($version) == 'latest') {
531 $parts = explode('.', $version);
532 $count = count($parts);
540 throw new Zend_Loader_Exception('Invalid version string provided');
546 * Get available versions for the version type requested
548 * @param string $path
549 * @param string $version
552 protected function _getAvailableVersions($path, $version)
554 if (!is_dir($path)) {
555 throw new Zend_Loader_Exception('Invalid ZF path provided');
558 $path = rtrim($path, '/');
559 $path = rtrim($path, '\\');
560 $versionLen = strlen($version);
562 $dirs = glob("$path/*", GLOB_ONLYDIR
);
563 foreach ($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)) {
569 $matchedVersion = $matches[1];
571 if (('latest' == $version)
572 ||
((strlen($matchedVersion) >= $versionLen)
573 && (0 === strpos($matchedVersion, $version)))
575 $versions[$matchedVersion] = $dir . '/library';
579 uksort($versions, 'version_compare');