[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |