File indexing completed on 2024-12-22 05:36:49

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  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  */
0020 
0021 // Grab SplAutoloader interface
0022 // require_once dirname(__FILE__) . '/SplAutoloader.php';
0023 
0024 /**
0025  * PSR-0 compliant autoloader
0026  *
0027  * Allows autoloading both namespaced and vendor-prefixed classes. Class
0028  * lookups are performed on the filesystem. If a class file for the referenced
0029  * class is not found, a PHP warning will be raised by include().
0030  *
0031  * @package    Zend_Loader
0032  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0033  * @license    New BSD {@link http://framework.zend.com/license/new-bsd}
0034  */
0035 class Zend_Loader_StandardAutoloader implements Zend_Loader_SplAutoloader
0036 {
0037     const NS_SEPARATOR     = '\\';
0038     const PREFIX_SEPARATOR = '_';
0039     const LOAD_NS          = 'namespaces';
0040     const LOAD_PREFIX      = 'prefixes';
0041     const ACT_AS_FALLBACK  = 'fallback_autoloader';
0042     const AUTOREGISTER_ZF  = 'autoregister_zf';
0043 
0044     /**
0045      * @var array Namespace/directory pairs to search; ZF library added by default
0046      */
0047     protected $namespaces = array();
0048 
0049     /**
0050      * @var array Prefix/directory pairs to search
0051      */
0052     protected $prefixes = array();
0053 
0054     /**
0055      * @var bool Whether or not the autoloader should also act as a fallback autoloader
0056      */
0057     protected $fallbackAutoloaderFlag = false;
0058 
0059     /**
0060      * @var bool
0061      */
0062     protected $error;
0063 
0064     /**
0065      * Constructor
0066      *
0067      * @param  null|array|Traversable $options
0068      * @return void
0069      */
0070     public function __construct($options = null)
0071     {
0072         if (null !== $options) {
0073             $this->setOptions($options);
0074         }
0075     }
0076 
0077     /**
0078      * Configure autoloader
0079      *
0080      * Allows specifying both "namespace" and "prefix" pairs, using the
0081      * following structure:
0082      * <code>
0083      * array(
0084      *     'namespaces' => array(
0085      *         'Zend'     => '/path/to/Zend/library',
0086      *         'Doctrine' => '/path/to/Doctrine/library',
0087      *     ),
0088      *     'prefixes' => array(
0089      *         'Phly_'     => '/path/to/Phly/library',
0090      *     ),
0091      *     'fallback_autoloader' => true,
0092      * )
0093      * </code>
0094      *
0095      * @param  array|Traversable $options
0096      * @return Zend_Loader_StandardAutoloader
0097      */
0098     public function setOptions($options)
0099     {
0100         if (!is_array($options) && !($options instanceof Traversable)) {
0101             // require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
0102             throw new Zend_Loader_Exception_InvalidArgumentException('Options must be either an array or Traversable');
0103         }
0104 
0105         foreach ($options as $type => $pairs) {
0106             switch ($type) {
0107                 case self::AUTOREGISTER_ZF:
0108                     if ($pairs) {
0109                         $this->registerPrefix('Zend', dirname(dirname(__FILE__)));
0110                     }
0111                     break;
0112                 case self::LOAD_NS:
0113                     if (is_array($pairs) || $pairs instanceof Traversable) {
0114                         $this->registerNamespaces($pairs);
0115                     }
0116                     break;
0117                 case self::LOAD_PREFIX:
0118                     if (is_array($pairs) || $pairs instanceof Traversable) {
0119                         $this->registerPrefixes($pairs);
0120                     }
0121                     break;
0122                 case self::ACT_AS_FALLBACK:
0123                     $this->setFallbackAutoloader($pairs);
0124                     break;
0125                 default:
0126                     // ignore
0127             }
0128         }
0129         return $this;
0130     }
0131 
0132     /**
0133      * Set flag indicating fallback autoloader status
0134      *
0135      * @param  bool $flag
0136      * @return Zend_Loader_StandardAutoloader
0137      */
0138     public function setFallbackAutoloader($flag)
0139     {
0140         $this->fallbackAutoloaderFlag = (bool) $flag;
0141         return $this;
0142     }
0143 
0144     /**
0145      * Is this autoloader acting as a fallback autoloader?
0146      *
0147      * @return bool
0148      */
0149     public function isFallbackAutoloader()
0150     {
0151         return $this->fallbackAutoloaderFlag;
0152     }
0153 
0154     /**
0155      * Register a namespace/directory pair
0156      *
0157      * @param  string $namespace
0158      * @param  string $directory
0159      * @return Zend_Loader_StandardAutoloader
0160      */
0161     public function registerNamespace($namespace, $directory)
0162     {
0163         $namespace = rtrim($namespace, self::NS_SEPARATOR). self::NS_SEPARATOR;
0164         $this->namespaces[$namespace] = $this->normalizeDirectory($directory);
0165         return $this;
0166     }
0167 
0168     /**
0169      * Register many namespace/directory pairs at once
0170      *
0171      * @param  array $namespaces
0172      * @return Zend_Loader_StandardAutoloader
0173      */
0174     public function registerNamespaces($namespaces)
0175     {
0176         if (!is_array($namespaces) && !$namespaces instanceof Traversable) {
0177             // require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
0178             throw new Zend_Loader_Exception_InvalidArgumentException('Namespace pairs must be either an array or Traversable');
0179         }
0180 
0181         foreach ($namespaces as $namespace => $directory) {
0182             $this->registerNamespace($namespace, $directory);
0183         }
0184         return $this;
0185     }
0186 
0187     /**
0188      * Register a prefix/directory pair
0189      *
0190      * @param  string $prefix
0191      * @param  string $directory
0192      * @return Zend_Loader_StandardAutoloader
0193      */
0194     public function registerPrefix($prefix, $directory)
0195     {
0196         $prefix = rtrim($prefix, self::PREFIX_SEPARATOR). self::PREFIX_SEPARATOR;
0197         $this->prefixes[$prefix] = $this->normalizeDirectory($directory);
0198         return $this;
0199     }
0200 
0201     /**
0202      * Register many namespace/directory pairs at once
0203      *
0204      * @param  array $prefixes
0205      * @return Zend_Loader_StandardAutoloader
0206      */
0207     public function registerPrefixes($prefixes)
0208     {
0209         if (!is_array($prefixes) && !$prefixes instanceof Traversable) {
0210             // require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
0211             throw new Zend_Loader_Exception_InvalidArgumentException('Prefix pairs must be either an array or Traversable');
0212         }
0213 
0214         foreach ($prefixes as $prefix => $directory) {
0215             $this->registerPrefix($prefix, $directory);
0216         }
0217         return $this;
0218     }
0219 
0220     /**
0221      * Defined by Autoloadable; autoload a class
0222      *
0223      * @param  string $class
0224      * @return false|string
0225      */
0226     public function autoload($class)
0227     {
0228         $isFallback = $this->isFallbackAutoloader();
0229         if (false !== strpos($class, self::NS_SEPARATOR)) {
0230             if ($this->loadClass($class, self::LOAD_NS)) {
0231                 return $class;
0232             } elseif ($isFallback) {
0233                 return $this->loadClass($class, self::ACT_AS_FALLBACK);
0234             }
0235             return false;
0236         }
0237         if (false !== strpos($class, self::PREFIX_SEPARATOR)) {
0238             if ($this->loadClass($class, self::LOAD_PREFIX)) {
0239                 return $class;
0240             } elseif ($isFallback) {
0241                 return $this->loadClass($class, self::ACT_AS_FALLBACK);
0242             }
0243             return false;
0244         }
0245         if ($isFallback) {
0246             return $this->loadClass($class, self::ACT_AS_FALLBACK);
0247         }
0248         return false;
0249     }
0250 
0251     /**
0252      * Register the autoloader with spl_autoload
0253      *
0254      * @return void
0255      */
0256     public function register()
0257     {
0258         spl_autoload_register(array($this, 'autoload'));
0259     }
0260 
0261     /**
0262      * Error handler
0263      *
0264      * Used by {@link loadClass} during fallback autoloading in PHP versions
0265      * prior to 5.3.0.
0266      * 
0267      * @param mixed $errno 
0268      * @param mixed $errstr 
0269      * @return void
0270      */
0271     public function handleError($errno, $errstr)
0272     {
0273         $this->error = true;
0274     }
0275 
0276     /**
0277      * Transform the class name to a filename
0278      *
0279      * @param  string $class
0280      * @param  string $directory
0281      * @return string
0282      */
0283     protected function transformClassNameToFilename($class, $directory)
0284     {
0285         // $class may contain a namespace portion, in  which case we need
0286         // to preserve any underscores in that portion.
0287         $matches = array();
0288         preg_match('/(?P<namespace>.+\\\)?(?P<class>[^\\\]+$)/', $class, $matches);
0289 
0290         $class     = (isset($matches['class'])) ? $matches['class'] : '';
0291         $namespace = (isset($matches['namespace'])) ? $matches['namespace'] : '';
0292 
0293         return $directory
0294              . str_replace(self::NS_SEPARATOR, '/', $namespace)
0295              . str_replace(self::PREFIX_SEPARATOR, '/', $class)
0296              . '.php';
0297     }
0298 
0299     /**
0300      * Load a class, based on its type (namespaced or prefixed)
0301      *
0302      * @param  string $class
0303      * @param  string $type
0304      * @return void
0305      */
0306     protected function loadClass($class, $type)
0307     {
0308         if (!in_array($type, array(self::LOAD_NS, self::LOAD_PREFIX, self::ACT_AS_FALLBACK))) {
0309             // require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
0310             throw new Zend_Loader_Exception_InvalidArgumentException();
0311         }
0312 
0313         // Fallback autoloading
0314         if ($type === self::ACT_AS_FALLBACK) {
0315             // create filename
0316             $filename = $this->transformClassNameToFilename($class, '');
0317             if (version_compare(PHP_VERSION, '5.3.2', '>=')) {
0318                 $resolvedName = stream_resolve_include_path($filename);
0319                 if ($resolvedName !== false) {
0320                     return include $resolvedName;
0321                 }
0322                 return false;
0323             }
0324             $this->error = false;
0325             set_error_handler(array($this, 'handleError'), E_WARNING);
0326             include $filename;
0327             restore_error_handler();
0328             if ($this->error) {
0329                 return false;
0330             }
0331             return class_exists($class, false);
0332         }
0333 
0334         // Namespace and/or prefix autoloading
0335         foreach ($this->$type as $leader => $path) {
0336             if (0 === strpos($class, $leader)) {
0337                 // Trim off leader (namespace or prefix)
0338                 $trimmedClass = substr($class, strlen($leader));
0339 
0340                 // create filename
0341                 $filename = $this->transformClassNameToFilename($trimmedClass, $path);
0342                 if (file_exists($filename)) {
0343                     return include $filename;
0344                 }
0345                 return false;
0346             }
0347         }
0348         return false;
0349     }
0350 
0351     /**
0352      * Normalize the directory to include a trailing directory separator
0353      *
0354      * @param  string $directory
0355      * @return string
0356      */
0357     protected function normalizeDirectory($directory)
0358     {
0359         $last = $directory[strlen($directory) - 1];
0360         if (in_array($last, array('/', '\\'))) {
0361             $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR;
0362             return $directory;
0363         }
0364         $directory .= DIRECTORY_SEPARATOR;
0365         return $directory;
0366     }
0367 
0368 }