Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/debug/DebugClassLoader.php @ 17:129ea1e6d783
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:21:36 +0000 |
parents | 5fb285c0d0e3 |
children | af1871eacc83 |
comparison
equal
deleted
inserted
replaced
16:c2387f117808 | 17:129ea1e6d783 |
---|---|
24 */ | 24 */ |
25 class DebugClassLoader | 25 class DebugClassLoader |
26 { | 26 { |
27 private $classLoader; | 27 private $classLoader; |
28 private $isFinder; | 28 private $isFinder; |
29 private $loaded = array(); | 29 private $loaded = []; |
30 private static $caseCheck; | 30 private static $caseCheck; |
31 private static $checkedClasses = array(); | 31 private static $checkedClasses = []; |
32 private static $final = array(); | 32 private static $final = []; |
33 private static $finalMethods = array(); | 33 private static $finalMethods = []; |
34 private static $deprecated = array(); | 34 private static $deprecated = []; |
35 private static $internal = array(); | 35 private static $internal = []; |
36 private static $internalMethods = array(); | 36 private static $internalMethods = []; |
37 private static $php7Reserved = array('int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1); | 37 private static $php7Reserved = ['int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1]; |
38 private static $darwinCache = array('/' => array('/', array())); | 38 private static $darwinCache = ['/' => ['/', []]]; |
39 | 39 |
40 public function __construct(callable $classLoader) | 40 public function __construct(callable $classLoader) |
41 { | 41 { |
42 $this->classLoader = $classLoader; | 42 $this->classLoader = $classLoader; |
43 $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile'); | 43 $this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile'); |
44 | 44 |
45 if (!isset(self::$caseCheck)) { | 45 if (!isset(self::$caseCheck)) { |
46 $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR); | 46 $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR); |
47 $i = strrpos($file, DIRECTORY_SEPARATOR); | 47 $i = strrpos($file, \DIRECTORY_SEPARATOR); |
48 $dir = substr($file, 0, 1 + $i); | 48 $dir = substr($file, 0, 1 + $i); |
49 $file = substr($file, 1 + $i); | 49 $file = substr($file, 1 + $i); |
50 $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); | 50 $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); |
51 $test = realpath($dir.$test); | 51 $test = realpath($dir.$test); |
52 | 52 |
53 if (false === $test || false === $i) { | 53 if (false === $test || false === $i) { |
54 // filesystem is case sensitive | 54 // filesystem is case sensitive |
55 self::$caseCheck = 0; | 55 self::$caseCheck = 0; |
56 } elseif (substr($test, -strlen($file)) === $file) { | 56 } elseif (substr($test, -\strlen($file)) === $file) { |
57 // filesystem is case insensitive and realpath() normalizes the case of characters | 57 // filesystem is case insensitive and realpath() normalizes the case of characters |
58 self::$caseCheck = 1; | 58 self::$caseCheck = 1; |
59 } elseif (false !== stripos(PHP_OS, 'darwin')) { | 59 } elseif (false !== stripos(PHP_OS, 'darwin')) { |
60 // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters | 60 // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters |
61 self::$caseCheck = 2; | 61 self::$caseCheck = 2; |
83 { | 83 { |
84 // Ensures we don't hit https://bugs.php.net/42098 | 84 // Ensures we don't hit https://bugs.php.net/42098 |
85 class_exists('Symfony\Component\Debug\ErrorHandler'); | 85 class_exists('Symfony\Component\Debug\ErrorHandler'); |
86 class_exists('Psr\Log\LogLevel'); | 86 class_exists('Psr\Log\LogLevel'); |
87 | 87 |
88 if (!is_array($functions = spl_autoload_functions())) { | 88 if (!\is_array($functions = spl_autoload_functions())) { |
89 return; | 89 return; |
90 } | 90 } |
91 | 91 |
92 foreach ($functions as $function) { | 92 foreach ($functions as $function) { |
93 spl_autoload_unregister($function); | 93 spl_autoload_unregister($function); |
94 } | 94 } |
95 | 95 |
96 foreach ($functions as $function) { | 96 foreach ($functions as $function) { |
97 if (!is_array($function) || !$function[0] instanceof self) { | 97 if (!\is_array($function) || !$function[0] instanceof self) { |
98 $function = array(new static($function), 'loadClass'); | 98 $function = [new static($function), 'loadClass']; |
99 } | 99 } |
100 | 100 |
101 spl_autoload_register($function); | 101 spl_autoload_register($function); |
102 } | 102 } |
103 } | 103 } |
105 /** | 105 /** |
106 * Disables the wrapping. | 106 * Disables the wrapping. |
107 */ | 107 */ |
108 public static function disable() | 108 public static function disable() |
109 { | 109 { |
110 if (!is_array($functions = spl_autoload_functions())) { | 110 if (!\is_array($functions = spl_autoload_functions())) { |
111 return; | 111 return; |
112 } | 112 } |
113 | 113 |
114 foreach ($functions as $function) { | 114 foreach ($functions as $function) { |
115 spl_autoload_unregister($function); | 115 spl_autoload_unregister($function); |
116 } | 116 } |
117 | 117 |
118 foreach ($functions as $function) { | 118 foreach ($functions as $function) { |
119 if (is_array($function) && $function[0] instanceof self) { | 119 if (\is_array($function) && $function[0] instanceof self) { |
120 $function = $function[0]->getClassLoader(); | 120 $function = $function[0]->getClassLoader(); |
121 } | 121 } |
122 | 122 |
123 spl_autoload_register($function); | 123 spl_autoload_register($function); |
124 } | 124 } |
125 } | |
126 | |
127 /** | |
128 * @return string|null | |
129 */ | |
130 public function findFile($class) | |
131 { | |
132 return $this->isFinder ? $this->classLoader[0]->findFile($class) ?: null : null; | |
125 } | 133 } |
126 | 134 |
127 /** | 135 /** |
128 * Loads the given class or interface. | 136 * Loads the given class or interface. |
129 * | 137 * |
130 * @param string $class The name of the class | 138 * @param string $class The name of the class |
131 * | 139 * |
132 * @return bool|null True, if loaded | |
133 * | |
134 * @throws \RuntimeException | 140 * @throws \RuntimeException |
135 */ | 141 */ |
136 public function loadClass($class) | 142 public function loadClass($class) |
137 { | 143 { |
138 $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); | 144 $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); |
139 | 145 |
140 try { | 146 try { |
141 if ($this->isFinder && !isset($this->loaded[$class])) { | 147 if ($this->isFinder && !isset($this->loaded[$class])) { |
142 $this->loaded[$class] = true; | 148 $this->loaded[$class] = true; |
143 if ($file = $this->classLoader[0]->findFile($class) ?: false) { | 149 if (!$file = $this->classLoader[0]->findFile($class) ?: false) { |
144 $wasCached = \function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file); | 150 // no-op |
145 | 151 } elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) { |
146 require $file; | 152 require $file; |
147 | 153 |
148 if ($wasCached) { | 154 return; |
149 return; | 155 } else { |
150 } | 156 require $file; |
151 } | 157 } |
152 } else { | 158 } else { |
153 call_user_func($this->classLoader, $class); | 159 \call_user_func($this->classLoader, $class); |
154 $file = false; | 160 $file = false; |
155 } | 161 } |
156 } finally { | 162 } finally { |
157 error_reporting($e); | 163 error_reporting($e); |
158 } | 164 } |
182 | 188 |
183 if ($name !== $class && 0 === \strcasecmp($name, $class)) { | 189 if ($name !== $class && 0 === \strcasecmp($name, $class)) { |
184 throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); | 190 throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); |
185 } | 191 } |
186 | 192 |
187 // Don't trigger deprecations for classes in the same vendor | 193 $deprecations = $this->checkAnnotations($refl, $name); |
188 if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) { | 194 |
189 $len = 0; | 195 if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) { |
190 $ns = ''; | 196 $deprecations[] = sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()); |
197 } | |
198 | |
199 foreach ($deprecations as $message) { | |
200 @trigger_error($message, E_USER_DEPRECATED); | |
201 } | |
202 } | |
203 | |
204 if (!$file) { | |
205 return; | |
206 } | |
207 | |
208 if (!$exists) { | |
209 if (false !== strpos($class, '/')) { | |
210 throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); | |
211 } | |
212 | |
213 throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); | |
214 } | |
215 | |
216 if (self::$caseCheck && $message = $this->checkCase($refl, $file, $class)) { | |
217 throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2])); | |
218 } | |
219 } | |
220 | |
221 public function checkAnnotations(\ReflectionClass $refl, $class) | |
222 { | |
223 $deprecations = []; | |
224 | |
225 // Don't trigger deprecations for classes in the same vendor | |
226 if (2 > $len = 1 + (\strpos($class, '\\') ?: \strpos($class, '_'))) { | |
227 $len = 0; | |
228 $ns = ''; | |
229 } else { | |
230 $ns = \str_replace('_', '\\', \substr($class, 0, $len)); | |
231 } | |
232 | |
233 // Detect annotations on the class | |
234 if (false !== $doc = $refl->getDocComment()) { | |
235 foreach (['final', 'deprecated', 'internal'] as $annotation) { | |
236 if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) { | |
237 self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; | |
238 } | |
239 } | |
240 } | |
241 | |
242 $parent = \get_parent_class($class); | |
243 $parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent); | |
244 if ($parent) { | |
245 $parentAndOwnInterfaces[$parent] = $parent; | |
246 | |
247 if (!isset(self::$checkedClasses[$parent])) { | |
248 $this->checkClass($parent); | |
249 } | |
250 | |
251 if (isset(self::$final[$parent])) { | |
252 $deprecations[] = sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $class); | |
253 } | |
254 } | |
255 | |
256 // Detect if the parent is annotated | |
257 foreach ($parentAndOwnInterfaces + \class_uses($class, false) as $use) { | |
258 if (!isset(self::$checkedClasses[$use])) { | |
259 $this->checkClass($use); | |
260 } | |
261 if (isset(self::$deprecated[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len)) { | |
262 $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); | |
263 $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); | |
264 | |
265 $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]); | |
266 } | |
267 if (isset(self::$internal[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len)) { | |
268 $deprecations[] = sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $class); | |
269 } | |
270 } | |
271 | |
272 if (\trait_exists($class)) { | |
273 return $deprecations; | |
274 } | |
275 | |
276 // Inherit @final and @internal annotations for methods | |
277 self::$finalMethods[$class] = []; | |
278 self::$internalMethods[$class] = []; | |
279 foreach ($parentAndOwnInterfaces as $use) { | |
280 foreach (['finalMethods', 'internalMethods'] as $property) { | |
281 if (isset(self::${$property}[$use])) { | |
282 self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use]; | |
283 } | |
284 } | |
285 } | |
286 | |
287 foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { | |
288 if ($method->class !== $class) { | |
289 continue; | |
290 } | |
291 | |
292 if ($parent && isset(self::$finalMethods[$parent][$method->name])) { | |
293 list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; | |
294 $deprecations[] = sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $class); | |
295 } | |
296 | |
297 if (isset(self::$internalMethods[$class][$method->name])) { | |
298 list($declaringClass, $message) = self::$internalMethods[$class][$method->name]; | |
299 if (\strncmp($ns, $declaringClass, $len)) { | |
300 $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $class); | |
301 } | |
302 } | |
303 | |
304 // Detect method annotations | |
305 if (false === $doc = $method->getDocComment()) { | |
306 continue; | |
307 } | |
308 | |
309 foreach (['final', 'internal'] as $annotation) { | |
310 if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) { | |
311 $message = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; | |
312 self::${$annotation.'Methods'}[$class][$method->name] = [$class, $message]; | |
313 } | |
314 } | |
315 } | |
316 | |
317 return $deprecations; | |
318 } | |
319 | |
320 public function checkCase(\ReflectionClass $refl, $file, $class) | |
321 { | |
322 $real = explode('\\', $class.strrchr($file, '.')); | |
323 $tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file)); | |
324 | |
325 $i = \count($tail) - 1; | |
326 $j = \count($real) - 1; | |
327 | |
328 while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { | |
329 --$i; | |
330 --$j; | |
331 } | |
332 | |
333 array_splice($tail, 0, $i + 1); | |
334 | |
335 if (!$tail) { | |
336 return; | |
337 } | |
338 | |
339 $tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail); | |
340 $tailLen = \strlen($tail); | |
341 $real = $refl->getFileName(); | |
342 | |
343 if (2 === self::$caseCheck) { | |
344 $real = $this->darwinRealpath($real); | |
345 } | |
346 | |
347 if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) | |
348 && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) | |
349 ) { | |
350 return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)]; | |
351 } | |
352 } | |
353 | |
354 /** | |
355 * `realpath` on MacOSX doesn't normalize the case of characters. | |
356 */ | |
357 private function darwinRealpath($real) | |
358 { | |
359 $i = 1 + strrpos($real, '/'); | |
360 $file = substr($real, $i); | |
361 $real = substr($real, 0, $i); | |
362 | |
363 if (isset(self::$darwinCache[$real])) { | |
364 $kDir = $real; | |
365 } else { | |
366 $kDir = strtolower($real); | |
367 | |
368 if (isset(self::$darwinCache[$kDir])) { | |
369 $real = self::$darwinCache[$kDir][0]; | |
191 } else { | 370 } else { |
192 $ns = \substr($name, 0, $len); | 371 $dir = getcwd(); |
193 } | 372 chdir($real); |
194 | 373 $real = getcwd().'/'; |
195 // Detect annotations on the class | 374 chdir($dir); |
196 if (false !== $doc = $refl->getDocComment()) { | 375 |
197 foreach (array('final', 'deprecated', 'internal') as $annotation) { | 376 $dir = $real; |
198 if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) { | 377 $k = $kDir; |
199 self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; | 378 $i = \strlen($dir) - 1; |
379 while (!isset(self::$darwinCache[$k])) { | |
380 self::$darwinCache[$k] = [$dir, []]; | |
381 self::$darwinCache[$dir] = &self::$darwinCache[$k]; | |
382 | |
383 while ('/' !== $dir[--$i]) { | |
200 } | 384 } |
201 } | 385 $k = substr($k, 0, ++$i); |
202 } | 386 $dir = substr($dir, 0, $i--); |
203 | 387 } |
204 $parentAndTraits = \class_uses($name, false); | 388 } |
205 if ($parent = \get_parent_class($class)) { | 389 } |
206 $parentAndTraits[] = $parent; | 390 |
207 | 391 $dirFiles = self::$darwinCache[$kDir][1]; |
208 if (!isset(self::$checkedClasses[$parent])) { | 392 |
209 $this->checkClass($parent); | 393 if (isset($dirFiles[$file])) { |
210 } | 394 return $real .= $dirFiles[$file]; |
211 | 395 } |
212 if (isset(self::$final[$parent])) { | 396 |
213 @trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED); | 397 $kFile = strtolower($file); |
214 } | 398 |
215 } | 399 if (!isset($dirFiles[$kFile])) { |
216 | 400 foreach (scandir($real, 2) as $f) { |
217 // Detect if the parent is annotated | 401 if ('.' !== $f[0]) { |
218 foreach ($parentAndTraits + $this->getOwnInterfaces($name, $parent) as $use) { | 402 $dirFiles[$f] = $f; |
219 if (!isset(self::$checkedClasses[$use])) { | 403 if ($f === $file) { |
220 $this->checkClass($use); | 404 $kFile = $k = $file; |
221 } | 405 } elseif ($f !== $k = strtolower($f)) { |
222 if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) { | 406 $dirFiles[$k] = $f; |
223 $type = class_exists($name, false) ? 'class' : (interface_exists($name, false) ? 'interface' : 'trait'); | |
224 $verb = class_exists($use, false) || interface_exists($name, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); | |
225 | |
226 @trigger_error(sprintf('The "%s" %s %s "%s" that is deprecated%s.', $name, $type, $verb, $use, self::$deprecated[$use]), E_USER_DEPRECATED); | |
227 } | |
228 if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) { | |
229 @trigger_error(sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $name), E_USER_DEPRECATED); | |
230 } | |
231 } | |
232 | |
233 // Inherit @final and @internal annotations for methods | |
234 self::$finalMethods[$name] = array(); | |
235 self::$internalMethods[$name] = array(); | |
236 foreach ($parentAndTraits as $use) { | |
237 foreach (array('finalMethods', 'internalMethods') as $property) { | |
238 if (isset(self::${$property}[$use])) { | |
239 self::${$property}[$name] = self::${$property}[$name] ? self::${$property}[$use] + self::${$property}[$name] : self::${$property}[$use]; | |
240 } | 407 } |
241 } | 408 } |
242 } | 409 } |
243 | 410 self::$darwinCache[$kDir][1] = $dirFiles; |
244 $isClass = \class_exists($name, false); | 411 } |
245 foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { | 412 |
246 if ($method->class !== $name) { | 413 return $real .= $dirFiles[$kFile]; |
247 continue; | |
248 } | |
249 | |
250 // Method from a trait | |
251 if ($method->getFilename() !== $refl->getFileName()) { | |
252 continue; | |
253 } | |
254 | |
255 if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) { | |
256 list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; | |
257 @trigger_error(sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); | |
258 } | |
259 | |
260 foreach ($parentAndTraits as $use) { | |
261 if (isset(self::$internalMethods[$use][$method->name])) { | |
262 list($declaringClass, $message) = self::$internalMethods[$use][$method->name]; | |
263 if (\strncmp($ns, $declaringClass, $len)) { | |
264 @trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); | |
265 } | |
266 } | |
267 } | |
268 | |
269 // Detect method annotations | |
270 if (false === $doc = $method->getDocComment()) { | |
271 continue; | |
272 } | |
273 | |
274 foreach (array('final', 'internal') as $annotation) { | |
275 if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) { | |
276 $message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; | |
277 self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message); | |
278 } | |
279 } | |
280 } | |
281 | |
282 if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) { | |
283 @trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); | |
284 } | |
285 } | |
286 | |
287 if ($file) { | |
288 if (!$exists) { | |
289 if (false !== strpos($class, '/')) { | |
290 throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); | |
291 } | |
292 | |
293 throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); | |
294 } | |
295 if (self::$caseCheck) { | |
296 $real = explode('\\', $class.strrchr($file, '.')); | |
297 $tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file)); | |
298 | |
299 $i = count($tail) - 1; | |
300 $j = count($real) - 1; | |
301 | |
302 while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { | |
303 --$i; | |
304 --$j; | |
305 } | |
306 | |
307 array_splice($tail, 0, $i + 1); | |
308 } | |
309 if (self::$caseCheck && $tail) { | |
310 $tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail); | |
311 $tailLen = strlen($tail); | |
312 $real = $refl->getFileName(); | |
313 | |
314 if (2 === self::$caseCheck) { | |
315 // realpath() on MacOSX doesn't normalize the case of characters | |
316 | |
317 $i = 1 + strrpos($real, '/'); | |
318 $file = substr($real, $i); | |
319 $real = substr($real, 0, $i); | |
320 | |
321 if (isset(self::$darwinCache[$real])) { | |
322 $kDir = $real; | |
323 } else { | |
324 $kDir = strtolower($real); | |
325 | |
326 if (isset(self::$darwinCache[$kDir])) { | |
327 $real = self::$darwinCache[$kDir][0]; | |
328 } else { | |
329 $dir = getcwd(); | |
330 chdir($real); | |
331 $real = getcwd().'/'; | |
332 chdir($dir); | |
333 | |
334 $dir = $real; | |
335 $k = $kDir; | |
336 $i = strlen($dir) - 1; | |
337 while (!isset(self::$darwinCache[$k])) { | |
338 self::$darwinCache[$k] = array($dir, array()); | |
339 self::$darwinCache[$dir] = &self::$darwinCache[$k]; | |
340 | |
341 while ('/' !== $dir[--$i]) { | |
342 } | |
343 $k = substr($k, 0, ++$i); | |
344 $dir = substr($dir, 0, $i--); | |
345 } | |
346 } | |
347 } | |
348 | |
349 $dirFiles = self::$darwinCache[$kDir][1]; | |
350 | |
351 if (isset($dirFiles[$file])) { | |
352 $kFile = $file; | |
353 } else { | |
354 $kFile = strtolower($file); | |
355 | |
356 if (!isset($dirFiles[$kFile])) { | |
357 foreach (scandir($real, 2) as $f) { | |
358 if ('.' !== $f[0]) { | |
359 $dirFiles[$f] = $f; | |
360 if ($f === $file) { | |
361 $kFile = $k = $file; | |
362 } elseif ($f !== $k = strtolower($f)) { | |
363 $dirFiles[$k] = $f; | |
364 } | |
365 } | |
366 } | |
367 self::$darwinCache[$kDir][1] = $dirFiles; | |
368 } | |
369 } | |
370 | |
371 $real .= $dirFiles[$kFile]; | |
372 } | |
373 | |
374 if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) | |
375 && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) | |
376 ) { | |
377 throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1))); | |
378 } | |
379 } | |
380 } | |
381 } | 414 } |
382 | 415 |
383 /** | 416 /** |
384 * `class_implements` includes interfaces from the parents so we have to manually exclude them. | 417 * `class_implements` includes interfaces from the parents so we have to manually exclude them. |
385 * | 418 * |