Mercurial > hg > rc1
comparison vendor/composer/ClassLoader.php @ 0:1e000243b222
vanilla 1.3.3 distro, I hope
author | Charlie Root |
---|---|
date | Thu, 04 Jan 2018 15:50:29 -0500 |
parents | |
children | 771f6803cc4b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1e000243b222 |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of Composer. | |
5 * | |
6 * (c) Nils Adermann <naderman@naderman.de> | |
7 * Jordi Boggiano <j.boggiano@seld.be> | |
8 * | |
9 * For the full copyright and license information, please view the LICENSE | |
10 * file that was distributed with this source code. | |
11 */ | |
12 | |
13 namespace Composer\Autoload; | |
14 | |
15 /** | |
16 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. | |
17 * | |
18 * $loader = new \Composer\Autoload\ClassLoader(); | |
19 * | |
20 * // register classes with namespaces | |
21 * $loader->add('Symfony\Component', __DIR__.'/component'); | |
22 * $loader->add('Symfony', __DIR__.'/framework'); | |
23 * | |
24 * // activate the autoloader | |
25 * $loader->register(); | |
26 * | |
27 * // to enable searching the include path (eg. for PEAR packages) | |
28 * $loader->setUseIncludePath(true); | |
29 * | |
30 * In this example, if you try to use a class in the Symfony\Component | |
31 * namespace or one of its children (Symfony\Component\Console for instance), | |
32 * the autoloader will first look for the class under the component/ | |
33 * directory, and it will then fallback to the framework/ directory if not | |
34 * found before giving up. | |
35 * | |
36 * This class is loosely based on the Symfony UniversalClassLoader. | |
37 * | |
38 * @author Fabien Potencier <fabien@symfony.com> | |
39 * @author Jordi Boggiano <j.boggiano@seld.be> | |
40 * @see http://www.php-fig.org/psr/psr-0/ | |
41 * @see http://www.php-fig.org/psr/psr-4/ | |
42 */ | |
43 class ClassLoader | |
44 { | |
45 // PSR-4 | |
46 private $prefixLengthsPsr4 = array(); | |
47 private $prefixDirsPsr4 = array(); | |
48 private $fallbackDirsPsr4 = array(); | |
49 | |
50 // PSR-0 | |
51 private $prefixesPsr0 = array(); | |
52 private $fallbackDirsPsr0 = array(); | |
53 | |
54 private $useIncludePath = false; | |
55 private $classMap = array(); | |
56 private $classMapAuthoritative = false; | |
57 private $missingClasses = array(); | |
58 private $apcuPrefix; | |
59 | |
60 public function getPrefixes() | |
61 { | |
62 if (!empty($this->prefixesPsr0)) { | |
63 return call_user_func_array('array_merge', $this->prefixesPsr0); | |
64 } | |
65 | |
66 return array(); | |
67 } | |
68 | |
69 public function getPrefixesPsr4() | |
70 { | |
71 return $this->prefixDirsPsr4; | |
72 } | |
73 | |
74 public function getFallbackDirs() | |
75 { | |
76 return $this->fallbackDirsPsr0; | |
77 } | |
78 | |
79 public function getFallbackDirsPsr4() | |
80 { | |
81 return $this->fallbackDirsPsr4; | |
82 } | |
83 | |
84 public function getClassMap() | |
85 { | |
86 return $this->classMap; | |
87 } | |
88 | |
89 /** | |
90 * @param array $classMap Class to filename map | |
91 */ | |
92 public function addClassMap(array $classMap) | |
93 { | |
94 if ($this->classMap) { | |
95 $this->classMap = array_merge($this->classMap, $classMap); | |
96 } else { | |
97 $this->classMap = $classMap; | |
98 } | |
99 } | |
100 | |
101 /** | |
102 * Registers a set of PSR-0 directories for a given prefix, either | |
103 * appending or prepending to the ones previously set for this prefix. | |
104 * | |
105 * @param string $prefix The prefix | |
106 * @param array|string $paths The PSR-0 root directories | |
107 * @param bool $prepend Whether to prepend the directories | |
108 */ | |
109 public function add($prefix, $paths, $prepend = false) | |
110 { | |
111 if (!$prefix) { | |
112 if ($prepend) { | |
113 $this->fallbackDirsPsr0 = array_merge( | |
114 (array) $paths, | |
115 $this->fallbackDirsPsr0 | |
116 ); | |
117 } else { | |
118 $this->fallbackDirsPsr0 = array_merge( | |
119 $this->fallbackDirsPsr0, | |
120 (array) $paths | |
121 ); | |
122 } | |
123 | |
124 return; | |
125 } | |
126 | |
127 $first = $prefix[0]; | |
128 if (!isset($this->prefixesPsr0[$first][$prefix])) { | |
129 $this->prefixesPsr0[$first][$prefix] = (array) $paths; | |
130 | |
131 return; | |
132 } | |
133 if ($prepend) { | |
134 $this->prefixesPsr0[$first][$prefix] = array_merge( | |
135 (array) $paths, | |
136 $this->prefixesPsr0[$first][$prefix] | |
137 ); | |
138 } else { | |
139 $this->prefixesPsr0[$first][$prefix] = array_merge( | |
140 $this->prefixesPsr0[$first][$prefix], | |
141 (array) $paths | |
142 ); | |
143 } | |
144 } | |
145 | |
146 /** | |
147 * Registers a set of PSR-4 directories for a given namespace, either | |
148 * appending or prepending to the ones previously set for this namespace. | |
149 * | |
150 * @param string $prefix The prefix/namespace, with trailing '\\' | |
151 * @param array|string $paths The PSR-4 base directories | |
152 * @param bool $prepend Whether to prepend the directories | |
153 * | |
154 * @throws \InvalidArgumentException | |
155 */ | |
156 public function addPsr4($prefix, $paths, $prepend = false) | |
157 { | |
158 if (!$prefix) { | |
159 // Register directories for the root namespace. | |
160 if ($prepend) { | |
161 $this->fallbackDirsPsr4 = array_merge( | |
162 (array) $paths, | |
163 $this->fallbackDirsPsr4 | |
164 ); | |
165 } else { | |
166 $this->fallbackDirsPsr4 = array_merge( | |
167 $this->fallbackDirsPsr4, | |
168 (array) $paths | |
169 ); | |
170 } | |
171 } elseif (!isset($this->prefixDirsPsr4[$prefix])) { | |
172 // Register directories for a new namespace. | |
173 $length = strlen($prefix); | |
174 if ('\\' !== $prefix[$length - 1]) { | |
175 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); | |
176 } | |
177 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; | |
178 $this->prefixDirsPsr4[$prefix] = (array) $paths; | |
179 } elseif ($prepend) { | |
180 // Prepend directories for an already registered namespace. | |
181 $this->prefixDirsPsr4[$prefix] = array_merge( | |
182 (array) $paths, | |
183 $this->prefixDirsPsr4[$prefix] | |
184 ); | |
185 } else { | |
186 // Append directories for an already registered namespace. | |
187 $this->prefixDirsPsr4[$prefix] = array_merge( | |
188 $this->prefixDirsPsr4[$prefix], | |
189 (array) $paths | |
190 ); | |
191 } | |
192 } | |
193 | |
194 /** | |
195 * Registers a set of PSR-0 directories for a given prefix, | |
196 * replacing any others previously set for this prefix. | |
197 * | |
198 * @param string $prefix The prefix | |
199 * @param array|string $paths The PSR-0 base directories | |
200 */ | |
201 public function set($prefix, $paths) | |
202 { | |
203 if (!$prefix) { | |
204 $this->fallbackDirsPsr0 = (array) $paths; | |
205 } else { | |
206 $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; | |
207 } | |
208 } | |
209 | |
210 /** | |
211 * Registers a set of PSR-4 directories for a given namespace, | |
212 * replacing any others previously set for this namespace. | |
213 * | |
214 * @param string $prefix The prefix/namespace, with trailing '\\' | |
215 * @param array|string $paths The PSR-4 base directories | |
216 * | |
217 * @throws \InvalidArgumentException | |
218 */ | |
219 public function setPsr4($prefix, $paths) | |
220 { | |
221 if (!$prefix) { | |
222 $this->fallbackDirsPsr4 = (array) $paths; | |
223 } else { | |
224 $length = strlen($prefix); | |
225 if ('\\' !== $prefix[$length - 1]) { | |
226 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); | |
227 } | |
228 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; | |
229 $this->prefixDirsPsr4[$prefix] = (array) $paths; | |
230 } | |
231 } | |
232 | |
233 /** | |
234 * Turns on searching the include path for class files. | |
235 * | |
236 * @param bool $useIncludePath | |
237 */ | |
238 public function setUseIncludePath($useIncludePath) | |
239 { | |
240 $this->useIncludePath = $useIncludePath; | |
241 } | |
242 | |
243 /** | |
244 * Can be used to check if the autoloader uses the include path to check | |
245 * for classes. | |
246 * | |
247 * @return bool | |
248 */ | |
249 public function getUseIncludePath() | |
250 { | |
251 return $this->useIncludePath; | |
252 } | |
253 | |
254 /** | |
255 * Turns off searching the prefix and fallback directories for classes | |
256 * that have not been registered with the class map. | |
257 * | |
258 * @param bool $classMapAuthoritative | |
259 */ | |
260 public function setClassMapAuthoritative($classMapAuthoritative) | |
261 { | |
262 $this->classMapAuthoritative = $classMapAuthoritative; | |
263 } | |
264 | |
265 /** | |
266 * Should class lookup fail if not found in the current class map? | |
267 * | |
268 * @return bool | |
269 */ | |
270 public function isClassMapAuthoritative() | |
271 { | |
272 return $this->classMapAuthoritative; | |
273 } | |
274 | |
275 /** | |
276 * APCu prefix to use to cache found/not-found classes, if the extension is enabled. | |
277 * | |
278 * @param string|null $apcuPrefix | |
279 */ | |
280 public function setApcuPrefix($apcuPrefix) | |
281 { | |
282 $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; | |
283 } | |
284 | |
285 /** | |
286 * The APCu prefix in use, or null if APCu caching is not enabled. | |
287 * | |
288 * @return string|null | |
289 */ | |
290 public function getApcuPrefix() | |
291 { | |
292 return $this->apcuPrefix; | |
293 } | |
294 | |
295 /** | |
296 * Registers this instance as an autoloader. | |
297 * | |
298 * @param bool $prepend Whether to prepend the autoloader or not | |
299 */ | |
300 public function register($prepend = false) | |
301 { | |
302 spl_autoload_register(array($this, 'loadClass'), true, $prepend); | |
303 } | |
304 | |
305 /** | |
306 * Unregisters this instance as an autoloader. | |
307 */ | |
308 public function unregister() | |
309 { | |
310 spl_autoload_unregister(array($this, 'loadClass')); | |
311 } | |
312 | |
313 /** | |
314 * Loads the given class or interface. | |
315 * | |
316 * @param string $class The name of the class | |
317 * @return bool|null True if loaded, null otherwise | |
318 */ | |
319 public function loadClass($class) | |
320 { | |
321 if ($file = $this->findFile($class)) { | |
322 includeFile($file); | |
323 | |
324 return true; | |
325 } | |
326 } | |
327 | |
328 /** | |
329 * Finds the path to the file where the class is defined. | |
330 * | |
331 * @param string $class The name of the class | |
332 * | |
333 * @return string|false The path if found, false otherwise | |
334 */ | |
335 public function findFile($class) | |
336 { | |
337 // class map lookup | |
338 if (isset($this->classMap[$class])) { | |
339 return $this->classMap[$class]; | |
340 } | |
341 if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { | |
342 return false; | |
343 } | |
344 if (null !== $this->apcuPrefix) { | |
345 $file = apcu_fetch($this->apcuPrefix.$class, $hit); | |
346 if ($hit) { | |
347 return $file; | |
348 } | |
349 } | |
350 | |
351 $file = $this->findFileWithExtension($class, '.php'); | |
352 | |
353 // Search for Hack files if we are running on HHVM | |
354 if (false === $file && defined('HHVM_VERSION')) { | |
355 $file = $this->findFileWithExtension($class, '.hh'); | |
356 } | |
357 | |
358 if (null !== $this->apcuPrefix) { | |
359 apcu_add($this->apcuPrefix.$class, $file); | |
360 } | |
361 | |
362 if (false === $file) { | |
363 // Remember that this class does not exist. | |
364 $this->missingClasses[$class] = true; | |
365 } | |
366 | |
367 return $file; | |
368 } | |
369 | |
370 private function findFileWithExtension($class, $ext) | |
371 { | |
372 // PSR-4 lookup | |
373 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; | |
374 | |
375 $first = $class[0]; | |
376 if (isset($this->prefixLengthsPsr4[$first])) { | |
377 $subPath = $class; | |
378 while (false !== $lastPos = strrpos($subPath, '\\')) { | |
379 $subPath = substr($subPath, 0, $lastPos); | |
380 $search = $subPath.'\\'; | |
381 if (isset($this->prefixDirsPsr4[$search])) { | |
382 foreach ($this->prefixDirsPsr4[$search] as $dir) { | |
383 $length = $this->prefixLengthsPsr4[$first][$search]; | |
384 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { | |
385 return $file; | |
386 } | |
387 } | |
388 } | |
389 } | |
390 } | |
391 | |
392 // PSR-4 fallback dirs | |
393 foreach ($this->fallbackDirsPsr4 as $dir) { | |
394 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { | |
395 return $file; | |
396 } | |
397 } | |
398 | |
399 // PSR-0 lookup | |
400 if (false !== $pos = strrpos($class, '\\')) { | |
401 // namespaced class name | |
402 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) | |
403 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); | |
404 } else { | |
405 // PEAR-like class name | |
406 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; | |
407 } | |
408 | |
409 if (isset($this->prefixesPsr0[$first])) { | |
410 foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { | |
411 if (0 === strpos($class, $prefix)) { | |
412 foreach ($dirs as $dir) { | |
413 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { | |
414 return $file; | |
415 } | |
416 } | |
417 } | |
418 } | |
419 } | |
420 | |
421 // PSR-0 fallback dirs | |
422 foreach ($this->fallbackDirsPsr0 as $dir) { | |
423 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { | |
424 return $file; | |
425 } | |
426 } | |
427 | |
428 // PSR-0 include paths. | |
429 if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { | |
430 return $file; | |
431 } | |
432 | |
433 return false; | |
434 } | |
435 } | |
436 | |
437 /** | |
438 * Scope isolated include. | |
439 * | |
440 * Prevents access to $this/self from included files. | |
441 */ | |
442 function includeFile($file) | |
443 { | |
444 include $file; | |
445 } |