Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

DebugClassLoader.php

Zuletzt modifiziert: 01.05.2026, 11:26 - Dateigröße: 16.12 KiB


 001  <?php
 002   
 003  /*
 004   * This file is part of the Symfony package.
 005   *
 006   * (c) Fabien Potencier <fabien@symfony.com>
 007   *
 008   * For the full copyright and license information, please view the LICENSE
 009   * file that was distributed with this source code.
 010   */
 011   
 012  namespace Symfony\Component\Debug;
 013   
 014  /**
 015   * Autoloader checking if the class is really defined in the file found.
 016   *
 017   * The ClassLoader will wrap all registered autoloaders
 018   * and will throw an exception if a file is found but does
 019   * not declare the class.
 020   *
 021   * @author Fabien Potencier <fabien@symfony.com>
 022   * @author Christophe Coevoet <stof@notk.org>
 023   * @author Nicolas Grekas <p@tchwork.com>
 024   */
 025  class DebugClassLoader
 026  {
 027      private $classLoader;
 028      private $isFinder;
 029      private $loaded = [];
 030      private static $caseCheck;
 031      private static $checkedClasses = [];
 032      private static $final = [];
 033      private static $finalMethods = [];
 034      private static $deprecated = [];
 035      private static $internal = [];
 036      private static $internalMethods = [];
 037      private static $php7Reserved = ['int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1];
 038      private static $darwinCache = ['/' => ['/', []]];
 039   
 040      public function __construct(callable $classLoader)
 041      {
 042          $this->classLoader = $classLoader;
 043          $this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile');
 044   
 045          if (!isset(self::$caseCheck)) {
 046              $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR);
 047              $i = strrpos($file, \DIRECTORY_SEPARATOR);
 048              $dir = substr($file, 0, 1 + $i);
 049              $file = substr($file, 1 + $i);
 050              $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file);
 051              $test = realpath($dir.$test);
 052   
 053              if (false === $test || false === $i) {
 054                  // filesystem is case sensitive
 055                  self::$caseCheck = 0;
 056              } elseif (substr($test, -\strlen($file)) === $file) {
 057                  // filesystem is case insensitive and realpath() normalizes the case of characters
 058                  self::$caseCheck = 1;
 059              } elseif (false !== stripos(\PHP_OS, 'darwin')) {
 060                  // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
 061                  self::$caseCheck = 2;
 062              } else {
 063                  // filesystem case checks failed, fallback to disabling them
 064                  self::$caseCheck = 0;
 065              }
 066          }
 067      }
 068   
 069      /**
 070       * Gets the wrapped class loader.
 071       *
 072       * @return callable The wrapped class loader
 073       */
 074      public function getClassLoader()
 075      {
 076          return $this->classLoader;
 077      }
 078   
 079      /**
 080       * Wraps all autoloaders.
 081       */
 082      public static function enable()
 083      {
 084          // Ensures we don't hit https://bugs.php.net/42098
 085          class_exists('Symfony\Component\Debug\ErrorHandler');
 086          class_exists('Psr\Log\LogLevel');
 087   
 088          if (!\is_array($functions = spl_autoload_functions())) {
 089              return;
 090          }
 091   
 092          foreach ($functions as $function) {
 093              spl_autoload_unregister($function);
 094          }
 095   
 096          foreach ($functions as $function) {
 097              if (!\is_array($function) || !$function[0] instanceof self) {
 098                  $function = [new static($function), 'loadClass'];
 099              }
 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  }
 456