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