3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Loader
;
12 // Grab SplAutoloader interface
13 require_once __DIR__
. '/SplAutoloader.php';
16 * PSR-0 compliant autoloader
18 * Allows autoloading both namespaced and vendor-prefixed classes. Class
19 * lookups are performed on the filesystem. If a class file for the referenced
20 * class is not found, a PHP warning will be raised by include().
22 class StandardAutoloader
implements SplAutoloader
24 const NS_SEPARATOR
= '\\';
25 const PREFIX_SEPARATOR
= '_';
26 const LOAD_NS
= 'namespaces';
27 const LOAD_PREFIX
= 'prefixes';
28 const ACT_AS_FALLBACK
= 'fallback_autoloader';
29 const AUTOREGISTER_ZF
= 'autoregister_zf';
32 * @var array Namespace/directory pairs to search; ZF library added by default
34 protected $namespaces = array();
37 * @var array Prefix/directory pairs to search
39 protected $prefixes = array();
42 * @var bool Whether or not the autoloader should also act as a fallback autoloader
44 protected $fallbackAutoloaderFlag = false;
49 * @param null|array|\Traversable $options
51 public function __construct($options = null)
53 if (null !== $options) {
54 $this->setOptions($options);
59 * Configure autoloader
61 * Allows specifying both "namespace" and "prefix" pairs, using the
62 * following structure:
65 * 'namespaces' => array(
66 * 'Zend' => '/path/to/Zend/library',
67 * 'Doctrine' => '/path/to/Doctrine/library',
69 * 'prefixes' => array(
70 * 'Phly_' => '/path/to/Phly/library',
72 * 'fallback_autoloader' => true,
76 * @param array|\Traversable $options
77 * @throws Exception\InvalidArgumentException
78 * @return StandardAutoloader
80 public function setOptions($options)
82 if (!is_array($options) && !($options instanceof \Traversable
)) {
83 require_once __DIR__
. '/Exception/InvalidArgumentException.php';
84 throw new Exception\
InvalidArgumentException('Options must be either an array or Traversable');
87 foreach ($options as $type => $pairs) {
89 case self
::AUTOREGISTER_ZF
:
91 $this->registerNamespace('Zend', dirname(__DIR__
));
95 if (is_array($pairs) ||
$pairs instanceof \Traversable
) {
96 $this->registerNamespaces($pairs);
99 case self
::LOAD_PREFIX
:
100 if (is_array($pairs) ||
$pairs instanceof \Traversable
) {
101 $this->registerPrefixes($pairs);
104 case self
::ACT_AS_FALLBACK
:
105 $this->setFallbackAutoloader($pairs);
115 * Set flag indicating fallback autoloader status
118 * @return StandardAutoloader
120 public function setFallbackAutoloader($flag)
122 $this->fallbackAutoloaderFlag
= (bool) $flag;
127 * Is this autoloader acting as a fallback autoloader?
131 public function isFallbackAutoloader()
133 return $this->fallbackAutoloaderFlag
;
137 * Register a namespace/directory pair
139 * @param string $namespace
140 * @param string $directory
141 * @return StandardAutoloader
143 public function registerNamespace($namespace, $directory)
145 $namespace = rtrim($namespace, self
::NS_SEPARATOR
) . self
::NS_SEPARATOR
;
146 $this->namespaces
[$namespace] = $this->normalizeDirectory($directory);
151 * Register many namespace/directory pairs at once
153 * @param array $namespaces
154 * @throws Exception\InvalidArgumentException
155 * @return StandardAutoloader
157 public function registerNamespaces($namespaces)
159 if (!is_array($namespaces) && !$namespaces instanceof \Traversable
) {
160 require_once __DIR__
. '/Exception/InvalidArgumentException.php';
161 throw new Exception\
InvalidArgumentException('Namespace pairs must be either an array or Traversable');
164 foreach ($namespaces as $namespace => $directory) {
165 $this->registerNamespace($namespace, $directory);
171 * Register a prefix/directory pair
173 * @param string $prefix
174 * @param string $directory
175 * @return StandardAutoloader
177 public function registerPrefix($prefix, $directory)
179 $prefix = rtrim($prefix, self
::PREFIX_SEPARATOR
). self
::PREFIX_SEPARATOR
;
180 $this->prefixes
[$prefix] = $this->normalizeDirectory($directory);
185 * Register many namespace/directory pairs at once
187 * @param array $prefixes
188 * @throws Exception\InvalidArgumentException
189 * @return StandardAutoloader
191 public function registerPrefixes($prefixes)
193 if (!is_array($prefixes) && !$prefixes instanceof \Traversable
) {
194 require_once __DIR__
. '/Exception/InvalidArgumentException.php';
195 throw new Exception\
InvalidArgumentException('Prefix pairs must be either an array or Traversable');
198 foreach ($prefixes as $prefix => $directory) {
199 $this->registerPrefix($prefix, $directory);
205 * Defined by Autoloadable; autoload a class
207 * @param string $class
208 * @return false|string
210 public function autoload($class)
212 $isFallback = $this->isFallbackAutoloader();
213 if (false !== strpos($class, self
::NS_SEPARATOR
)) {
214 if ($this->loadClass($class, self
::LOAD_NS
)) {
216 } elseif ($isFallback) {
217 return $this->loadClass($class, self
::ACT_AS_FALLBACK
);
221 if (false !== strpos($class, self
::PREFIX_SEPARATOR
)) {
222 if ($this->loadClass($class, self
::LOAD_PREFIX
)) {
224 } elseif ($isFallback) {
225 return $this->loadClass($class, self
::ACT_AS_FALLBACK
);
230 return $this->loadClass($class, self
::ACT_AS_FALLBACK
);
236 * Register the autoloader with spl_autoload
240 public function register()
242 spl_autoload_register(array($this, 'autoload'));
246 * Transform the class name to a filename
248 * @param string $class
249 * @param string $directory
252 protected function transformClassNameToFilename($class, $directory)
254 // $class may contain a namespace portion, in which case we need
255 // to preserve any underscores in that portion.
257 preg_match('/(?P<namespace>.+\\\)?(?P<class>[^\\\]+$)/', $class, $matches);
259 $class = (isset($matches['class'])) ?
$matches['class'] : '';
260 $namespace = (isset($matches['namespace'])) ?
$matches['namespace'] : '';
263 . str_replace(self
::NS_SEPARATOR
, '/', $namespace)
264 . str_replace(self
::PREFIX_SEPARATOR
, '/', $class)
269 * Load a class, based on its type (namespaced or prefixed)
271 * @param string $class
272 * @param string $type
273 * @return bool|string
274 * @throws Exception\InvalidArgumentException
276 protected function loadClass($class, $type)
278 if (!in_array($type, array(self
::LOAD_NS
, self
::LOAD_PREFIX
, self
::ACT_AS_FALLBACK
))) {
279 require_once __DIR__
. '/Exception/InvalidArgumentException.php';
280 throw new Exception\
InvalidArgumentException();
283 // Fallback autoloading
284 if ($type === self
::ACT_AS_FALLBACK
) {
286 $filename = $this->transformClassNameToFilename($class, '');
287 $resolvedName = stream_resolve_include_path($filename);
288 if ($resolvedName !== false) {
289 return include $resolvedName;
294 // Namespace and/or prefix autoloading
295 foreach ($this->$type as $leader => $path) {
296 if (0 === strpos($class, $leader)) {
297 // Trim off leader (namespace or prefix)
298 $trimmedClass = substr($class, strlen($leader));
301 $filename = $this->transformClassNameToFilename($trimmedClass, $path);
302 if (file_exists($filename)) {
303 return include $filename;
312 * Normalize the directory to include a trailing directory separator
314 * @param string $directory
317 protected function normalizeDirectory($directory)
319 $last = $directory[strlen($directory) - 1];
320 if (in_array($last, array('/', '\\'))) {
321 $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR
;
324 $directory .= DIRECTORY_SEPARATOR
;