[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/symfony/debug/FatalErrorHandler/ -> ClassNotFoundFatalErrorHandler.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\FatalErrorHandler;
  13  
  14  use Composer\Autoload\ClassLoader as ComposerClassLoader;
  15  use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
  16  use Symfony\Component\Debug\DebugClassLoader;
  17  use Symfony\Component\Debug\Exception\ClassNotFoundException;
  18  use Symfony\Component\Debug\Exception\FatalErrorException;
  19  
  20  /**
  21   * ErrorHandler for classes that do not exist.
  22   *
  23   * @author Fabien Potencier <fabien@symfony.com>
  24   */
  25  class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
  26  {
  27      /**
  28       * {@inheritdoc}
  29       */
  30      public function handleError(array $error, FatalErrorException $exception)
  31      {
  32          if (!preg_match('/^(Class|Interface|Trait) [\'"]([^\'"]+)[\'"] not found$/', $error['message'], $matches)) {
  33              return null;
  34          }
  35          $typeName = strtolower($matches[1]);
  36          $fullyQualifiedClassName = $matches[2];
  37  
  38          if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) {
  39              $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1);
  40              $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex);
  41              $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix);
  42              $tail = ' for another namespace?';
  43          } else {
  44              $className = $fullyQualifiedClassName;
  45              $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className);
  46              $tail = '?';
  47          }
  48  
  49          if ($candidates = $this->getClassCandidates($className)) {
  50              $tail = array_pop($candidates).'"?';
  51              if ($candidates) {
  52                  $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail;
  53              } else {
  54                  $tail = ' for "'.$tail;
  55              }
  56          }
  57          $message .= "\nDid you forget a \"use\" statement".$tail;
  58  
  59          return new ClassNotFoundException($message, $exception);
  60      }
  61  
  62      /**
  63       * Tries to guess the full namespace for a given class name.
  64       *
  65       * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer
  66       * autoloader (that should cover all common cases).
  67       *
  68       * @param string $class A class name (without its namespace)
  69       *
  70       * @return array An array of possible fully qualified class names
  71       */
  72      private function getClassCandidates($class)
  73      {
  74          if (!\is_array($functions = spl_autoload_functions())) {
  75              return [];
  76          }
  77  
  78          // find Symfony and Composer autoloaders
  79          $classes = [];
  80  
  81          foreach ($functions as $function) {
  82              if (!\is_array($function)) {
  83                  continue;
  84              }
  85              // get class loaders wrapped by DebugClassLoader
  86              if ($function[0] instanceof DebugClassLoader) {
  87                  $function = $function[0]->getClassLoader();
  88  
  89                  if (!\is_array($function)) {
  90                      continue;
  91                  }
  92              }
  93  
  94              if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
  95                  foreach ($function[0]->getPrefixes() as $prefix => $paths) {
  96                      foreach ($paths as $path) {
  97                          $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
  98                      }
  99                  }
 100              }
 101              if ($function[0] instanceof ComposerClassLoader) {
 102                  foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) {
 103                      foreach ($paths as $path) {
 104                          $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
 105                      }
 106                  }
 107              }
 108          }
 109  
 110          return array_unique($classes);
 111      }
 112  
 113      /**
 114       * @param string $path
 115       * @param string $class
 116       * @param string $prefix
 117       *
 118       * @return array
 119       */
 120      private function findClassInPath($path, $class, $prefix)
 121      {
 122          if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) {
 123              return [];
 124          }
 125  
 126          $classes = [];
 127          $filename = $class.'.php';
 128          foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
 129              if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) {
 130                  $classes[] = $class;
 131              }
 132          }
 133  
 134          return $classes;
 135      }
 136  
 137      /**
 138       * @param string $path
 139       * @param string $file
 140       * @param string $prefix
 141       *
 142       * @return string|null
 143       */
 144      private function convertFileToClass($path, $file, $prefix)
 145      {
 146          $candidates = [
 147              // namespaced class
 148              $namespacedClass = str_replace([$path.\DIRECTORY_SEPARATOR, '.php', '/'], ['', '', '\\'], $file),
 149              // namespaced class (with target dir)
 150              $prefix.$namespacedClass,
 151              // namespaced class (with target dir and separator)
 152              $prefix.'\\'.$namespacedClass,
 153              // PEAR class
 154              str_replace('\\', '_', $namespacedClass),
 155              // PEAR class (with target dir)
 156              str_replace('\\', '_', $prefix.$namespacedClass),
 157              // PEAR class (with target dir and separator)
 158              str_replace('\\', '_', $prefix.'\\'.$namespacedClass),
 159          ];
 160  
 161          if ($prefix) {
 162              $candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); });
 163          }
 164  
 165          // We cannot use the autoloader here as most of them use require; but if the class
 166          // is not found, the new autoloader call will require the file again leading to a
 167          // "cannot redeclare class" error.
 168          foreach ($candidates as $candidate) {
 169              if ($this->classExists($candidate)) {
 170                  return $candidate;
 171              }
 172          }
 173  
 174          try {
 175              require_once $file;
 176          } catch (\Throwable $e) {
 177              return null;
 178          }
 179  
 180          foreach ($candidates as $candidate) {
 181              if ($this->classExists($candidate)) {
 182                  return $candidate;
 183              }
 184          }
 185  
 186          return null;
 187      }
 188  
 189      /**
 190       * @param string $class
 191       *
 192       * @return bool
 193       */
 194      private function classExists($class)
 195      {
 196          return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
 197      }
 198  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1