[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/symfony/error-handler/ErrorEnhancer/ -> ClassNotFoundErrorEnhancer.php (source)

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


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer