[ Index ]

PHP Cross Reference of phpBB-3.3.2-deutsch

title

Body

[close]

/vendor/symfony/debug/ -> DebugClassLoader.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\Debug;
  13  
  14  /**
  15   * Autoloader checking if the class is really defined in the file found.
  16   *
  17   * The ClassLoader will wrap all registered autoloaders
  18   * and will throw an exception if a file is found but does
  19   * not declare the class.
  20   *
  21   * @author Fabien Potencier <fabien@symfony.com>
  22   * @author Christophe Coevoet <stof@notk.org>
  23   * @author Nicolas Grekas <p@tchwork.com>
  24   */
  25  class DebugClassLoader
  26  {
  27      private $classLoader;
  28      private $isFinder;
  29      private $loaded = [];
  30      private static $caseCheck;
  31      private static $checkedClasses = [];
  32      private static $final = [];
  33      private static $finalMethods = [];
  34      private static $deprecated = [];
  35      private static $internal = [];
  36      private static $internalMethods = [];
  37      private static $php7Reserved = ['int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1];
  38      private static $darwinCache = ['/' => ['/', []]];
  39  
  40      public function __construct(callable $classLoader)
  41      {
  42          $this->classLoader = $classLoader;
  43          $this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile');
  44  
  45          if (!isset(self::$caseCheck)) {
  46              $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR);
  47              $i = strrpos($file, \DIRECTORY_SEPARATOR);
  48              $dir = substr($file, 0, 1 + $i);
  49              $file = substr($file, 1 + $i);
  50              $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file);
  51              $test = realpath($dir.$test);
  52  
  53              if (false === $test || false === $i) {
  54                  // filesystem is case sensitive
  55                  self::$caseCheck = 0;
  56              } elseif (substr($test, -\strlen($file)) === $file) {
  57                  // filesystem is case insensitive and realpath() normalizes the case of characters
  58                  self::$caseCheck = 1;
  59              } elseif (false !== stripos(\PHP_OS, 'darwin')) {
  60                  // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
  61                  self::$caseCheck = 2;
  62              } else {
  63                  // filesystem case checks failed, fallback to disabling them
  64                  self::$caseCheck = 0;
  65              }
  66          }
  67      }
  68  
  69      /**
  70       * Gets the wrapped class loader.
  71       *
  72       * @return callable The wrapped class loader
  73       */
  74      public function getClassLoader()
  75      {
  76          return $this->classLoader;
  77      }
  78  
  79      /**
  80       * Wraps all autoloaders.
  81       */
  82      public static function enable()
  83      {
  84          // Ensures we don't hit https://bugs.php.net/42098
  85          class_exists('Symfony\Component\Debug\ErrorHandler');
  86          class_exists('Psr\Log\LogLevel');
  87  
  88          if (!\is_array($functions = spl_autoload_functions())) {
  89              return;
  90          }
  91  
  92          foreach ($functions as $function) {
  93              spl_autoload_unregister($function);
  94          }
  95  
  96          foreach ($functions as $function) {
  97              if (!\is_array($function) || !$function[0] instanceof self) {
  98                  $function = [new static($function), 'loadClass'];
  99              }
 100  
 101              spl_autoload_register($function);
 102          }
 103      }
 104  
 105      /**
 106       * Disables the wrapping.
 107       */
 108      public static function disable()
 109      {
 110          if (!\is_array($functions = spl_autoload_functions())) {
 111              return;
 112          }
 113  
 114          foreach ($functions as $function) {
 115              spl_autoload_unregister($function);
 116          }
 117  
 118          foreach ($functions as $function) {
 119              if (\is_array($function) && $function[0] instanceof self) {
 120                  $function = $function[0]->getClassLoader();
 121              }
 122  
 123              spl_autoload_register($function);
 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;
 133      }
 134  
 135      /**
 136       * Loads the given class or interface.
 137       *
 138       * @param string $class The name of the class
 139       *
 140       * @throws \RuntimeException
 141       */
 142      public function loadClass($class)
 143      {
 144          $e = error_reporting(error_reporting() | \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR);
 145  
 146          try {
 147              if ($this->isFinder && !isset($this->loaded[$class])) {
 148                  $this->loaded[$class] = true;
 149                  if (!$file = $this->classLoader[0]->findFile($class) ?: false) {
 150                      // no-op
 151                  } elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) {
 152                      include $file;
 153  
 154                      return;
 155                  } elseif (false === include $file) {
 156                      return;
 157                  }
 158              } else {
 159                  \call_user_func($this->classLoader, $class);
 160                  $file = false;
 161              }
 162          } finally {
 163              error_reporting($e);
 164          }
 165  
 166          $this->checkClass($class, $file);
 167      }
 168  
 169      private function checkClass($class, $file = null)
 170      {
 171          $exists = null === $file || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
 172  
 173          if (null !== $file && $class && '\\' === $class[0]) {
 174              $class = substr($class, 1);
 175          }
 176  
 177          if ($exists) {
 178              if (isset(self::$checkedClasses[$class])) {
 179                  return;
 180              }
 181              self::$checkedClasses[$class] = true;
 182  
 183              $refl = new \ReflectionClass($class);
 184              if (null === $file && $refl->isInternal()) {
 185                  return;
 186              }
 187              $name = $refl->getName();
 188  
 189              if ($name !== $class && 0 === strcasecmp($name, $class)) {
 190                  throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
 191              }
 192  
 193              $deprecations = $this->checkAnnotations($refl, $name);
 194  
 195              if (isset(self::$php7Reserved[strtolower($refl->getShortName())])) {
 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+\*(?: @|/$|\r?\n)#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) && !isset(self::$deprecated[$class])) {
 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+\*(?: @|/$|\r?\n)#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      /**
 321       * @param string $file
 322       * @param string $class
 323       *
 324       * @return array|null
 325       */
 326      public function checkCase(\ReflectionClass $refl, $file, $class)
 327      {
 328          $real = explode('\\', $class.strrchr($file, '.'));
 329          $tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file));
 330  
 331          $i = \count($tail) - 1;
 332          $j = \count($real) - 1;
 333  
 334          while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
 335              --$i;
 336              --$j;
 337          }
 338  
 339          array_splice($tail, 0, $i + 1);
 340  
 341          if (!$tail) {
 342              return null;
 343          }
 344  
 345          $tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
 346          $tailLen = \strlen($tail);
 347          $real = $refl->getFileName();
 348  
 349          if (2 === self::$caseCheck) {
 350              $real = $this->darwinRealpath($real);
 351          }
 352  
 353          if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
 354              && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
 355          ) {
 356              return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)];
 357          }
 358  
 359          return null;
 360      }
 361  
 362      /**
 363       * `realpath` on MacOSX doesn't normalize the case of characters.
 364       */
 365      private function darwinRealpath($real)
 366      {
 367          $i = 1 + strrpos($real, '/');
 368          $file = substr($real, $i);
 369          $real = substr($real, 0, $i);
 370  
 371          if (isset(self::$darwinCache[$real])) {
 372              $kDir = $real;
 373          } else {
 374              $kDir = strtolower($real);
 375  
 376              if (isset(self::$darwinCache[$kDir])) {
 377                  $real = self::$darwinCache[$kDir][0];
 378              } else {
 379                  $dir = getcwd();
 380                  chdir($real);
 381                  $real = getcwd().'/';
 382                  chdir($dir);
 383  
 384                  $dir = $real;
 385                  $k = $kDir;
 386                  $i = \strlen($dir) - 1;
 387                  while (!isset(self::$darwinCache[$k])) {
 388                      self::$darwinCache[$k] = [$dir, []];
 389                      self::$darwinCache[$dir] = &self::$darwinCache[$k];
 390  
 391                      while ('/' !== $dir[--$i]) {
 392                      }
 393                      $k = substr($k, 0, ++$i);
 394                      $dir = substr($dir, 0, $i--);
 395                  }
 396              }
 397          }
 398  
 399          $dirFiles = self::$darwinCache[$kDir][1];
 400  
 401          if (!isset($dirFiles[$file]) && ') : eval()\'d code' === substr($file, -17)) {
 402              // Get the file name from "file_name.php(123) : eval()'d code"
 403              $file = substr($file, 0, strrpos($file, '(', -17));
 404          }
 405  
 406          if (isset($dirFiles[$file])) {
 407              return $real.$dirFiles[$file];
 408          }
 409  
 410          $kFile = strtolower($file);
 411  
 412          if (!isset($dirFiles[$kFile])) {
 413              foreach (scandir($real, 2) as $f) {
 414                  if ('.' !== $f[0]) {
 415                      $dirFiles[$f] = $f;
 416                      if ($f === $file) {
 417                          $kFile = $k = $file;
 418                      } elseif ($f !== $k = strtolower($f)) {
 419                          $dirFiles[$k] = $f;
 420                      }
 421                  }
 422              }
 423              self::$darwinCache[$kDir][1] = $dirFiles;
 424          }
 425  
 426          return $real.$dirFiles[$kFile];
 427      }
 428  
 429      /**
 430       * `class_implements` includes interfaces from the parents so we have to manually exclude them.
 431       *
 432       * @param string       $class
 433       * @param string|false $parent
 434       *
 435       * @return string[]
 436       */
 437      private function getOwnInterfaces($class, $parent)
 438      {
 439          $ownInterfaces = class_implements($class, false);
 440  
 441          if ($parent) {
 442              foreach (class_implements($parent, false) as $interface) {
 443                  unset($ownInterfaces[$interface]);
 444              }
 445          }
 446  
 447          foreach ($ownInterfaces as $interface) {
 448              foreach (class_implements($interface) as $interface) {
 449                  unset($ownInterfaces[$interface]);
 450              }
 451          }
 452  
 453          return $ownInterfaces;
 454      }
 455  }


Generated: Wed Nov 11 20:28:18 2020 Cross-referenced by PHPXref 0.7.1