1: | <?php
|
2: |
|
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: |
|
12: |
|
13: | namespace Composer\Autoload;
|
14: |
|
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | |
40: | |
41: | |
42: |
|
43: | class ClassLoader
|
44: | {
|
45: |
|
46: | private static $includeFile;
|
47: |
|
48: |
|
49: | private $vendorDir;
|
50: |
|
51: |
|
52: | |
53: | |
54: |
|
55: | private $prefixLengthsPsr4 = array();
|
56: | |
57: | |
58: |
|
59: | private $prefixDirsPsr4 = array();
|
60: | |
61: | |
62: |
|
63: | private $fallbackDirsPsr4 = array();
|
64: |
|
65: |
|
66: | |
67: | |
68: | |
69: | |
70: | |
71: | |
72: |
|
73: | private $prefixesPsr0 = array();
|
74: | |
75: | |
76: |
|
77: | private $fallbackDirsPsr0 = array();
|
78: |
|
79: |
|
80: | private $useIncludePath = false;
|
81: |
|
82: | |
83: | |
84: |
|
85: | private $classMap = array();
|
86: |
|
87: |
|
88: | private $classMapAuthoritative = false;
|
89: |
|
90: | |
91: | |
92: |
|
93: | private $missingClasses = array();
|
94: |
|
95: |
|
96: | private $apcuPrefix;
|
97: |
|
98: | |
99: | |
100: |
|
101: | private static $registeredLoaders = array();
|
102: |
|
103: | |
104: | |
105: |
|
106: | public function __construct($vendorDir = null)
|
107: | {
|
108: | $this->vendorDir = $vendorDir;
|
109: | self::initializeIncludeClosure();
|
110: | }
|
111: |
|
112: | |
113: | |
114: |
|
115: | public function getPrefixes()
|
116: | {
|
117: | if (!empty($this->prefixesPsr0)) {
|
118: | return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
119: | }
|
120: |
|
121: | return array();
|
122: | }
|
123: |
|
124: | |
125: | |
126: |
|
127: | public function getPrefixesPsr4()
|
128: | {
|
129: | return $this->prefixDirsPsr4;
|
130: | }
|
131: |
|
132: | |
133: | |
134: |
|
135: | public function getFallbackDirs()
|
136: | {
|
137: | return $this->fallbackDirsPsr0;
|
138: | }
|
139: |
|
140: | |
141: | |
142: |
|
143: | public function getFallbackDirsPsr4()
|
144: | {
|
145: | return $this->fallbackDirsPsr4;
|
146: | }
|
147: |
|
148: | |
149: | |
150: |
|
151: | public function getClassMap()
|
152: | {
|
153: | return $this->classMap;
|
154: | }
|
155: |
|
156: | |
157: | |
158: | |
159: | |
160: |
|
161: | public function addClassMap(array $classMap)
|
162: | {
|
163: | if ($this->classMap) {
|
164: | $this->classMap = array_merge($this->classMap, $classMap);
|
165: | } else {
|
166: | $this->classMap = $classMap;
|
167: | }
|
168: | }
|
169: |
|
170: | |
171: | |
172: | |
173: | |
174: | |
175: | |
176: | |
177: | |
178: | |
179: |
|
180: | public function add($prefix, $paths, $prepend = false)
|
181: | {
|
182: | $paths = (array) $paths;
|
183: | if (!$prefix) {
|
184: | if ($prepend) {
|
185: | $this->fallbackDirsPsr0 = array_merge(
|
186: | $paths,
|
187: | $this->fallbackDirsPsr0
|
188: | );
|
189: | } else {
|
190: | $this->fallbackDirsPsr0 = array_merge(
|
191: | $this->fallbackDirsPsr0,
|
192: | $paths
|
193: | );
|
194: | }
|
195: |
|
196: | return;
|
197: | }
|
198: |
|
199: | $first = $prefix[0];
|
200: | if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
201: | $this->prefixesPsr0[$first][$prefix] = $paths;
|
202: |
|
203: | return;
|
204: | }
|
205: | if ($prepend) {
|
206: | $this->prefixesPsr0[$first][$prefix] = array_merge(
|
207: | $paths,
|
208: | $this->prefixesPsr0[$first][$prefix]
|
209: | );
|
210: | } else {
|
211: | $this->prefixesPsr0[$first][$prefix] = array_merge(
|
212: | $this->prefixesPsr0[$first][$prefix],
|
213: | $paths
|
214: | );
|
215: | }
|
216: | }
|
217: |
|
218: | |
219: | |
220: | |
221: | |
222: | |
223: | |
224: | |
225: | |
226: | |
227: | |
228: | |
229: |
|
230: | public function addPsr4($prefix, $paths, $prepend = false)
|
231: | {
|
232: | $paths = (array) $paths;
|
233: | if (!$prefix) {
|
234: |
|
235: | if ($prepend) {
|
236: | $this->fallbackDirsPsr4 = array_merge(
|
237: | $paths,
|
238: | $this->fallbackDirsPsr4
|
239: | );
|
240: | } else {
|
241: | $this->fallbackDirsPsr4 = array_merge(
|
242: | $this->fallbackDirsPsr4,
|
243: | $paths
|
244: | );
|
245: | }
|
246: | } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
247: |
|
248: | $length = strlen($prefix);
|
249: | if ('\\' !== $prefix[$length - 1]) {
|
250: | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
251: | }
|
252: | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
253: | $this->prefixDirsPsr4[$prefix] = $paths;
|
254: | } elseif ($prepend) {
|
255: |
|
256: | $this->prefixDirsPsr4[$prefix] = array_merge(
|
257: | $paths,
|
258: | $this->prefixDirsPsr4[$prefix]
|
259: | );
|
260: | } else {
|
261: |
|
262: | $this->prefixDirsPsr4[$prefix] = array_merge(
|
263: | $this->prefixDirsPsr4[$prefix],
|
264: | $paths
|
265: | );
|
266: | }
|
267: | }
|
268: |
|
269: | |
270: | |
271: | |
272: | |
273: | |
274: | |
275: | |
276: | |
277: |
|
278: | public function set($prefix, $paths)
|
279: | {
|
280: | if (!$prefix) {
|
281: | $this->fallbackDirsPsr0 = (array) $paths;
|
282: | } else {
|
283: | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
284: | }
|
285: | }
|
286: |
|
287: | |
288: | |
289: | |
290: | |
291: | |
292: | |
293: | |
294: | |
295: | |
296: | |
297: |
|
298: | public function setPsr4($prefix, $paths)
|
299: | {
|
300: | if (!$prefix) {
|
301: | $this->fallbackDirsPsr4 = (array) $paths;
|
302: | } else {
|
303: | $length = strlen($prefix);
|
304: | if ('\\' !== $prefix[$length - 1]) {
|
305: | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
306: | }
|
307: | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
308: | $this->prefixDirsPsr4[$prefix] = (array) $paths;
|
309: | }
|
310: | }
|
311: |
|
312: | |
313: | |
314: | |
315: | |
316: | |
317: | |
318: |
|
319: | public function setUseIncludePath($useIncludePath)
|
320: | {
|
321: | $this->useIncludePath = $useIncludePath;
|
322: | }
|
323: |
|
324: | |
325: | |
326: | |
327: | |
328: | |
329: |
|
330: | public function getUseIncludePath()
|
331: | {
|
332: | return $this->useIncludePath;
|
333: | }
|
334: |
|
335: | |
336: | |
337: | |
338: | |
339: | |
340: | |
341: | |
342: |
|
343: | public function setClassMapAuthoritative($classMapAuthoritative)
|
344: | {
|
345: | $this->classMapAuthoritative = $classMapAuthoritative;
|
346: | }
|
347: |
|
348: | |
349: | |
350: | |
351: | |
352: |
|
353: | public function isClassMapAuthoritative()
|
354: | {
|
355: | return $this->classMapAuthoritative;
|
356: | }
|
357: |
|
358: | |
359: | |
360: | |
361: | |
362: | |
363: | |
364: |
|
365: | public function setApcuPrefix($apcuPrefix)
|
366: | {
|
367: | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
368: | }
|
369: |
|
370: | |
371: | |
372: | |
373: | |
374: |
|
375: | public function getApcuPrefix()
|
376: | {
|
377: | return $this->apcuPrefix;
|
378: | }
|
379: |
|
380: | |
381: | |
382: | |
383: | |
384: | |
385: | |
386: |
|
387: | public function register($prepend = false)
|
388: | {
|
389: | spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
390: |
|
391: | if (null === $this->vendorDir) {
|
392: | return;
|
393: | }
|
394: |
|
395: | if ($prepend) {
|
396: | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
397: | } else {
|
398: | unset(self::$registeredLoaders[$this->vendorDir]);
|
399: | self::$registeredLoaders[$this->vendorDir] = $this;
|
400: | }
|
401: | }
|
402: |
|
403: | |
404: | |
405: | |
406: | |
407: |
|
408: | public function unregister()
|
409: | {
|
410: | spl_autoload_unregister(array($this, 'loadClass'));
|
411: |
|
412: | if (null !== $this->vendorDir) {
|
413: | unset(self::$registeredLoaders[$this->vendorDir]);
|
414: | }
|
415: | }
|
416: |
|
417: | |
418: | |
419: | |
420: | |
421: | |
422: |
|
423: | public function loadClass($class)
|
424: | {
|
425: | if ($file = $this->findFile($class)) {
|
426: | $includeFile = self::$includeFile;
|
427: | $includeFile($file);
|
428: |
|
429: | return true;
|
430: | }
|
431: |
|
432: | return null;
|
433: | }
|
434: |
|
435: | |
436: | |
437: | |
438: | |
439: | |
440: | |
441: |
|
442: | public function findFile($class)
|
443: | {
|
444: |
|
445: | if (isset($this->classMap[$class])) {
|
446: | return $this->classMap[$class];
|
447: | }
|
448: | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
449: | return false;
|
450: | }
|
451: | if (null !== $this->apcuPrefix) {
|
452: | $file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
453: | if ($hit) {
|
454: | return $file;
|
455: | }
|
456: | }
|
457: |
|
458: | $file = $this->findFileWithExtension($class, '.php');
|
459: |
|
460: |
|
461: | if (false === $file && defined('HHVM_VERSION')) {
|
462: | $file = $this->findFileWithExtension($class, '.hh');
|
463: | }
|
464: |
|
465: | if (null !== $this->apcuPrefix) {
|
466: | apcu_add($this->apcuPrefix.$class, $file);
|
467: | }
|
468: |
|
469: | if (false === $file) {
|
470: |
|
471: | $this->missingClasses[$class] = true;
|
472: | }
|
473: |
|
474: | return $file;
|
475: | }
|
476: |
|
477: | |
478: | |
479: | |
480: | |
481: |
|
482: | public static function getRegisteredLoaders()
|
483: | {
|
484: | return self::$registeredLoaders;
|
485: | }
|
486: |
|
487: | |
488: | |
489: | |
490: | |
491: |
|
492: | private function findFileWithExtension($class, $ext)
|
493: | {
|
494: |
|
495: | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
496: |
|
497: | $first = $class[0];
|
498: | if (isset($this->prefixLengthsPsr4[$first])) {
|
499: | $subPath = $class;
|
500: | while (false !== $lastPos = strrpos($subPath, '\\')) {
|
501: | $subPath = substr($subPath, 0, $lastPos);
|
502: | $search = $subPath . '\\';
|
503: | if (isset($this->prefixDirsPsr4[$search])) {
|
504: | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
505: | foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
506: | if (file_exists($file = $dir . $pathEnd)) {
|
507: | return $file;
|
508: | }
|
509: | }
|
510: | }
|
511: | }
|
512: | }
|
513: |
|
514: |
|
515: | foreach ($this->fallbackDirsPsr4 as $dir) {
|
516: | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
517: | return $file;
|
518: | }
|
519: | }
|
520: |
|
521: |
|
522: | if (false !== $pos = strrpos($class, '\\')) {
|
523: |
|
524: | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
525: | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
526: | } else {
|
527: |
|
528: | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
529: | }
|
530: |
|
531: | if (isset($this->prefixesPsr0[$first])) {
|
532: | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
533: | if (0 === strpos($class, $prefix)) {
|
534: | foreach ($dirs as $dir) {
|
535: | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
536: | return $file;
|
537: | }
|
538: | }
|
539: | }
|
540: | }
|
541: | }
|
542: |
|
543: |
|
544: | foreach ($this->fallbackDirsPsr0 as $dir) {
|
545: | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
546: | return $file;
|
547: | }
|
548: | }
|
549: |
|
550: |
|
551: | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
552: | return $file;
|
553: | }
|
554: |
|
555: | return false;
|
556: | }
|
557: |
|
558: | |
559: | |
560: |
|
561: | private static function initializeIncludeClosure()
|
562: | {
|
563: | if (self::$includeFile !== null) {
|
564: | return;
|
565: | }
|
566: |
|
567: | |
568: | |
569: | |
570: | |
571: | |
572: | |
573: | |
574: |
|
575: | self::$includeFile = \Closure::bind(static function($file) {
|
576: | include $file;
|
577: | }, null, null);
|
578: | }
|
579: | }
|
580: | |