|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Loader |
|
17 * @subpackage Autoloader |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @version $Id: Autoloader.php 23161 2010-10-19 16:08:36Z matthew $ |
|
20 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
21 */ |
|
22 |
|
23 /** Zend_Loader */ |
|
24 require_once 'Zend/Loader.php'; |
|
25 |
|
26 /** |
|
27 * Autoloader stack and namespace autoloader |
|
28 * |
|
29 * @uses Zend_Loader_Autoloader |
|
30 * @package Zend_Loader |
|
31 * @subpackage Autoloader |
|
32 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
33 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
34 */ |
|
35 class Zend_Loader_Autoloader |
|
36 { |
|
37 /** |
|
38 * @var Zend_Loader_Autoloader Singleton instance |
|
39 */ |
|
40 protected static $_instance; |
|
41 |
|
42 /** |
|
43 * @var array Concrete autoloader callback implementations |
|
44 */ |
|
45 protected $_autoloaders = array(); |
|
46 |
|
47 /** |
|
48 * @var array Default autoloader callback |
|
49 */ |
|
50 protected $_defaultAutoloader = array('Zend_Loader', 'loadClass'); |
|
51 |
|
52 /** |
|
53 * @var bool Whether or not to act as a fallback autoloader |
|
54 */ |
|
55 protected $_fallbackAutoloader = false; |
|
56 |
|
57 /** |
|
58 * @var array Callback for internal autoloader implementation |
|
59 */ |
|
60 protected $_internalAutoloader; |
|
61 |
|
62 /** |
|
63 * @var array Supported namespaces 'Zend' and 'ZendX' by default. |
|
64 */ |
|
65 protected $_namespaces = array( |
|
66 'Zend_' => true, |
|
67 'ZendX_' => true, |
|
68 ); |
|
69 |
|
70 /** |
|
71 * @var array Namespace-specific autoloaders |
|
72 */ |
|
73 protected $_namespaceAutoloaders = array(); |
|
74 |
|
75 /** |
|
76 * @var bool Whether or not to suppress file not found warnings |
|
77 */ |
|
78 protected $_suppressNotFoundWarnings = false; |
|
79 |
|
80 /** |
|
81 * @var null|string |
|
82 */ |
|
83 protected $_zfPath; |
|
84 |
|
85 /** |
|
86 * Retrieve singleton instance |
|
87 * |
|
88 * @return Zend_Loader_Autoloader |
|
89 */ |
|
90 public static function getInstance() |
|
91 { |
|
92 if (null === self::$_instance) { |
|
93 self::$_instance = new self(); |
|
94 } |
|
95 return self::$_instance; |
|
96 } |
|
97 |
|
98 /** |
|
99 * Reset the singleton instance |
|
100 * |
|
101 * @return void |
|
102 */ |
|
103 public static function resetInstance() |
|
104 { |
|
105 self::$_instance = null; |
|
106 } |
|
107 |
|
108 /** |
|
109 * Autoload a class |
|
110 * |
|
111 * @param string $class |
|
112 * @return bool |
|
113 */ |
|
114 public static function autoload($class) |
|
115 { |
|
116 $self = self::getInstance(); |
|
117 |
|
118 foreach ($self->getClassAutoloaders($class) as $autoloader) { |
|
119 if ($autoloader instanceof Zend_Loader_Autoloader_Interface) { |
|
120 if ($autoloader->autoload($class)) { |
|
121 return true; |
|
122 } |
|
123 } elseif (is_array($autoloader)) { |
|
124 if (call_user_func($autoloader, $class)) { |
|
125 return true; |
|
126 } |
|
127 } elseif (is_string($autoloader) || is_callable($autoloader)) { |
|
128 if ($autoloader($class)) { |
|
129 return true; |
|
130 } |
|
131 } |
|
132 } |
|
133 |
|
134 return false; |
|
135 } |
|
136 |
|
137 /** |
|
138 * Set the default autoloader implementation |
|
139 * |
|
140 * @param string|array $callback PHP callback |
|
141 * @return void |
|
142 */ |
|
143 public function setDefaultAutoloader($callback) |
|
144 { |
|
145 if (!is_callable($callback)) { |
|
146 throw new Zend_Loader_Exception('Invalid callback specified for default autoloader'); |
|
147 } |
|
148 |
|
149 $this->_defaultAutoloader = $callback; |
|
150 return $this; |
|
151 } |
|
152 |
|
153 /** |
|
154 * Retrieve the default autoloader callback |
|
155 * |
|
156 * @return string|array PHP Callback |
|
157 */ |
|
158 public function getDefaultAutoloader() |
|
159 { |
|
160 return $this->_defaultAutoloader; |
|
161 } |
|
162 |
|
163 /** |
|
164 * Set several autoloader callbacks at once |
|
165 * |
|
166 * @param array $autoloaders Array of PHP callbacks (or Zend_Loader_Autoloader_Interface implementations) to act as autoloaders |
|
167 * @return Zend_Loader_Autoloader |
|
168 */ |
|
169 public function setAutoloaders(array $autoloaders) |
|
170 { |
|
171 $this->_autoloaders = $autoloaders; |
|
172 return $this; |
|
173 } |
|
174 |
|
175 /** |
|
176 * Get attached autoloader implementations |
|
177 * |
|
178 * @return array |
|
179 */ |
|
180 public function getAutoloaders() |
|
181 { |
|
182 return $this->_autoloaders; |
|
183 } |
|
184 |
|
185 /** |
|
186 * Return all autoloaders for a given namespace |
|
187 * |
|
188 * @param string $namespace |
|
189 * @return array |
|
190 */ |
|
191 public function getNamespaceAutoloaders($namespace) |
|
192 { |
|
193 $namespace = (string) $namespace; |
|
194 if (!array_key_exists($namespace, $this->_namespaceAutoloaders)) { |
|
195 return array(); |
|
196 } |
|
197 return $this->_namespaceAutoloaders[$namespace]; |
|
198 } |
|
199 |
|
200 /** |
|
201 * Register a namespace to autoload |
|
202 * |
|
203 * @param string|array $namespace |
|
204 * @return Zend_Loader_Autoloader |
|
205 */ |
|
206 public function registerNamespace($namespace) |
|
207 { |
|
208 if (is_string($namespace)) { |
|
209 $namespace = (array) $namespace; |
|
210 } elseif (!is_array($namespace)) { |
|
211 throw new Zend_Loader_Exception('Invalid namespace provided'); |
|
212 } |
|
213 |
|
214 foreach ($namespace as $ns) { |
|
215 if (!isset($this->_namespaces[$ns])) { |
|
216 $this->_namespaces[$ns] = true; |
|
217 } |
|
218 } |
|
219 return $this; |
|
220 } |
|
221 |
|
222 /** |
|
223 * Unload a registered autoload namespace |
|
224 * |
|
225 * @param string|array $namespace |
|
226 * @return Zend_Loader_Autoloader |
|
227 */ |
|
228 public function unregisterNamespace($namespace) |
|
229 { |
|
230 if (is_string($namespace)) { |
|
231 $namespace = (array) $namespace; |
|
232 } elseif (!is_array($namespace)) { |
|
233 throw new Zend_Loader_Exception('Invalid namespace provided'); |
|
234 } |
|
235 |
|
236 foreach ($namespace as $ns) { |
|
237 if (isset($this->_namespaces[$ns])) { |
|
238 unset($this->_namespaces[$ns]); |
|
239 } |
|
240 } |
|
241 return $this; |
|
242 } |
|
243 |
|
244 /** |
|
245 * Get a list of registered autoload namespaces |
|
246 * |
|
247 * @return array |
|
248 */ |
|
249 public function getRegisteredNamespaces() |
|
250 { |
|
251 return array_keys($this->_namespaces); |
|
252 } |
|
253 |
|
254 public function setZfPath($spec, $version = 'latest') |
|
255 { |
|
256 $path = $spec; |
|
257 if (is_array($spec)) { |
|
258 if (!isset($spec['path'])) { |
|
259 throw new Zend_Loader_Exception('No path specified for ZF'); |
|
260 } |
|
261 $path = $spec['path']; |
|
262 if (isset($spec['version'])) { |
|
263 $version = $spec['version']; |
|
264 } |
|
265 } |
|
266 |
|
267 $this->_zfPath = $this->_getVersionPath($path, $version); |
|
268 set_include_path(implode(PATH_SEPARATOR, array( |
|
269 $this->_zfPath, |
|
270 get_include_path(), |
|
271 ))); |
|
272 return $this; |
|
273 } |
|
274 |
|
275 public function getZfPath() |
|
276 { |
|
277 return $this->_zfPath; |
|
278 } |
|
279 |
|
280 /** |
|
281 * Get or set the value of the "suppress not found warnings" flag |
|
282 * |
|
283 * @param null|bool $flag |
|
284 * @return bool|Zend_Loader_Autoloader Returns boolean if no argument is passed, object instance otherwise |
|
285 */ |
|
286 public function suppressNotFoundWarnings($flag = null) |
|
287 { |
|
288 if (null === $flag) { |
|
289 return $this->_suppressNotFoundWarnings; |
|
290 } |
|
291 $this->_suppressNotFoundWarnings = (bool) $flag; |
|
292 return $this; |
|
293 } |
|
294 |
|
295 /** |
|
296 * Indicate whether or not this autoloader should be a fallback autoloader |
|
297 * |
|
298 * @param bool $flag |
|
299 * @return Zend_Loader_Autoloader |
|
300 */ |
|
301 public function setFallbackAutoloader($flag) |
|
302 { |
|
303 $this->_fallbackAutoloader = (bool) $flag; |
|
304 return $this; |
|
305 } |
|
306 |
|
307 /** |
|
308 * Is this instance acting as a fallback autoloader? |
|
309 * |
|
310 * @return bool |
|
311 */ |
|
312 public function isFallbackAutoloader() |
|
313 { |
|
314 return $this->_fallbackAutoloader; |
|
315 } |
|
316 |
|
317 /** |
|
318 * Get autoloaders to use when matching class |
|
319 * |
|
320 * Determines if the class matches a registered namespace, and, if so, |
|
321 * returns only the autoloaders for that namespace. Otherwise, it returns |
|
322 * all non-namespaced autoloaders. |
|
323 * |
|
324 * @param string $class |
|
325 * @return array Array of autoloaders to use |
|
326 */ |
|
327 public function getClassAutoloaders($class) |
|
328 { |
|
329 $namespace = false; |
|
330 $autoloaders = array(); |
|
331 |
|
332 // Add concrete namespaced autoloaders |
|
333 foreach (array_keys($this->_namespaceAutoloaders) as $ns) { |
|
334 if ('' == $ns) { |
|
335 continue; |
|
336 } |
|
337 if (0 === strpos($class, $ns)) { |
|
338 $namespace = $ns; |
|
339 $autoloaders = $autoloaders + $this->getNamespaceAutoloaders($ns); |
|
340 break; |
|
341 } |
|
342 } |
|
343 |
|
344 // Add internal namespaced autoloader |
|
345 foreach ($this->getRegisteredNamespaces() as $ns) { |
|
346 if (0 === strpos($class, $ns)) { |
|
347 $namespace = $ns; |
|
348 $autoloaders[] = $this->_internalAutoloader; |
|
349 break; |
|
350 } |
|
351 } |
|
352 |
|
353 // Add non-namespaced autoloaders |
|
354 $autoloaders = $autoloaders + $this->getNamespaceAutoloaders(''); |
|
355 |
|
356 // Add fallback autoloader |
|
357 if (!$namespace && $this->isFallbackAutoloader()) { |
|
358 $autoloaders[] = $this->_internalAutoloader; |
|
359 } |
|
360 |
|
361 return $autoloaders; |
|
362 } |
|
363 |
|
364 /** |
|
365 * Add an autoloader to the beginning of the stack |
|
366 * |
|
367 * @param object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation |
|
368 * @param string|array $namespace Specific namespace(s) under which to register callback |
|
369 * @return Zend_Loader_Autoloader |
|
370 */ |
|
371 public function unshiftAutoloader($callback, $namespace = '') |
|
372 { |
|
373 $autoloaders = $this->getAutoloaders(); |
|
374 array_unshift($autoloaders, $callback); |
|
375 $this->setAutoloaders($autoloaders); |
|
376 |
|
377 $namespace = (array) $namespace; |
|
378 foreach ($namespace as $ns) { |
|
379 $autoloaders = $this->getNamespaceAutoloaders($ns); |
|
380 array_unshift($autoloaders, $callback); |
|
381 $this->_setNamespaceAutoloaders($autoloaders, $ns); |
|
382 } |
|
383 |
|
384 return $this; |
|
385 } |
|
386 |
|
387 /** |
|
388 * Append an autoloader to the autoloader stack |
|
389 * |
|
390 * @param object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation |
|
391 * @param string|array $namespace Specific namespace(s) under which to register callback |
|
392 * @return Zend_Loader_Autoloader |
|
393 */ |
|
394 public function pushAutoloader($callback, $namespace = '') |
|
395 { |
|
396 $autoloaders = $this->getAutoloaders(); |
|
397 array_push($autoloaders, $callback); |
|
398 $this->setAutoloaders($autoloaders); |
|
399 |
|
400 $namespace = (array) $namespace; |
|
401 foreach ($namespace as $ns) { |
|
402 $autoloaders = $this->getNamespaceAutoloaders($ns); |
|
403 array_push($autoloaders, $callback); |
|
404 $this->_setNamespaceAutoloaders($autoloaders, $ns); |
|
405 } |
|
406 |
|
407 return $this; |
|
408 } |
|
409 |
|
410 /** |
|
411 * Remove an autoloader from the autoloader stack |
|
412 * |
|
413 * @param object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation |
|
414 * @param null|string|array $namespace Specific namespace(s) from which to remove autoloader |
|
415 * @return Zend_Loader_Autoloader |
|
416 */ |
|
417 public function removeAutoloader($callback, $namespace = null) |
|
418 { |
|
419 if (null === $namespace) { |
|
420 $autoloaders = $this->getAutoloaders(); |
|
421 if (false !== ($index = array_search($callback, $autoloaders, true))) { |
|
422 unset($autoloaders[$index]); |
|
423 $this->setAutoloaders($autoloaders); |
|
424 } |
|
425 |
|
426 foreach ($this->_namespaceAutoloaders as $ns => $autoloaders) { |
|
427 if (false !== ($index = array_search($callback, $autoloaders, true))) { |
|
428 unset($autoloaders[$index]); |
|
429 $this->_setNamespaceAutoloaders($autoloaders, $ns); |
|
430 } |
|
431 } |
|
432 } else { |
|
433 $namespace = (array) $namespace; |
|
434 foreach ($namespace as $ns) { |
|
435 $autoloaders = $this->getNamespaceAutoloaders($ns); |
|
436 if (false !== ($index = array_search($callback, $autoloaders, true))) { |
|
437 unset($autoloaders[$index]); |
|
438 $this->_setNamespaceAutoloaders($autoloaders, $ns); |
|
439 } |
|
440 } |
|
441 } |
|
442 |
|
443 return $this; |
|
444 } |
|
445 |
|
446 /** |
|
447 * Constructor |
|
448 * |
|
449 * Registers instance with spl_autoload stack |
|
450 * |
|
451 * @return void |
|
452 */ |
|
453 protected function __construct() |
|
454 { |
|
455 spl_autoload_register(array(__CLASS__, 'autoload')); |
|
456 $this->_internalAutoloader = array($this, '_autoload'); |
|
457 } |
|
458 |
|
459 /** |
|
460 * Internal autoloader implementation |
|
461 * |
|
462 * @param string $class |
|
463 * @return bool |
|
464 */ |
|
465 protected function _autoload($class) |
|
466 { |
|
467 $callback = $this->getDefaultAutoloader(); |
|
468 try { |
|
469 if ($this->suppressNotFoundWarnings()) { |
|
470 @call_user_func($callback, $class); |
|
471 } else { |
|
472 call_user_func($callback, $class); |
|
473 } |
|
474 return $class; |
|
475 } catch (Zend_Exception $e) { |
|
476 return false; |
|
477 } |
|
478 } |
|
479 |
|
480 /** |
|
481 * Set autoloaders for a specific namespace |
|
482 * |
|
483 * @param array $autoloaders |
|
484 * @param string $namespace |
|
485 * @return Zend_Loader_Autoloader |
|
486 */ |
|
487 protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '') |
|
488 { |
|
489 $namespace = (string) $namespace; |
|
490 $this->_namespaceAutoloaders[$namespace] = $autoloaders; |
|
491 return $this; |
|
492 } |
|
493 |
|
494 /** |
|
495 * Retrieve the filesystem path for the requested ZF version |
|
496 * |
|
497 * @param string $path |
|
498 * @param string $version |
|
499 * @return void |
|
500 */ |
|
501 protected function _getVersionPath($path, $version) |
|
502 { |
|
503 $type = $this->_getVersionType($version); |
|
504 |
|
505 if ($type == 'latest') { |
|
506 $version = 'latest'; |
|
507 } |
|
508 |
|
509 $availableVersions = $this->_getAvailableVersions($path, $version); |
|
510 if (empty($availableVersions)) { |
|
511 throw new Zend_Loader_Exception('No valid ZF installations discovered'); |
|
512 } |
|
513 |
|
514 $matchedVersion = array_pop($availableVersions); |
|
515 return $matchedVersion; |
|
516 } |
|
517 |
|
518 /** |
|
519 * Retrieve the ZF version type |
|
520 * |
|
521 * @param string $version |
|
522 * @return string "latest", "major", "minor", or "specific" |
|
523 * @throws Zend_Loader_Exception if version string contains too many dots |
|
524 */ |
|
525 protected function _getVersionType($version) |
|
526 { |
|
527 if (strtolower($version) == 'latest') { |
|
528 return 'latest'; |
|
529 } |
|
530 |
|
531 $parts = explode('.', $version); |
|
532 $count = count($parts); |
|
533 if (1 == $count) { |
|
534 return 'major'; |
|
535 } |
|
536 if (2 == $count) { |
|
537 return 'minor'; |
|
538 } |
|
539 if (3 < $count) { |
|
540 throw new Zend_Loader_Exception('Invalid version string provided'); |
|
541 } |
|
542 return 'specific'; |
|
543 } |
|
544 |
|
545 /** |
|
546 * Get available versions for the version type requested |
|
547 * |
|
548 * @param string $path |
|
549 * @param string $version |
|
550 * @return array |
|
551 */ |
|
552 protected function _getAvailableVersions($path, $version) |
|
553 { |
|
554 if (!is_dir($path)) { |
|
555 throw new Zend_Loader_Exception('Invalid ZF path provided'); |
|
556 } |
|
557 |
|
558 $path = rtrim($path, '/'); |
|
559 $path = rtrim($path, '\\'); |
|
560 $versionLen = strlen($version); |
|
561 $versions = array(); |
|
562 $dirs = glob("$path/*", GLOB_ONLYDIR); |
|
563 foreach ((array) $dirs as $dir) { |
|
564 $dirName = substr($dir, strlen($path) + 1); |
|
565 if (!preg_match('/^(?:ZendFramework-)?(\d+\.\d+\.\d+((a|b|pl|pr|p|rc)\d+)?)(?:-minimal)?$/i', $dirName, $matches)) { |
|
566 continue; |
|
567 } |
|
568 |
|
569 $matchedVersion = $matches[1]; |
|
570 |
|
571 if (('latest' == $version) |
|
572 || ((strlen($matchedVersion) >= $versionLen) |
|
573 && (0 === strpos($matchedVersion, $version))) |
|
574 ) { |
|
575 $versions[$matchedVersion] = $dir . '/library'; |
|
576 } |
|
577 } |
|
578 |
|
579 uksort($versions, 'version_compare'); |
|
580 return $versions; |
|
581 } |
|
582 } |