vendor/symfony/symfony/src/Symfony/Component/Debug/DebugClassLoader.php line 141

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Debug;
  11. /**
  12.  * Autoloader checking if the class is really defined in the file found.
  13.  *
  14.  * The ClassLoader will wrap all registered autoloaders
  15.  * and will throw an exception if a file is found but does
  16.  * not declare the class.
  17.  *
  18.  * @author Fabien Potencier <fabien@symfony.com>
  19.  * @author Christophe Coevoet <stof@notk.org>
  20.  * @author Nicolas Grekas <p@tchwork.com>
  21.  */
  22. class DebugClassLoader
  23. {
  24.     private $classLoader;
  25.     private $isFinder;
  26.     private $loaded = array();
  27.     private static $caseCheck;
  28.     private static $final = array();
  29.     private static $finalMethods = array();
  30.     private static $deprecated = array();
  31.     private static $php7Reserved = array('int''float''bool''string''true''false''null');
  32.     private static $darwinCache = array('/' => array('/', array()));
  33.     public function __construct(callable $classLoader)
  34.     {
  35.         $this->classLoader $classLoader;
  36.         $this->isFinder is_array($classLoader) && method_exists($classLoader[0], 'findFile');
  37.         if (!isset(self::$caseCheck)) {
  38.             $file file_exists(__FILE__) ? __FILE__ rtrim(realpath('.'), DIRECTORY_SEPARATOR);
  39.             $i strrpos($fileDIRECTORY_SEPARATOR);
  40.             $dir substr($file0$i);
  41.             $file substr($file$i);
  42.             $test strtoupper($file) === $file strtolower($file) : strtoupper($file);
  43.             $test realpath($dir.$test);
  44.             if (false === $test || false === $i) {
  45.                 // filesystem is case sensitive
  46.                 self::$caseCheck 0;
  47.             } elseif (substr($test, -strlen($file)) === $file) {
  48.                 // filesystem is case insensitive and realpath() normalizes the case of characters
  49.                 self::$caseCheck 1;
  50.             } elseif (false !== stripos(PHP_OS'darwin')) {
  51.                 // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
  52.                 self::$caseCheck 2;
  53.             } else {
  54.                 // filesystem case checks failed, fallback to disabling them
  55.                 self::$caseCheck 0;
  56.             }
  57.         }
  58.     }
  59.     /**
  60.      * Gets the wrapped class loader.
  61.      *
  62.      * @return callable The wrapped class loader
  63.      */
  64.     public function getClassLoader()
  65.     {
  66.         return $this->classLoader;
  67.     }
  68.     /**
  69.      * Wraps all autoloaders.
  70.      */
  71.     public static function enable()
  72.     {
  73.         // Ensures we don't hit https://bugs.php.net/42098
  74.         class_exists('Symfony\Component\Debug\ErrorHandler');
  75.         class_exists('Psr\Log\LogLevel');
  76.         if (!is_array($functions spl_autoload_functions())) {
  77.             return;
  78.         }
  79.         foreach ($functions as $function) {
  80.             spl_autoload_unregister($function);
  81.         }
  82.         foreach ($functions as $function) {
  83.             if (!is_array($function) || !$function[0] instanceof self) {
  84.                 $function = array(new static($function), 'loadClass');
  85.             }
  86.             spl_autoload_register($function);
  87.         }
  88.     }
  89.     /**
  90.      * Disables the wrapping.
  91.      */
  92.     public static function disable()
  93.     {
  94.         if (!is_array($functions spl_autoload_functions())) {
  95.             return;
  96.         }
  97.         foreach ($functions as $function) {
  98.             spl_autoload_unregister($function);
  99.         }
  100.         foreach ($functions as $function) {
  101.             if (is_array($function) && $function[0] instanceof self) {
  102.                 $function $function[0]->getClassLoader();
  103.             }
  104.             spl_autoload_register($function);
  105.         }
  106.     }
  107.     /**
  108.      * Loads the given class or interface.
  109.      *
  110.      * @param string $class The name of the class
  111.      *
  112.      * @return bool|null True, if loaded
  113.      *
  114.      * @throws \RuntimeException
  115.      */
  116.     public function loadClass($class)
  117.     {
  118.         ErrorHandler::stackErrors();
  119.         try {
  120.             if ($this->isFinder && !isset($this->loaded[$class])) {
  121.                 $this->loaded[$class] = true;
  122.                 if ($file $this->classLoader[0]->findFile($class)) {
  123.                     require $file;
  124.                 }
  125.             } else {
  126.                 call_user_func($this->classLoader$class);
  127.                 $file false;
  128.             }
  129.         } finally {
  130.             ErrorHandler::unstackErrors();
  131.         }
  132.         $exists class_exists($classfalse) || interface_exists($classfalse) || trait_exists($classfalse);
  133.         if ($class && '\\' === $class[0]) {
  134.             $class substr($class1);
  135.         }
  136.         if ($exists) {
  137.             $refl = new \ReflectionClass($class);
  138.             $name $refl->getName();
  139.             if ($name !== $class && === strcasecmp($name$class)) {
  140.                 throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".'$class$name));
  141.             }
  142.             $parent get_parent_class($class);
  143.             // Not an interface nor a trait
  144.             if (class_exists($namefalse)) {
  145.                 if (preg_match('#\n \* @final(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s'$refl->getDocComment(), $notice)) {
  146.                     self::$final[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#'' '$notice[1]) : '';
  147.                 }
  148.                 if ($parent && isset(self::$final[$parent])) {
  149.                     @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".'$parentself::$final[$parent], $name), E_USER_DEPRECATED);
  150.                 }
  151.                 // Inherit @final annotations
  152.                 self::$finalMethods[$name] = $parent && isset(self::$finalMethods[$parent]) ? self::$finalMethods[$parent] : array();
  153.                 foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
  154.                     if ($method->class !== $name) {
  155.                         continue;
  156.                     }
  157.                     if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
  158.                         @trigger_error(sprintf('%s It may change without further notice as of its next major version. You should not extend it from "%s".'self::$finalMethods[$parent][$method->name], $name), E_USER_DEPRECATED);
  159.                     }
  160.                     $doc $method->getDocComment();
  161.                     if (false === $doc || false === strpos($doc'@final')) {
  162.                         continue;
  163.                     }
  164.                     if (preg_match('#\n\s+\* @final(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s'$doc$notice)) {
  165.                         $message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#'' '$notice[1]) : '';
  166.                         self::$finalMethods[$name][$method->name] = sprintf('The "%s::%s()" method is considered final%s.'$name$method->name$message);
  167.                     }
  168.                 }
  169.             }
  170.             if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) {
  171.                 @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);
  172.             } elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s'$refl->getDocComment(), $notice)) {
  173.                 self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#'' '$notice[1]);
  174.             } else {
  175.                 // Don't trigger deprecations for classes in the same vendor
  176.                 if ($len + (strpos($name'\\') ?: strpos($name'_'))) {
  177.                     $len 0;
  178.                     $ns '';
  179.                 } else {
  180.                     $ns substr($name0$len);
  181.                 }
  182.                 if (!$parent || strncmp($ns$parent$len)) {
  183.                     if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns$parent$len)) {
  184.                         @trigger_error(sprintf('The "%s" class extends "%s" that is deprecated %s'$name$parentself::$deprecated[$parent]), E_USER_DEPRECATED);
  185.                     }
  186.                     $parentInterfaces = array();
  187.                     $deprecatedInterfaces = array();
  188.                     if ($parent) {
  189.                         foreach (class_implements($parent) as $interface) {
  190.                             $parentInterfaces[$interface] = 1;
  191.                         }
  192.                     }
  193.                     foreach ($refl->getInterfaceNames() as $interface) {
  194.                         if (isset(self::$deprecated[$interface]) && strncmp($ns$interface$len)) {
  195.                             $deprecatedInterfaces[] = $interface;
  196.                         }
  197.                         foreach (class_implements($interface) as $interface) {
  198.                             $parentInterfaces[$interface] = 1;
  199.                         }
  200.                     }
  201.                     foreach ($deprecatedInterfaces as $interface) {
  202.                         if (!isset($parentInterfaces[$interface])) {
  203.                             @trigger_error(sprintf('The "%s" %s "%s" that is deprecated %s'$name$refl->isInterface() ? 'interface extends' 'class implements'$interfaceself::$deprecated[$interface]), E_USER_DEPRECATED);
  204.                         }
  205.                     }
  206.                 }
  207.             }
  208.         }
  209.         if ($file) {
  210.             if (!$exists) {
  211.                 if (false !== strpos($class'/')) {
  212.                     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));
  213.                 }
  214.                 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));
  215.             }
  216.             if (self::$caseCheck) {
  217.                 $real explode('\\'$class.strrchr($file'.'));
  218.                 $tail explode(DIRECTORY_SEPARATORstr_replace('/'DIRECTORY_SEPARATOR$file));
  219.                 $i count($tail) - 1;
  220.                 $j count($real) - 1;
  221.                 while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
  222.                     --$i;
  223.                     --$j;
  224.                 }
  225.                 array_splice($tail0$i 1);
  226.             }
  227.             if (self::$caseCheck && $tail) {
  228.                 $tail DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR$tail);
  229.                 $tailLen strlen($tail);
  230.                 $real $refl->getFileName();
  231.                 if (=== self::$caseCheck) {
  232.                     // realpath() on MacOSX doesn't normalize the case of characters
  233.                     $i strrpos($real'/');
  234.                     $file substr($real$i);
  235.                     $real substr($real0$i);
  236.                     if (isset(self::$darwinCache[$real])) {
  237.                         $kDir $real;
  238.                     } else {
  239.                         $kDir strtolower($real);
  240.                         if (isset(self::$darwinCache[$kDir])) {
  241.                             $real self::$darwinCache[$kDir][0];
  242.                         } else {
  243.                             $dir getcwd();
  244.                             chdir($real);
  245.                             $real getcwd().'/';
  246.                             chdir($dir);
  247.                             $dir $real;
  248.                             $k $kDir;
  249.                             $i strlen($dir) - 1;
  250.                             while (!isset(self::$darwinCache[$k])) {
  251.                                 self::$darwinCache[$k] = array($dir, array());
  252.                                 self::$darwinCache[$dir] = &self::$darwinCache[$k];
  253.                                 while ('/' !== $dir[--$i]) {
  254.                                 }
  255.                                 $k substr($k0, ++$i);
  256.                                 $dir substr($dir0$i--);
  257.                             }
  258.                         }
  259.                     }
  260.                     $dirFiles self::$darwinCache[$kDir][1];
  261.                     if (isset($dirFiles[$file])) {
  262.                         $kFile $file;
  263.                     } else {
  264.                         $kFile strtolower($file);
  265.                         if (!isset($dirFiles[$kFile])) {
  266.                             foreach (scandir($real2) as $f) {
  267.                                 if ('.' !== $f[0]) {
  268.                                     $dirFiles[$f] = $f;
  269.                                     if ($f === $file) {
  270.                                         $kFile $k $file;
  271.                                     } elseif ($f !== $k strtolower($f)) {
  272.                                         $dirFiles[$k] = $f;
  273.                                     }
  274.                                 }
  275.                             }
  276.                             self::$darwinCache[$kDir][1] = $dirFiles;
  277.                         }
  278.                     }
  279.                     $real .= $dirFiles[$kFile];
  280.                 }
  281.                 if (=== substr_compare($real$tail, -$tailLen$tailLentrue)
  282.                   && !== substr_compare($real$tail, -$tailLen$tailLenfalse)
  283.                 ) {
  284.                     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($real0, -$tailLen 1)));
  285.                 }
  286.             }
  287.             return true;
  288.         }
  289.     }
  290. }