<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to [email protected] so we can send you a copy immediately. * * @category Zend * @package Zend_Loader * @subpackage Autoloader * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @version $Id$ * @license http://framework.zend.com/license/new-bsd New BSD License */ /** Zend_Loader */ require_once 'Zend/Loader.php'; /** * Autoloader stack and namespace autoloader * * @uses Zend_Loader_Autoloader * @package Zend_Loader * @subpackage Autoloader * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Loader_Autoloader { /** * @var Zend_Loader_Autoloader Singleton instance */ protected static $_instance; /** * @var array Concrete autoloader callback implementations */ protected $_autoloaders = array(); /** * @var array Default autoloader callback */ protected $_defaultAutoloader = array('Zend_Loader', 'loadClass'); /** * @var bool Whether or not to act as a fallback autoloader */ protected $_fallbackAutoloader = false; /** * @var array Callback for internal autoloader implementation */ protected $_internalAutoloader; /** * @var array Supported namespaces 'Zend' and 'ZendX' by default. */ protected $_namespaces = array( 'Zend_' => true, 'ZendX_' => true, ); /** * @var array Namespace-specific autoloaders */ protected $_namespaceAutoloaders = array(); /** * @var bool Whether or not to suppress file not found warnings */ protected $_suppressNotFoundWarnings = false; /** * @var null|string */ protected $_zfPath; /** * Retrieve singleton instance * * @return Zend_Loader_Autoloader */ public static function getInstance() { if (null === self::$_instance) { self::$_instance = new self(); } return self::$_instance; } /** * Reset the singleton instance * * @return void */ public static function resetInstance() { self::$_instance = null; } /** * Autoload a class * * @param string $class * @return bool */ public static function autoload($class) { $self = self::getInstance(); foreach ($self->getClassAutoloaders($class) as $autoloader) { if ($autoloader instanceof Zend_Loader_Autoloader_Interface) { if ($autoloader->autoload($class)) { return true; } } elseif (is_array($autoloader)) { if (call_user_func($autoloader, $class)) { return true; } } elseif (is_string($autoloader) || is_callable($autoloader)) { if ($autoloader($class)) { return true; } } } return false; } /** * Set the default autoloader implementation * * @param string|array $callback PHP callback * @return void */ public function setDefaultAutoloader($callback) { if (!is_callable($callback)) { throw new Zend_Loader_Exception('Invalid callback specified for default autoloader'); } $this->_defaultAutoloader = $callback; return $this; } /** * Retrieve the default autoloader callback * * @return string|array PHP Callback */ public function getDefaultAutoloader() { return $this->_defaultAutoloader; } /** * Set several autoloader callbacks at once * * @param array $autoloaders Array of PHP callbacks (or Zend_Loader_Autoloader_Interface implementations) to act as autoloaders * @return Zend_Loader_Autoloader */ public function setAutoloaders(array $autoloaders) { $this->_autoloaders = $autoloaders; return $this; } /** * Get attached autoloader implementations * * @return array */ public function getAutoloaders() { return $this->_autoloaders; } /** * Return all autoloaders for a given namespace * * @param string $namespace * @return array */ public function getNamespaceAutoloaders($namespace) { $namespace = (string) $namespace; if (!array_key_exists($namespace, $this->_namespaceAutoloaders)) { return array(); } return $this->_namespaceAutoloaders[$namespace]; } /** * Register a namespace to autoload * * @param string|array $namespace * @return Zend_Loader_Autoloader */ public function registerNamespace($namespace) { if (is_string($namespace)) { $namespace = (array) $namespace; } elseif (!is_array($namespace)) { throw new Zend_Loader_Exception('Invalid namespace provided'); } foreach ($namespace as $ns) { if (!isset($this->_namespaces[$ns])) { $this->_namespaces[$ns] = true; } } return $this; } /** * Unload a registered autoload namespace * * @param string|array $namespace * @return Zend_Loader_Autoloader */ public function unregisterNamespace($namespace) { if (is_string($namespace)) { $namespace = (array) $namespace; } elseif (!is_array($namespace)) { throw new Zend_Loader_Exception('Invalid namespace provided'); } foreach ($namespace as $ns) { if (isset($this->_namespaces[$ns])) { unset($this->_namespaces[$ns]); } } return $this; } /** * Get a list of registered autoload namespaces * * @return array */ public function getRegisteredNamespaces() { return array_keys($this->_namespaces); } public function setZfPath($spec, $version = 'latest') { $path = $spec; if (is_array($spec)) { if (!isset($spec['path'])) { throw new Zend_Loader_Exception('No path specified for ZF'); } $path = $spec['path']; if (isset($spec['version'])) { $version = $spec['version']; } } $this->_zfPath = $this->_getVersionPath($path, $version); set_include_path(implode(PATH_SEPARATOR, array( $this->_zfPath, get_include_path(), ))); return $this; } public function getZfPath() { return $this->_zfPath; } /** * Get or set the value of the "suppress not found warnings" flag * * @param null|bool $flag * @return bool|Zend_Loader_Autoloader Returns boolean if no argument is passed, object instance otherwise */ public function suppressNotFoundWarnings($flag = null) { if (null === $flag) { return $this->_suppressNotFoundWarnings; } $this->_suppressNotFoundWarnings = (bool) $flag; return $this; } /** * Indicate whether or not this autoloader should be a fallback autoloader * * @param bool $flag * @return Zend_Loader_Autoloader */ public function setFallbackAutoloader($flag) { $this->_fallbackAutoloader = (bool) $flag; return $this; } /** * Is this instance acting as a fallback autoloader? * * @return bool */ public function isFallbackAutoloader() { return $this->_fallbackAutoloader; } /** * Get autoloaders to use when matching class * * Determines if the class matches a registered namespace, and, if so, * returns only the autoloaders for that namespace. Otherwise, it returns * all non-namespaced autoloaders. * * @param string $class * @return array Array of autoloaders to use */ public function getClassAutoloaders($class) { $namespace = false; $autoloaders = array(); // Add concrete namespaced autoloaders foreach (array_keys($this->_namespaceAutoloaders) as $ns) { if ('' == $ns) { continue; } if (0 === strpos($class, $ns)) { if ((false === $namespace) || (strlen($ns) > strlen($namespace))) { $namespace = $ns; $autoloaders = $this->getNamespaceAutoloaders($ns); } } } // Add internal namespaced autoloader foreach ($this->getRegisteredNamespaces() as $ns) { if (0 === strpos($class, $ns)) { $namespace = $ns; $autoloaders[] = $this->_internalAutoloader; break; } } // Add non-namespaced autoloaders $autoloadersNonNamespace = $this->getNamespaceAutoloaders(''); if (count($autoloadersNonNamespace)) { foreach ($autoloadersNonNamespace as $ns) { $autoloaders[] = $ns; } unset($autoloadersNonNamespace); } // Add fallback autoloader if (!$namespace && $this->isFallbackAutoloader()) { $autoloaders[] = $this->_internalAutoloader; } return $autoloaders; } /** * Add an autoloader to the beginning of the stack * * @param object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation * @param string|array $namespace Specific namespace(s) under which to register callback * @return Zend_Loader_Autoloader */ public function unshiftAutoloader($callback, $namespace = '') { $autoloaders = $this->getAutoloaders(); array_unshift($autoloaders, $callback); $this->setAutoloaders($autoloaders); $namespace = (array) $namespace; foreach ($namespace as $ns) { $autoloaders = $this->getNamespaceAutoloaders($ns); array_unshift($autoloaders, $callback); $this->_setNamespaceAutoloaders($autoloaders, $ns); } return $this; } /** * Append an autoloader to the autoloader stack * * @param object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation * @param string|array $namespace Specific namespace(s) under which to register callback * @return Zend_Loader_Autoloader */ public function pushAutoloader($callback, $namespace = '') { $autoloaders = $this->getAutoloaders(); array_push($autoloaders, $callback); $this->setAutoloaders($autoloaders); $namespace = (array) $namespace; foreach ($namespace as $ns) { $autoloaders = $this->getNamespaceAutoloaders($ns); array_push($autoloaders, $callback); $this->_setNamespaceAutoloaders($autoloaders, $ns); } return $this; } /** * Remove an autoloader from the autoloader stack * * @param object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation * @param null|string|array $namespace Specific namespace(s) from which to remove autoloader * @return Zend_Loader_Autoloader */ public function removeAutoloader($callback, $namespace = null) { if (null === $namespace) { $autoloaders = $this->getAutoloaders(); if (false !== ($index = array_search($callback, $autoloaders, true))) { unset($autoloaders[$index]); $this->setAutoloaders($autoloaders); } foreach ($this->_namespaceAutoloaders as $ns => $autoloaders) { if (false !== ($index = array_search($callback, $autoloaders, true))) { unset($autoloaders[$index]); $this->_setNamespaceAutoloaders($autoloaders, $ns); } } } else { $namespace = (array) $namespace; foreach ($namespace as $ns) { $autoloaders = $this->getNamespaceAutoloaders($ns); if (false !== ($index = array_search($callback, $autoloaders, true))) { unset($autoloaders[$index]); $this->_setNamespaceAutoloaders($autoloaders, $ns); } } } return $this; } /** * Constructor * * Registers instance with spl_autoload stack * * @return void */ protected function __construct() { spl_autoload_register(array(__CLASS__, 'autoload')); $this->_internalAutoloader = array($this, '_autoload'); } /** * Internal autoloader implementation * * @param string $class * @return bool */ protected function _autoload($class) { $callback = $this->getDefaultAutoloader(); try { if ($this->suppressNotFoundWarnings()) { @call_user_func($callback, $class); } else { call_user_func($callback, $class); } return $class; } catch (Zend_Exception $e) { return false; } } /** * Set autoloaders for a specific namespace * * @param array $autoloaders * @param string $namespace * @return Zend_Loader_Autoloader */ protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '') { $namespace = (string) $namespace; $this->_namespaceAutoloaders[$namespace] = $autoloaders; return $this; } /** * Retrieve the filesystem path for the requested ZF version * * @param string $path * @param string $version * @return void */ protected function _getVersionPath($path, $version) { $type = $this->_getVersionType($version); if ($type == 'latest') { $version = 'latest'; } $availableVersions = $this->_getAvailableVersions($path, $version); if (empty($availableVersions)) { throw new Zend_Loader_Exception('No valid ZF installations discovered'); } $matchedVersion = array_pop($availableVersions); return $matchedVersion; } /** * Retrieve the ZF version type * * @param string $version * @return string "latest", "major", "minor", or "specific" * @throws Zend_Loader_Exception if version string contains too many dots */ protected function _getVersionType($version) { if (strtolower($version) == 'latest') { return 'latest'; } $parts = explode('.', $version); $count = count($parts); if (1 == $count) { return 'major'; } if (2 == $count) { return 'minor'; } if (3 < $count) { throw new Zend_Loader_Exception('Invalid version string provided'); } return 'specific'; } /** * Get available versions for the version type requested * * @param string $path * @param string $version * @return array */ protected function _getAvailableVersions($path, $version) { if (!is_dir($path)) { throw new Zend_Loader_Exception('Invalid ZF path provided'); } $path = rtrim($path, '/'); $path = rtrim($path, '\\'); $versionLen = strlen($version); $versions = array(); $dirs = glob("$path/*", GLOB_ONLYDIR); foreach ((array) $dirs as $dir) { $dirName = substr($dir, strlen($path) + 1); if (!preg_match('/^(?:ZendFramework-)?(\d+\.\d+\.\d+((a|b|pl|pr|p|rc)\d+)?)(?:-minimal)?$/i', $dirName, $matches)) { continue; } $matchedVersion = $matches[1]; if (('latest' == $version) || ((strlen($matchedVersion) >= $versionLen) && (0 === strpos($matchedVersion, $version))) ) { $versions[$matchedVersion] = $dir . '/library'; } } uksort($versions, 'version_compare'); return $versions; } }