diff options
Diffstat (limited to 'vendor/symfony/debug/FatalErrorHandler')
4 files changed, 388 insertions, 0 deletions
diff --git a/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php new file mode 100644 index 00000000..32ba9a09 --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -0,0 +1,206 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\ClassNotFoundException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\DebugClassLoader; +use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; + +/** + * ErrorHandler for classes that do not exist. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '\' not found'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + foreach (array('class', 'interface', 'trait') as $typeName) { + $prefix = ucfirst($typeName).' \''; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + continue; + } + + $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { + $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); + $tail = ' for another namespace?'; + } else { + $className = $fullyQualifiedClassName; + $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); + $tail = '?'; + } + + if ($candidates = $this->getClassCandidates($className)) { + $tail = array_pop($candidates).'"?'; + if ($candidates) { + $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; + } else { + $tail = ' for "'.$tail; + } + } + $message .= "\nDid you forget a \"use\" statement".$tail; + + return new ClassNotFoundException($message, $exception); + } + } + + /** + * Tries to guess the full namespace for a given class name. + * + * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer + * autoloader (that should cover all common cases). + * + * @param string $class A class name (without its namespace) + * + * @return array An array of possible fully qualified class names + */ + private function getClassCandidates($class) + { + if (!is_array($functions = spl_autoload_functions())) { + return array(); + } + + // find Symfony and Composer autoloaders + $classes = array(); + + foreach ($functions as $function) { + if (!is_array($function)) { + continue; + } + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + + if (!is_array($function)) { + continue; + } + } + + if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) { + foreach ($function[0]->getPrefixes() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + if ($function[0] instanceof ComposerClassLoader) { + foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + } + + return array_unique($classes); + } + + /** + * @param string $path + * @param string $class + * @param string $prefix + * + * @return array + */ + private function findClassInPath($path, $class, $prefix) + { + if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { + return array(); + } + + $classes = array(); + $filename = $class.'.php'; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) { + $classes[] = $class; + } + } + + return $classes; + } + + /** + * @param string $path + * @param string $file + * @param string $prefix + * + * @return string|null + */ + private function convertFileToClass($path, $file, $prefix) + { + $candidates = array( + // namespaced class + $namespacedClass = str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file), + // namespaced class (with target dir) + $prefix.$namespacedClass, + // namespaced class (with target dir and separator) + $prefix.'\\'.$namespacedClass, + // PEAR class + str_replace('\\', '_', $namespacedClass), + // PEAR class (with target dir) + str_replace('\\', '_', $prefix.$namespacedClass), + // PEAR class (with target dir and separator) + str_replace('\\', '_', $prefix.'\\'.$namespacedClass), + ); + + if ($prefix) { + $candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); }); + } + + // We cannot use the autoloader here as most of them use require; but if the class + // is not found, the new autoloader call will require the file again leading to a + // "cannot redeclare class" error. + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + + require_once $file; + + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + } + + /** + * @param string $class + * + * @return bool + */ + private function classExists($class) + { + return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + } +} diff --git a/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php new file mode 100644 index 00000000..6b87eb30 --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php @@ -0,0 +1,32 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * Attempts to convert fatal errors to exceptions. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +interface FatalErrorHandlerInterface +{ + /** + * Attempts to convert an error into an exception. + * + * @param array $error An array as returned by error_get_last() + * @param FatalErrorException $exception A FatalErrorException instance + * + * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise + */ + public function handleError(array $error, FatalErrorException $exception); +} diff --git a/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php new file mode 100644 index 00000000..c6f391a7 --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php @@ -0,0 +1,84 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\UndefinedFunctionException; +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * ErrorHandler for undefined functions. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '()'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + $prefix = 'Call to undefined function '; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + return; + } + + $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { + $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); + } else { + $functionName = $fullyQualifiedFunctionName; + $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); + } + + $candidates = array(); + foreach (get_defined_functions() as $type => $definedFunctionNames) { + foreach ($definedFunctionNames as $definedFunctionName) { + if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { + $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); + } else { + $definedFunctionNameBasename = $definedFunctionName; + } + + if ($definedFunctionNameBasename === $functionName) { + $candidates[] = '\\'.$definedFunctionName; + } + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedFunctionException($message, $exception); + } +} diff --git a/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php new file mode 100644 index 00000000..6fa62b6f --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php @@ -0,0 +1,66 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\UndefinedMethodException; + +/** + * ErrorHandler for undefined methods. + * + * @author Grégoire Pineau <lyrixx@lyrixx.info> + */ +class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches); + if (!$matches) { + return; + } + + $className = $matches[1]; + $methodName = $matches[2]; + + $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); + + if (!class_exists($className) || null === $methods = get_class_methods($className)) { + // failed to get the class or its methods on which an unknown method was called (for example on an anonymous class) + return new UndefinedMethodException($message, $exception); + } + + $candidates = array(); + foreach ($methods as $definedMethodName) { + $lev = levenshtein($methodName, $definedMethodName); + if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) { + $candidates[] = $definedMethodName; + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedMethodException($message, $exception); + } +} |