Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:02 - 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