00001 <?php
00024 require_once 'Zend/Loader.php';
00025
00035 class Zend_Loader_Autoloader
00036 {
00040 protected static $_instance;
00041
00045 protected $_autoloaders = array();
00046
00050 protected $_defaultAutoloader = array('Zend_Loader', 'loadClass');
00051
00055 protected $_fallbackAutoloader = false;
00056
00060 protected $_internalAutoloader;
00061
00065 protected $_namespaces = array(
00066 'Zend_' => true,
00067 'ZendX_' => true,
00068 );
00069
00073 protected $_namespaceAutoloaders = array();
00074
00078 protected $_suppressNotFoundWarnings = false;
00079
00083 protected $_zfPath;
00084
00090 public static function getInstance()
00091 {
00092 if (null === self::$_instance) {
00093 self::$_instance = new self();
00094 }
00095 return self::$_instance;
00096 }
00097
00103 public static function resetInstance()
00104 {
00105 self::$_instance = null;
00106 }
00107
00114 public static function autoload($class)
00115 {
00116 $self = self::getInstance();
00117
00118 foreach ($self->getClassAutoloaders($class) as $autoloader) {
00119 if ($autoloader instanceof Zend_Loader_Autoloader_Interface) {
00120 if ($autoloader->autoload($class)) {
00121 return true;
00122 }
00123 } elseif (is_string($autoloader)) {
00124 if ($autoloader($class)) {
00125 return true;
00126 }
00127 } elseif (is_array($autoloader)) {
00128 $object = array_shift($autoloader);
00129 $method = array_shift($autoloader);
00130 if (call_user_func(array($object, $method), $class)) {
00131 return true;
00132 }
00133 }
00134 }
00135
00136 return false;
00137 }
00138
00145 public function setDefaultAutoloader($callback)
00146 {
00147 if (!is_callable($callback)) {
00148 throw new Zend_Loader_Exception('Invalid callback specified for default autoloader');
00149 }
00150
00151 $this->_defaultAutoloader = $callback;
00152 return $this;
00153 }
00154
00160 public function getDefaultAutoloader()
00161 {
00162 return $this->_defaultAutoloader;
00163 }
00164
00171 public function setAutoloaders(array $autoloaders)
00172 {
00173 $this->_autoloaders = $autoloaders;
00174 return $this;
00175 }
00176
00182 public function getAutoloaders()
00183 {
00184 return $this->_autoloaders;
00185 }
00186
00193 public function getNamespaceAutoloaders($namespace)
00194 {
00195 $namespace = (string) $namespace;
00196 if (!array_key_exists($namespace, $this->_namespaceAutoloaders)) {
00197 return array();
00198 }
00199 return $this->_namespaceAutoloaders[$namespace];
00200 }
00201
00208 public function registerNamespace($namespace)
00209 {
00210 if (is_string($namespace)) {
00211 $namespace = (array) $namespace;
00212 } elseif (!is_array($namespace)) {
00213 throw new Zend_Loader_Exception('Invalid namespace provided');
00214 }
00215
00216 foreach ($namespace as $ns) {
00217 if (!isset($this->_namespaces[$ns])) {
00218 $this->_namespaces[$ns] = true;
00219 }
00220 }
00221 return $this;
00222 }
00223
00230 public function unregisterNamespace($namespace)
00231 {
00232 if (is_string($namespace)) {
00233 $namespace = (array) $namespace;
00234 } elseif (!is_array($namespace)) {
00235 throw new Zend_Loader_Exception('Invalid namespace provided');
00236 }
00237
00238 foreach ($namespace as $ns) {
00239 if (isset($this->_namespaces[$ns])) {
00240 unset($this->_namespaces[$ns]);
00241 }
00242 }
00243 return $this;
00244 }
00245
00251 public function getRegisteredNamespaces()
00252 {
00253 return array_keys($this->_namespaces);
00254 }
00255
00256 public function setZfPath($spec, $version = 'latest')
00257 {
00258 $path = $spec;
00259 if (is_array($spec)) {
00260 if (!isset($spec['path'])) {
00261 throw new Zend_Loader_Exception('No path specified for ZF');
00262 }
00263 $path = $spec['path'];
00264 if (isset($spec['version'])) {
00265 $version = $spec['version'];
00266 }
00267 }
00268
00269 $this->_zfPath = $this->_getVersionPath($path, $version);
00270 set_include_path(implode(PATH_SEPARATOR, array(
00271 $this->_zfPath,
00272 get_include_path(),
00273 )));
00274 return $this;
00275 }
00276
00277 public function getZfPath()
00278 {
00279 return $this->_zfPath;
00280 }
00281
00288 public function suppressNotFoundWarnings($flag = null)
00289 {
00290 if (null === $flag) {
00291 return $this->_suppressNotFoundWarnings;
00292 }
00293 $this->_suppressNotFoundWarnings = (bool) $flag;
00294 return $this;
00295 }
00296
00303 public function setFallbackAutoloader($flag)
00304 {
00305 $this->_fallbackAutoloader = (bool) $flag;
00306 return $this;
00307 }
00308
00314 public function isFallbackAutoloader()
00315 {
00316 return $this->_fallbackAutoloader;
00317 }
00318
00329 public function getClassAutoloaders($class)
00330 {
00331 $namespace = false;
00332 $autoloaders = array();
00333
00334
00335 foreach (array_keys($this->_namespaceAutoloaders) as $ns) {
00336 if ('' == $ns) {
00337 continue;
00338 }
00339 if (0 === strpos($class, $ns)) {
00340 $namespace = $ns;
00341 $autoloaders = $autoloaders + $this->getNamespaceAutoloaders($ns);
00342 break;
00343 }
00344 }
00345
00346
00347 foreach ($this->getRegisteredNamespaces() as $ns) {
00348 if (0 === strpos($class, $ns)) {
00349 $namespace = $ns;
00350 $autoloaders[] = $this->_internalAutoloader;
00351 break;
00352 }
00353 }
00354
00355
00356 $autoloaders = $autoloaders + $this->getNamespaceAutoloaders('');
00357
00358
00359 if (!$namespace && $this->isFallbackAutoloader()) {
00360 $autoloaders[] = $this->_internalAutoloader;
00361 }
00362
00363 return $autoloaders;
00364 }
00365
00373 public function unshiftAutoloader($callback, $namespace = '')
00374 {
00375 $autoloaders = $this->getAutoloaders();
00376 array_unshift($autoloaders, $callback);
00377 $this->setAutoloaders($autoloaders);
00378
00379 $namespace = (array) $namespace;
00380 foreach ($namespace as $ns) {
00381 $autoloaders = $this->getNamespaceAutoloaders($ns);
00382 array_unshift($autoloaders, $callback);
00383 $this->_setNamespaceAutoloaders($autoloaders, $ns);
00384 }
00385
00386 return $this;
00387 }
00388
00396 public function pushAutoloader($callback, $namespace = '')
00397 {
00398 $autoloaders = $this->getAutoloaders();
00399 array_push($autoloaders, $callback);
00400 $this->setAutoloaders($autoloaders);
00401
00402 $namespace = (array) $namespace;
00403 foreach ($namespace as $ns) {
00404 $autoloaders = $this->getNamespaceAutoloaders($ns);
00405 array_push($autoloaders, $callback);
00406 $this->_setNamespaceAutoloaders($autoloaders, $ns);
00407 }
00408
00409 return $this;
00410 }
00411
00419 public function removeAutoloader($callback, $namespace = null)
00420 {
00421 if (null === $namespace) {
00422 $autoloaders = $this->getAutoloaders();
00423 if (false !== ($index = array_search($callback, $autoloaders, true))) {
00424 unset($autoloaders[$index]);
00425 $this->setAutoloaders($autoloaders);
00426 }
00427
00428 foreach ($this->_namespaceAutoloaders as $ns => $autoloaders) {
00429 if (false !== ($index = array_search($callback, $autoloaders, true))) {
00430 unset($autoloaders[$index]);
00431 $this->_setNamespaceAutoloaders($autoloaders, $ns);
00432 }
00433 }
00434 } else {
00435 $namespace = (array) $namespace;
00436 foreach ($namespace as $ns) {
00437 $autoloaders = $this->getNamespaceAutoloaders($ns);
00438 if (false !== ($index = array_search($callback, $autoloaders, true))) {
00439 unset($autoloaders[$index]);
00440 $this->_setNamespaceAutoloaders($autoloaders, $ns);
00441 }
00442 }
00443 }
00444
00445 return $this;
00446 }
00447
00455 protected function __construct()
00456 {
00457 spl_autoload_register(array(__CLASS__, 'autoload'));
00458 $this->_internalAutoloader = array($this, '_autoload');
00459 }
00460
00467 protected function _autoload($class)
00468 {
00469 $callback = $this->getDefaultAutoloader();
00470 try {
00471 if ($this->suppressNotFoundWarnings()) {
00472 @call_user_func($callback, $class);
00473 } else {
00474 call_user_func($callback, $class);
00475 }
00476 return $class;
00477 } catch (Zend_Exception $e) {
00478 return false;
00479 }
00480 }
00481
00489 protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '')
00490 {
00491 $namespace = (string) $namespace;
00492 $this->_namespaceAutoloaders[$namespace] = $autoloaders;
00493 return $this;
00494 }
00495
00503 protected function _getVersionPath($path, $version)
00504 {
00505 $type = $this->_getVersionType($version);
00506
00507 if ($type == 'latest') {
00508 $version = 'latest';
00509 }
00510
00511 $availableVersions = $this->_getAvailableVersions($path, $version);
00512 if (empty($availableVersions)) {
00513 throw new Zend_Loader_Exception('No valid ZF installations discovered');
00514 }
00515
00516 $matchedVersion = array_pop($availableVersions);
00517 return $matchedVersion;
00518 }
00519
00527 protected function _getVersionType($version)
00528 {
00529 if (strtolower($version) == 'latest') {
00530 return 'latest';
00531 }
00532
00533 $parts = explode('.', $version);
00534 $count = count($parts);
00535 if (1 == $count) {
00536 return 'major';
00537 }
00538 if (2 == $count) {
00539 return 'minor';
00540 }
00541 if (3 < $count) {
00542 throw new Zend_Loader_Exception('Invalid version string provided');
00543 }
00544 return 'specific';
00545 }
00546
00554 protected function _getAvailableVersions($path, $version)
00555 {
00556 if (!is_dir($path)) {
00557 throw new Zend_Loader_Exception('Invalid ZF path provided');
00558 }
00559
00560 $path = rtrim($path, '/');
00561 $path = rtrim($path, '\\');
00562 $versionLen = strlen($version);
00563 $versions = array();
00564 $dirs = glob("$path/*", GLOB_ONLYDIR);
00565 foreach ($dirs as $dir) {
00566 $dirName = substr($dir, strlen($path) + 1);
00567 if (!preg_match('/^(?:ZendFramework-)?(\d+\.\d+\.\d+((a|b|pl|pr|p|rc)\d+)?)(?:-minimal)?$/i', $dirName, $matches)) {
00568 continue;
00569 }
00570
00571 $matchedVersion = $matches[1];
00572
00573 if (('latest' == $version)
00574 || ((strlen($matchedVersion) >= $versionLen)
00575 && (0 === strpos($matchedVersion, $version)))
00576 ) {
00577 $versions[$matchedVersion] = $dir . '/library';
00578 }
00579 }
00580
00581 uksort($versions, 'version_compare');
00582 return $versions;
00583 }
00584 }