Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 88d6c25

Browse files
[Debug] Fix false-positive "MicroKernelTrait::loadRoutes()" method is considered internal"
1 parent 0218507 commit 88d6c25

File tree

3 files changed

+215
-159
lines changed

3 files changed

+215
-159
lines changed

src/Symfony/Component/Debug/DebugClassLoader.php

Lines changed: 184 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ public static function disable()
129129
*
130130
* @param string $class The name of the class
131131
*
132-
* @return bool|null True, if loaded
133-
*
134132
* @throws \RuntimeException
135133
*/
136134
public function loadClass($class)
@@ -184,198 +182,227 @@ private function checkClass($class, $file = null)
184182
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
185183
}
186184

187-
// Don't trigger deprecations for classes in the same vendor
188-
if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) {
189-
$len = 0;
190-
$ns = '';
191-
} else {
192-
$ns = \substr($name, 0, $len);
185+
$deprecations = $this->checkAnnotations($refl, $name);
186+
187+
if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) {
188+
$deprecations[] = sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName());
193189
}
194190

195-
// Detect annotations on the class
196-
if (false !== $doc = $refl->getDocComment()) {
197-
foreach (array('final', 'deprecated', 'internal') as $annotation) {
198-
if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) {
199-
self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
200-
}
201-
}
191+
foreach ($deprecations as $message) {
192+
@trigger_error($message, E_USER_DEPRECATED);
202193
}
194+
}
203195

204-
$parentAndTraits = \class_uses($name, false);
205-
if ($parent = \get_parent_class($class)) {
206-
$parentAndTraits[] = $parent;
196+
if (!$file) {
197+
return;
198+
}
207199

208-
if (!isset(self::$checkedClasses[$parent])) {
209-
$this->checkClass($parent);
210-
}
200+
if (!$exists) {
201+
if (false !== strpos($class, '/')) {
202+
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));
203+
}
204+
205+
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));
206+
}
207+
208+
if (self::$caseCheck && $message = $this->checkCase($refl, $file, $class)) {
209+
throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2]));
210+
}
211+
}
211212

212-
if (isset(self::$final[$parent])) {
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);
213+
public function checkAnnotations(\ReflectionClass $refl, $class)
214+
{
215+
$deprecations = array();
216+
217+
// Don't trigger deprecations for classes in the same vendor
218+
if (2 > $len = 1 + (\strpos($class, '\\') ?: \strpos($class, '_'))) {
219+
$len = 0;
220+
$ns = '';
221+
} else {
222+
$ns = \substr($class, 0, $len);
223+
}
224+
225+
// Detect annotations on the class
226+
if (false !== $doc = $refl->getDocComment()) {
227+
foreach (array('final', 'deprecated', 'internal') as $annotation) {
228+
if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) {
229+
self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
214230
}
215231
}
232+
}
216233

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');
234+
$parent = \get_parent_class($class);
235+
$parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent);
236+
if ($parent) {
237+
$parentAndOwnInterfaces[$parent] = $parent;
225238

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-
}
239+
if (!isset(self::$checkedClasses[$parent])) {
240+
$this->checkClass($parent);
231241
}
232242

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-
}
243+
if (isset(self::$final[$parent])) {
244+
$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);
242245
}
246+
}
243247

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-
}
248+
// Detect if the parent is annotated
249+
foreach ($parentAndOwnInterfaces + \class_uses($class, false) as $use) {
250+
if (!isset(self::$checkedClasses[$use])) {
251+
$this->checkClass($use);
252+
}
253+
if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) {
254+
$type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait');
255+
$verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
249256

250-
if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) {
251-
list($declaringClass, $message) = self::$finalMethods[$parent][$method->name];
252-
@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);
253-
}
257+
$deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]);
258+
}
259+
if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) {
260+
$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);
261+
}
262+
}
254263

255-
if (isset(self::$internalMethods[$name][$method->name])) {
256-
list($declaringClass, $message) = self::$internalMethods[$name][$method->name];
257-
if (\strncmp($ns, $declaringClass, $len)) {
258-
@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);
259-
}
260-
}
264+
if (\trait_exists($class)) {
265+
return $deprecations;
266+
}
261267

262-
// Method from a trait
263-
if ($method->getFilename() !== $refl->getFileName()) {
264-
continue;
268+
// Inherit @final and @internal annotations for methods
269+
self::$finalMethods[$class] = array();
270+
self::$internalMethods[$class] = array();
271+
foreach ($parentAndOwnInterfaces as $use) {
272+
foreach (array('finalMethods', 'internalMethods') as $property) {
273+
if (isset(self::${$property}[$use])) {
274+
self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use];
265275
}
276+
}
277+
}
266278

267-
// Detect method annotations
268-
if (false === $doc = $method->getDocComment()) {
269-
continue;
270-
}
279+
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
280+
if ($method->class !== $class) {
281+
continue;
282+
}
271283

272-
foreach (array('final', 'internal') as $annotation) {
273-
if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) {
274-
$message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
275-
self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message);
276-
}
284+
if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
285+
list($declaringClass, $message) = self::$finalMethods[$parent][$method->name];
286+
$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);
287+
}
288+
289+
if (isset(self::$internalMethods[$class][$method->name])) {
290+
list($declaringClass, $message) = self::$internalMethods[$class][$method->name];
291+
if (\strncmp($ns, $declaringClass, $len)) {
292+
$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);
277293
}
278294
}
279295

280-
if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) {
281-
@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);
296+
// Detect method annotations
297+
if (!$doc = $method->getDocComment()) {
298+
continue;
282299
}
283-
}
284300

285-
if ($file) {
286-
if (!$exists) {
287-
if (false !== strpos($class, '/')) {
288-
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));
301+
foreach (array('final', 'internal') as $annotation) {
302+
if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) {
303+
$message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
304+
self::${$annotation.'Methods'}[$class][$method->name] = array($class, $message);
289305
}
290-
291-
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));
292306
}
293-
if (self::$caseCheck) {
294-
$real = explode('\\', $class.strrchr($file, '.'));
295-
$tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file));
307+
}
296308

297-
$i = \count($tail) - 1;
298-
$j = \count($real) - 1;
309+
return $deprecations;
310+
}
299311

300-
while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
301-
--$i;
302-
--$j;
303-
}
312+
public function checkCase(\ReflectionClass $refl, $file, $class)
313+
{
314+
$real = explode('\\', $class.strrchr($file, '.'));
315+
$tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file));
304316

305-
array_splice($tail, 0, $i + 1);
306-
}
307-
if (self::$caseCheck && $tail) {
308-
$tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
309-
$tailLen = \strlen($tail);
310-
$real = $refl->getFileName();
311-
312-
if (2 === self::$caseCheck) {
313-
// realpath() on MacOSX doesn't normalize the case of characters
314-
315-
$i = 1 + strrpos($real, '/');
316-
$file = substr($real, $i);
317-
$real = substr($real, 0, $i);
318-
319-
if (isset(self::$darwinCache[$real])) {
320-
$kDir = $real;
321-
} else {
322-
$kDir = strtolower($real);
323-
324-
if (isset(self::$darwinCache[$kDir])) {
325-
$real = self::$darwinCache[$kDir][0];
326-
} else {
327-
$dir = getcwd();
328-
chdir($real);
329-
$real = getcwd().'/';
330-
chdir($dir);
331-
332-
$dir = $real;
333-
$k = $kDir;
334-
$i = \strlen($dir) - 1;
335-
while (!isset(self::$darwinCache[$k])) {
336-
self::$darwinCache[$k] = array($dir, array());
337-
self::$darwinCache[$dir] = &self::$darwinCache[$k];
338-
339-
while ('/' !== $dir[--$i]) {
340-
}
341-
$k = substr($k, 0, ++$i);
342-
$dir = substr($dir, 0, $i--);
343-
}
344-
}
345-
}
317+
$i = \count($tail) - 1;
318+
$j = \count($real) - 1;
346319

347-
$dirFiles = self::$darwinCache[$kDir][1];
348-
349-
if (isset($dirFiles[$file])) {
350-
$kFile = $file;
351-
} else {
352-
$kFile = strtolower($file);
353-
354-
if (!isset($dirFiles[$kFile])) {
355-
foreach (scandir($real, 2) as $f) {
356-
if ('.' !== $f[0]) {
357-
$dirFiles[$f] = $f;
358-
if ($f === $file) {
359-
$kFile = $k = $file;
360-
} elseif ($f !== $k = strtolower($f)) {
361-
$dirFiles[$k] = $f;
362-
}
363-
}
364-
}
365-
self::$darwinCache[$kDir][1] = $dirFiles;
366-
}
367-
}
320+
while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
321+
--$i;
322+
--$j;
323+
}
324+
325+
array_splice($tail, 0, $i + 1);
368326

369-
$real .= $dirFiles[$kFile];
327+
if (!$tail) {
328+
return;
329+
}
330+
331+
$tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
332+
$tailLen = \strlen($tail);
333+
$real = $refl->getFileName();
334+
335+
if (2 === self::$caseCheck) {
336+
$real = $this->darwinRealpath($real);
337+
}
338+
339+
if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
340+
&& 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
341+
) {
342+
return array(substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1));
343+
}
344+
}
345+
346+
/**
347+
* `realpath` on MacOSX doesn't normalize the case of characters.
348+
*/
349+
private function darwinRealpath($real)
350+
{
351+
$i = 1 + strrpos($real, '/');
352+
$file = substr($real, $i);
353+
$real = substr($real, 0, $i);
354+
355+
if (isset(self::$darwinCache[$real])) {
356+
$kDir = $real;
357+
} else {
358+
$kDir = strtolower($real);
359+
360+
if (isset(self::$darwinCache[$kDir])) {
361+
$real = self::$darwinCache[$kDir][0];
362+
} else {
363+
$dir = getcwd();
364+
chdir($real);
365+
$real = getcwd().'/';
366+
chdir($dir);
367+
368+
$dir = $real;
369+
$k = $kDir;
370+
$i = \strlen($dir) - 1;
371+
while (!isset(self::$darwinCache[$k])) {
372+
self::$darwinCache[$k] = array($dir, array());
373+
self::$darwinCache[$dir] = &self::$darwinCache[$k];
374+
375+
while ('/' !== $dir[--$i]) {
376+
}
377+
$k = substr($k, 0, ++$i);
378+
$dir = substr($dir, 0, $i--);
370379
}
380+
}
381+
}
371382

372-
if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
373-
&& 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
374-
) {
375-
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)));
383+
$dirFiles = self::$darwinCache[$kDir][1];
384+
385+
if (isset($dirFiles[$file])) {
386+
return $real .= $dirFiles[$file];
387+
}
388+
389+
$kFile = strtolower($file);
390+
391+
if (!isset($dirFiles[$kFile])) {
392+
foreach (scandir($real, 2) as $f) {
393+
if ('.' !== $f[0]) {
394+
$dirFiles[$f] = $f;
395+
if ($f === $file) {
396+
$kFile = $k = $file;
397+
} elseif ($f !== $k = strtolower($f)) {
398+
$dirFiles[$k] = $f;
399+
}
376400
}
377401
}
402+
self::$darwinCache[$kDir][1] = $dirFiles;
378403
}
404+
405+
return $real .= $dirFiles[$kFile];
379406
}
380407

381408
/**

0 commit comments

Comments
 (0)