Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/debug/DebugClassLoader.php @ 12:7a779792577d
Update Drupal core to v8.4.5 (via Composer)
author | Chris Cannam |
---|---|
date | Fri, 23 Feb 2018 15:52:07 +0000 |
parents | 4c8ae668cc8c |
children | 5fb285c0d0e3 |
comparison
equal
deleted
inserted
replaced
11:bfffd8d7479a | 12:7a779792577d |
---|---|
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 static $caseCheck; | 30 private static $caseCheck; |
31 private static $checkedClasses = array(); | |
32 private static $final = array(); | |
33 private static $finalMethods = array(); | |
30 private static $deprecated = array(); | 34 private static $deprecated = array(); |
31 private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null'); | 35 private static $internal = array(); |
36 private static $internalMethods = array(); | |
37 private static $php7Reserved = array('int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1); | |
32 private static $darwinCache = array('/' => array('/', array())); | 38 private static $darwinCache = array('/' => array('/', array())); |
33 | 39 |
34 /** | |
35 * Constructor. | |
36 * | |
37 * @param callable $classLoader A class loader | |
38 */ | |
39 public function __construct(callable $classLoader) | 40 public function __construct(callable $classLoader) |
40 { | 41 { |
41 $this->classLoader = $classLoader; | 42 $this->classLoader = $classLoader; |
42 $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile'); | 43 $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile'); |
43 | 44 |
132 * | 133 * |
133 * @throws \RuntimeException | 134 * @throws \RuntimeException |
134 */ | 135 */ |
135 public function loadClass($class) | 136 public function loadClass($class) |
136 { | 137 { |
137 ErrorHandler::stackErrors(); | 138 $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); |
138 | 139 |
139 try { | 140 try { |
140 if ($this->isFinder) { | 141 if ($this->isFinder && !isset($this->loaded[$class])) { |
141 if ($file = $this->classLoader[0]->findFile($class)) { | 142 $this->loaded[$class] = true; |
142 require_once $file; | 143 if ($file = $this->classLoader[0]->findFile($class) ?: false) { |
144 $wasCached = \function_exists('opcache_is_script_cached') && opcache_is_script_cached($file); | |
145 | |
146 require $file; | |
147 | |
148 if ($wasCached) { | |
149 return; | |
150 } | |
143 } | 151 } |
144 } else { | 152 } else { |
145 call_user_func($this->classLoader, $class); | 153 call_user_func($this->classLoader, $class); |
146 $file = false; | 154 $file = false; |
147 } | 155 } |
148 } finally { | 156 } finally { |
149 ErrorHandler::unstackErrors(); | 157 error_reporting($e); |
150 } | 158 } |
151 | 159 |
152 $exists = class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); | 160 $this->checkClass($class, $file); |
153 | 161 } |
154 if ($class && '\\' === $class[0]) { | 162 |
163 private function checkClass($class, $file = null) | |
164 { | |
165 $exists = null === $file || \class_exists($class, false) || \interface_exists($class, false) || \trait_exists($class, false); | |
166 | |
167 if (null !== $file && $class && '\\' === $class[0]) { | |
155 $class = substr($class, 1); | 168 $class = substr($class, 1); |
156 } | 169 } |
157 | 170 |
158 if ($exists) { | 171 if ($exists) { |
172 if (isset(self::$checkedClasses[$class])) { | |
173 return; | |
174 } | |
175 self::$checkedClasses[$class] = true; | |
176 | |
159 $refl = new \ReflectionClass($class); | 177 $refl = new \ReflectionClass($class); |
178 if (null === $file && $refl->isInternal()) { | |
179 return; | |
180 } | |
160 $name = $refl->getName(); | 181 $name = $refl->getName(); |
161 | 182 |
162 if ($name !== $class && 0 === strcasecmp($name, $class)) { | 183 if ($name !== $class && 0 === \strcasecmp($name, $class)) { |
163 throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name)); | 184 throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); |
164 } | 185 } |
165 | 186 |
166 if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) { | 187 // Don't trigger deprecations for classes in the same vendor |
167 @trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); | 188 if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) { |
168 } elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) { | 189 $len = 0; |
169 self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]); | 190 $ns = ''; |
170 } else { | 191 } else { |
171 if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) { | 192 $ns = \substr($name, 0, $len); |
172 $len = 0; | 193 } |
173 $ns = ''; | 194 |
174 } else { | 195 // Detect annotations on the class |
175 switch ($ns = substr($name, 0, $len)) { | 196 if (false !== $doc = $refl->getDocComment()) { |
176 case 'Symfony\Bridge\\': | 197 foreach (array('final', 'deprecated', 'internal') as $annotation) { |
177 case 'Symfony\Bundle\\': | 198 if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) { |
178 case 'Symfony\Component\\': | 199 self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; |
179 $ns = 'Symfony\\'; | 200 } |
180 $len = strlen($ns); | 201 } |
181 break; | 202 } |
182 } | 203 |
183 } | 204 $parentAndTraits = \class_uses($name, false); |
184 $parent = get_parent_class($class); | 205 if ($parent = \get_parent_class($class)) { |
185 | 206 $parentAndTraits[] = $parent; |
186 if (!$parent || strncmp($ns, $parent, $len)) { | 207 |
187 if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns, $parent, $len)) { | 208 if (!isset(self::$checkedClasses[$parent])) { |
188 @trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED); | 209 $this->checkClass($parent); |
189 } | 210 } |
190 | 211 |
191 $parentInterfaces = array(); | 212 if (isset(self::$final[$parent])) { |
192 $deprecatedInterfaces = array(); | 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); |
193 if ($parent) { | 214 } |
194 foreach (class_implements($parent) as $interface) { | 215 } |
195 $parentInterfaces[$interface] = 1; | 216 |
217 // Detect if the parent is annotated | |
218 foreach ($parentAndTraits + $this->getOwnInterfaces($name, $parent) as $use) { | |
219 if (!isset(self::$checkedClasses[$use])) { | |
220 $this->checkClass($use); | |
221 } | |
222 if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) { | |
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 } | |
241 } | |
242 } | |
243 | |
244 $isClass = \class_exists($name, false); | |
245 foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { | |
246 if ($method->class !== $name) { | |
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); | |
196 } | 265 } |
197 } | 266 } |
198 | 267 } |
199 foreach ($refl->getInterfaceNames() as $interface) { | 268 |
200 if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) { | 269 // Detect method annotations |
201 $deprecatedInterfaces[] = $interface; | 270 if (false === $doc = $method->getDocComment()) { |
202 } | 271 continue; |
203 foreach (class_implements($interface) as $interface) { | 272 } |
204 $parentInterfaces[$interface] = 1; | 273 |
205 } | 274 foreach (array('final', 'internal') as $annotation) { |
206 } | 275 if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) { |
207 | 276 $message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; |
208 foreach ($deprecatedInterfaces as $interface) { | 277 self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message); |
209 if (!isset($parentInterfaces[$interface])) { | 278 } |
210 @trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED); | 279 } |
211 } | 280 } |
212 } | 281 |
213 } | 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); | |
214 } | 284 } |
215 } | 285 } |
216 | 286 |
217 if ($file) { | 287 if ($file) { |
218 if (!$exists) { | 288 if (!$exists) { |
302 } | 372 } |
303 | 373 |
304 if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) | 374 if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) |
305 && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) | 375 && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) |
306 ) { | 376 ) { |
307 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))); | 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))); |
308 } | 378 } |
309 } | 379 } |
310 | 380 } |
311 return true; | 381 } |
312 } | 382 |
383 /** | |
384 * `class_implements` includes interfaces from the parents so we have to manually exclude them. | |
385 * | |
386 * @param string $class | |
387 * @param string|false $parent | |
388 * | |
389 * @return string[] | |
390 */ | |
391 private function getOwnInterfaces($class, $parent) | |
392 { | |
393 $ownInterfaces = class_implements($class, false); | |
394 | |
395 if ($parent) { | |
396 foreach (class_implements($parent, false) as $interface) { | |
397 unset($ownInterfaces[$interface]); | |
398 } | |
399 } | |
400 | |
401 foreach ($ownInterfaces as $interface) { | |
402 foreach (class_implements($interface) as $interface) { | |
403 unset($ownInterfaces[$interface]); | |
404 } | |
405 } | |
406 | |
407 return $ownInterfaces; | |
313 } | 408 } |
314 } | 409 } |