590 lines
16 KiB
PHP
590 lines
16 KiB
PHP
<?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 license@zend.com 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;
|
|
}
|
|
}
|