From bd056422bfea3f788ea3b5f301687f23473367ce Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Jul 2019 16:21:14 +0200 Subject: [PATCH] [Debug] drop the component --- composer.json | 1 - src/Symfony/Component/Debug/.gitignore | 3 - .../Component/Debug/BufferingLogger.php | 41 - src/Symfony/Component/Debug/CHANGELOG.md | 84 -- src/Symfony/Component/Debug/Debug.php | 64 -- .../Component/Debug/DebugClassLoader.php | 529 ------------- src/Symfony/Component/Debug/ErrorHandler.php | 719 ------------------ .../Exception/ClassNotFoundException.php | 40 - .../Debug/Exception/FatalErrorException.php | 81 -- .../Debug/Exception/FatalThrowableError.php | 55 -- .../Debug/Exception/FlattenException.php | 368 --------- .../Debug/Exception/OutOfMemoryException.php | 25 - .../Debug/Exception/SilencedErrorContext.php | 71 -- .../Exception/UndefinedFunctionException.php | 40 - .../Exception/UndefinedMethodException.php | 40 - .../Component/Debug/ExceptionHandler.php | 470 ------------ .../ClassNotFoundFatalErrorHandler.php | 197 ----- .../FatalErrorHandlerInterface.php | 36 - .../UndefinedFunctionFatalErrorHandler.php | 88 --- .../UndefinedMethodFatalErrorHandler.php | 70 -- src/Symfony/Component/Debug/LICENSE | 19 - src/Symfony/Component/Debug/README.md | 13 - .../Debug/Tests/DebugClassLoaderTest.php | 451 ----------- .../Debug/Tests/ErrorHandlerTest.php | 562 -------------- .../Tests/Exception/FlattenExceptionTest.php | 391 ---------- .../Debug/Tests/ExceptionHandlerTest.php | 148 ---- .../ClassNotFoundFatalErrorHandlerTest.php | 183 ----- ...UndefinedFunctionFatalErrorHandlerTest.php | 84 -- .../UndefinedMethodFatalErrorHandlerTest.php | 79 -- .../Debug/Tests/Fixtures/AnnotatedClass.php | 13 - .../Debug/Tests/Fixtures/ClassAlias.php | 3 - .../Fixtures/ClassWithAnnotatedParameters.php | 34 - .../Fixtures/DefinitionInEvaluatedCode.php | 11 - .../Debug/Tests/Fixtures/DeprecatedClass.php | 12 - .../Tests/Fixtures/DeprecatedInterface.php | 12 - .../ErrorHandlerThatUsesThePreviousOne.php | 22 - .../Tests/Fixtures/ExtendedFinalMethod.php | 19 - .../Debug/Tests/Fixtures/FinalClasses.php | 85 --- .../Debug/Tests/Fixtures/FinalMethod.php | 26 - .../Tests/Fixtures/FinalMethod2Trait.php | 10 - .../InterfaceWithAnnotatedParameters.php | 27 - .../Debug/Tests/Fixtures/InternalClass.php | 15 - .../Tests/Fixtures/InternalInterface.php | 10 - .../Debug/Tests/Fixtures/InternalTrait.php | 10 - .../Debug/Tests/Fixtures/InternalTrait2.php | 23 - .../Fixtures/LoggerThatSetAnErrorHandler.php | 15 - .../Tests/Fixtures/NonDeprecatedInterface.php | 7 - .../Debug/Tests/Fixtures/PEARClass.php | 5 - .../SubClassWithAnnotatedParameters.php | 32 - .../Debug/Tests/Fixtures/Throwing.php | 3 - .../Debug/Tests/Fixtures/ToStringThrower.php | 24 - .../Fixtures/TraitWithAnnotatedParameters.php | 13 - .../Fixtures/TraitWithInternalMethod.php | 13 - .../Debug/Tests/Fixtures/VirtualClass.php | 11 - .../Tests/Fixtures/VirtualClassMagicCall.php | 18 - .../Debug/Tests/Fixtures/VirtualInterface.php | 34 - .../Tests/Fixtures/VirtualSubInterface.php | 10 - .../Debug/Tests/Fixtures/VirtualTrait.php | 10 - .../Debug/Tests/Fixtures/casemismatch.php | 7 - .../Debug/Tests/Fixtures/notPsr0Bis.php | 7 - .../Tests/Fixtures/psr4/Psr4CaseMismatch.php | 7 - .../Debug/Tests/Fixtures/reallyNotPsr0.php | 7 - .../Debug/Tests/Fixtures2/RequiredTwice.php | 7 - .../Component/Debug/Tests/HeaderMock.php | 38 - .../Debug/Tests/MockExceptionHandler.php | 24 - .../Debug/Tests/phpt/debug_class_loader.phpt | 28 - .../Tests/phpt/decorate_exception_hander.phpt | 48 -- .../Debug/Tests/phpt/exception_rethrown.phpt | 35 - .../phpt/fatal_with_nested_handlers.phpt | 43 -- src/Symfony/Component/Debug/composer.json | 40 - src/Symfony/Component/Debug/phpunit.xml.dist | 33 - 71 files changed, 5803 deletions(-) delete mode 100644 src/Symfony/Component/Debug/.gitignore delete mode 100644 src/Symfony/Component/Debug/BufferingLogger.php delete mode 100644 src/Symfony/Component/Debug/CHANGELOG.md delete mode 100644 src/Symfony/Component/Debug/Debug.php delete mode 100644 src/Symfony/Component/Debug/DebugClassLoader.php delete mode 100644 src/Symfony/Component/Debug/ErrorHandler.php delete mode 100644 src/Symfony/Component/Debug/Exception/ClassNotFoundException.php delete mode 100644 src/Symfony/Component/Debug/Exception/FatalErrorException.php delete mode 100644 src/Symfony/Component/Debug/Exception/FatalThrowableError.php delete mode 100644 src/Symfony/Component/Debug/Exception/FlattenException.php delete mode 100644 src/Symfony/Component/Debug/Exception/OutOfMemoryException.php delete mode 100644 src/Symfony/Component/Debug/Exception/SilencedErrorContext.php delete mode 100644 src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php delete mode 100644 src/Symfony/Component/Debug/Exception/UndefinedMethodException.php delete mode 100644 src/Symfony/Component/Debug/ExceptionHandler.php delete mode 100644 src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php delete mode 100644 src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php delete mode 100644 src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php delete mode 100644 src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php delete mode 100644 src/Symfony/Component/Debug/LICENSE delete mode 100644 src/Symfony/Component/Debug/README.md delete mode 100644 src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php delete mode 100644 src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php delete mode 100644 src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php delete mode 100644 src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php delete mode 100644 src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php delete mode 100644 src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php delete mode 100644 src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/AnnotatedClass.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/ClassAlias.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/ClassWithAnnotatedParameters.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/DefinitionInEvaluatedCode.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/DeprecatedClass.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/DeprecatedInterface.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/ErrorHandlerThatUsesThePreviousOne.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/ExtendedFinalMethod.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/FinalClasses.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod2Trait.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/InterfaceWithAnnotatedParameters.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/InternalClass.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/InternalInterface.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/InternalTrait.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/InternalTrait2.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/LoggerThatSetAnErrorHandler.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/NonDeprecatedInterface.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/PEARClass.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/SubClassWithAnnotatedParameters.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/Throwing.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/ToStringThrower.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/TraitWithAnnotatedParameters.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/TraitWithInternalMethod.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/VirtualClass.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/VirtualClassMagicCall.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/VirtualInterface.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/VirtualSubInterface.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/VirtualTrait.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/casemismatch.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/notPsr0Bis.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/psr4/Psr4CaseMismatch.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures/reallyNotPsr0.php delete mode 100644 src/Symfony/Component/Debug/Tests/Fixtures2/RequiredTwice.php delete mode 100644 src/Symfony/Component/Debug/Tests/HeaderMock.php delete mode 100644 src/Symfony/Component/Debug/Tests/MockExceptionHandler.php delete mode 100644 src/Symfony/Component/Debug/Tests/phpt/debug_class_loader.phpt delete mode 100644 src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt delete mode 100644 src/Symfony/Component/Debug/Tests/phpt/exception_rethrown.phpt delete mode 100644 src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt delete mode 100644 src/Symfony/Component/Debug/composer.json delete mode 100644 src/Symfony/Component/Debug/phpunit.xml.dist diff --git a/composer.json b/composer.json index 2e6fd64f1795..b45299e83b5f 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,6 @@ "symfony/console": "self.version", "symfony/css-selector": "self.version", "symfony/dependency-injection": "self.version", - "symfony/debug": "self.version", "symfony/debug-bundle": "self.version", "symfony/doctrine-bridge": "self.version", "symfony/dom-crawler": "self.version", diff --git a/src/Symfony/Component/Debug/.gitignore b/src/Symfony/Component/Debug/.gitignore deleted file mode 100644 index c49a5d8df5c6..000000000000 --- a/src/Symfony/Component/Debug/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/src/Symfony/Component/Debug/BufferingLogger.php b/src/Symfony/Component/Debug/BufferingLogger.php deleted file mode 100644 index 6e308f224728..000000000000 --- a/src/Symfony/Component/Debug/BufferingLogger.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -use Psr\Log\AbstractLogger; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', BufferingLogger::class, \Symfony\Component\ErrorHandler\BufferingLogger::class), E_USER_DEPRECATED); - -/** - * A buffering logger that stacks logs for later. - * - * @author Nicolas Grekas - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\BufferingLogger instead. - */ -class BufferingLogger extends AbstractLogger -{ - private $logs = []; - - public function log($level, $message, array $context = []) - { - $this->logs[] = [$level, $message, $context]; - } - - public function cleanLogs() - { - $logs = $this->logs; - $this->logs = []; - - return $logs; - } -} diff --git a/src/Symfony/Component/Debug/CHANGELOG.md b/src/Symfony/Component/Debug/CHANGELOG.md deleted file mode 100644 index b2e17f38211a..000000000000 --- a/src/Symfony/Component/Debug/CHANGELOG.md +++ /dev/null @@ -1,84 +0,0 @@ -CHANGELOG -========= - -4.4.0 ------ - - * deprecated `FlattenException`, use the `FlattenException` of the `ErrorRenderer` component - * deprecated the whole component in favor of the `ErrorHandler` component - -4.3.0 ------ - -* made the `ErrorHandler` and `ExceptionHandler` classes final -* added `Exception\FlattenException::getAsString` and -`Exception\FlattenException::getTraceAsString` to increase compatibility to php -exception objects - -4.0.0 ------ - -* removed the symfony_debug extension -* removed `ContextErrorException` - -3.4.0 ------ - -* deprecated `ErrorHandler::stackErrors()` and `ErrorHandler::unstackErrors()` - -3.3.0 ------ - -* deprecated the `ContextErrorException` class: use \ErrorException directly now - -3.2.0 ------ - -* `FlattenException::getTrace()` now returns additional type descriptions - `integer` and `float`. - - -3.0.0 ------ - -* removed classes, methods and interfaces deprecated in 2.x - -2.8.0 ------ - -* added BufferingLogger for errors that happen before a proper logger is configured -* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);` -* deprecate ExceptionHandler::createResponse - -2.7.0 ------ - -* added deprecations checking for parent interfaces/classes to DebugClassLoader -* added ZTS support to symfony_debug extension -* added symfony_debug_backtrace() to symfony_debug extension - to track the backtrace of fatal errors - -2.6.0 ------ - -* generalized ErrorHandler and ExceptionHandler, - with some new methods and others deprecated -* enhanced error messages for uncaught exceptions - -2.5.0 ------ - -* added ExceptionHandler::setHandler() -* added UndefinedMethodFatalErrorHandler -* deprecated DummyException - -2.4.0 ------ - - * added a DebugClassLoader able to wrap any autoloader providing a findFile method - * improved error messages for not found classes and functions - -2.3.0 ------ - - * added the component diff --git a/src/Symfony/Component/Debug/Debug.php b/src/Symfony/Component/Debug/Debug.php deleted file mode 100644 index 788ad7d9243f..000000000000 --- a/src/Symfony/Component/Debug/Debug.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', Debug::class, \Symfony\Component\ErrorHandler\Debug::class), E_USER_DEPRECATED); - -/** - * Registers all the debug tools. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Debug instead. - */ -class Debug -{ - private static $enabled = false; - - /** - * Enables the debug tools. - * - * This method registers an error handler and an exception handler. - * - * @param int $errorReportingLevel The level of error reporting you want - * @param bool $displayErrors Whether to display errors (for development) or just log them (for production) - */ - public static function enable($errorReportingLevel = E_ALL, $displayErrors = true) - { - if (static::$enabled) { - return; - } - - static::$enabled = true; - - if (null !== $errorReportingLevel) { - error_reporting($errorReportingLevel); - } else { - error_reporting(E_ALL); - } - - if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { - ini_set('display_errors', 0); - ExceptionHandler::register(); - } elseif ($displayErrors && (!filter_var(ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN) || ini_get('error_log'))) { - // CLI - display errors only if they're not already logged to STDERR - ini_set('display_errors', 1); - } - if ($displayErrors) { - ErrorHandler::register(new ErrorHandler(new BufferingLogger())); - } else { - ErrorHandler::register()->throwAt(0, true); - } - - DebugClassLoader::enable(); - } -} diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php deleted file mode 100644 index f422e557267f..000000000000 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ /dev/null @@ -1,529 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', DebugClassLoader::class, \Symfony\Component\ErrorHandler\DebugClassLoader::class), E_USER_DEPRECATED); - -/** - * Autoloader checking if the class is really defined in the file found. - * - * The ClassLoader will wrap all registered autoloaders - * and will throw an exception if a file is found but does - * not declare the class. - * - * @author Fabien Potencier - * @author Christophe Coevoet - * @author Nicolas Grekas - * @author Guilhem Niot - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\DebugClassLoader instead. - */ -class DebugClassLoader -{ - private $classLoader; - private $isFinder; - private $loaded = []; - private static $caseCheck; - private static $checkedClasses = []; - private static $final = []; - private static $finalMethods = []; - private static $deprecated = []; - private static $internal = []; - private static $internalMethods = []; - private static $annotatedParameters = []; - private static $darwinCache = ['/' => ['/', []]]; - private static $method = []; - - public function __construct(callable $classLoader) - { - $this->classLoader = $classLoader; - $this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile'); - - if (!isset(self::$caseCheck)) { - $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR); - $i = strrpos($file, \DIRECTORY_SEPARATOR); - $dir = substr($file, 0, 1 + $i); - $file = substr($file, 1 + $i); - $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); - $test = realpath($dir.$test); - - if (false === $test || false === $i) { - // filesystem is case sensitive - self::$caseCheck = 0; - } elseif (substr($test, -\strlen($file)) === $file) { - // filesystem is case insensitive and realpath() normalizes the case of characters - self::$caseCheck = 1; - } elseif (false !== stripos(PHP_OS, 'darwin')) { - // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters - self::$caseCheck = 2; - } else { - // filesystem case checks failed, fallback to disabling them - self::$caseCheck = 0; - } - } - } - - /** - * Gets the wrapped class loader. - * - * @return callable The wrapped class loader - */ - public function getClassLoader() - { - return $this->classLoader; - } - - /** - * Wraps all autoloaders. - */ - public static function enable() - { - // Ensures we don't hit https://bugs.php.net/42098 - class_exists('Symfony\Component\Debug\ErrorHandler'); - class_exists('Psr\Log\LogLevel'); - - if (!\is_array($functions = spl_autoload_functions())) { - return; - } - - foreach ($functions as $function) { - spl_autoload_unregister($function); - } - - foreach ($functions as $function) { - if (!\is_array($function) || !$function[0] instanceof self) { - $function = [new static($function), 'loadClass']; - } - - spl_autoload_register($function); - } - } - - /** - * Disables the wrapping. - */ - public static function disable() - { - if (!\is_array($functions = spl_autoload_functions())) { - return; - } - - foreach ($functions as $function) { - spl_autoload_unregister($function); - } - - foreach ($functions as $function) { - if (\is_array($function) && $function[0] instanceof self) { - $function = $function[0]->getClassLoader(); - } - - spl_autoload_register($function); - } - } - - /** - * @return string|null - */ - public function findFile($class) - { - return $this->isFinder ? $this->classLoader[0]->findFile($class) ?: null : null; - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @throws \RuntimeException - */ - public function loadClass($class) - { - $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); - - try { - if ($this->isFinder && !isset($this->loaded[$class])) { - $this->loaded[$class] = true; - if (!$file = $this->classLoader[0]->findFile($class) ?: false) { - // no-op - } elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) { - include $file; - - return; - } elseif (false === include $file) { - return; - } - } else { - ($this->classLoader)($class); - $file = false; - } - } finally { - error_reporting($e); - } - - $this->checkClass($class, $file); - } - - private function checkClass($class, $file = null) - { - $exists = null === $file || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); - - if (null !== $file && $class && '\\' === $class[0]) { - $class = substr($class, 1); - } - - if ($exists) { - if (isset(self::$checkedClasses[$class])) { - return; - } - self::$checkedClasses[$class] = true; - - $refl = new \ReflectionClass($class); - if (null === $file && $refl->isInternal()) { - return; - } - $name = $refl->getName(); - - if ($name !== $class && 0 === strcasecmp($name, $class)) { - throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); - } - - $deprecations = $this->checkAnnotations($refl, $name); - - foreach ($deprecations as $message) { - @trigger_error($message, E_USER_DEPRECATED); - } - } - - if (!$file) { - return; - } - - if (!$exists) { - if (false !== strpos($class, '/')) { - throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); - } - - throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); - } - - if (self::$caseCheck && $message = $this->checkCase($refl, $file, $class)) { - throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2])); - } - } - - public function checkAnnotations(\ReflectionClass $refl, $class) - { - $deprecations = []; - - // Don't trigger deprecations for classes in the same vendor - if (2 > $len = 1 + (strpos($class, '\\') ?: strpos($class, '_'))) { - $len = 0; - $ns = ''; - } else { - $ns = str_replace('_', '\\', substr($class, 0, $len)); - } - - // Detect annotations on the class - if (false !== $doc = $refl->getDocComment()) { - foreach (['final', 'deprecated', 'internal'] as $annotation) { - if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) { - self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; - } - } - - if ($refl->isInterface() && false !== strpos($doc, 'method') && preg_match_all('#\n \* @method\s+(static\s+)?+(?:[\w\|&\[\]\\\]+\s+)?(\w+(?:\s*\([^\)]*\))?)+(.+?([[:punct:]]\s*)?)?(?=\r?\n \*(?: @|/$|\r?\n))#', $doc, $notice, PREG_SET_ORDER)) { - foreach ($notice as $method) { - $static = '' !== $method[1]; - $name = $method[2]; - $description = $method[3] ?? null; - if (false === strpos($name, '(')) { - $name .= '()'; - } - if (null !== $description) { - $description = trim($description); - if (!isset($method[4])) { - $description .= '.'; - } - } - self::$method[$class][] = [$class, $name, $static, $description]; - } - } - } - - $parent = get_parent_class($class); - $parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent); - if ($parent) { - $parentAndOwnInterfaces[$parent] = $parent; - - if (!isset(self::$checkedClasses[$parent])) { - $this->checkClass($parent); - } - - if (isset(self::$final[$parent])) { - $deprecations[] = sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $class); - } - } - - // Detect if the parent is annotated - foreach ($parentAndOwnInterfaces + class_uses($class, false) as $use) { - if (!isset(self::$checkedClasses[$use])) { - $this->checkClass($use); - } - if (isset(self::$deprecated[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len) && !isset(self::$deprecated[$class])) { - $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); - $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); - - $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]); - } - if (isset(self::$internal[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len)) { - $deprecations[] = sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $class); - } - if (isset(self::$method[$use])) { - if ($refl->isAbstract()) { - if (isset(self::$method[$class])) { - self::$method[$class] = array_merge(self::$method[$class], self::$method[$use]); - } else { - self::$method[$class] = self::$method[$use]; - } - } elseif (!$refl->isInterface()) { - $hasCall = $refl->hasMethod('__call'); - $hasStaticCall = $refl->hasMethod('__callStatic'); - foreach (self::$method[$use] as $method) { - list($interface, $name, $static, $description) = $method; - if ($static ? $hasStaticCall : $hasCall) { - continue; - } - $realName = substr($name, 0, strpos($name, '(')); - if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static && !$methodRefl->isStatic()) || (!$static && $methodRefl->isStatic())) { - $deprecations[] = sprintf('Class "%s" should implement method "%s::%s"%s', $class, ($static ? 'static ' : '').$interface, $name, null == $description ? '.' : ': '.$description); - } - } - } - } - } - - if (trait_exists($class)) { - return $deprecations; - } - - // Inherit @final, @internal and @param annotations for methods - self::$finalMethods[$class] = []; - self::$internalMethods[$class] = []; - self::$annotatedParameters[$class] = []; - foreach ($parentAndOwnInterfaces as $use) { - foreach (['finalMethods', 'internalMethods', 'annotatedParameters'] as $property) { - if (isset(self::${$property}[$use])) { - self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use]; - } - } - } - - foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { - if ($method->class !== $class) { - continue; - } - - if ($parent && isset(self::$finalMethods[$parent][$method->name])) { - list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; - $deprecations[] = sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $class); - } - - if (isset(self::$internalMethods[$class][$method->name])) { - list($declaringClass, $message) = self::$internalMethods[$class][$method->name]; - if (strncmp($ns, $declaringClass, $len)) { - $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $class); - } - } - - // To read method annotations - $doc = $method->getDocComment(); - - if (isset(self::$annotatedParameters[$class][$method->name])) { - $definedParameters = []; - foreach ($method->getParameters() as $parameter) { - $definedParameters[$parameter->name] = true; - } - - foreach (self::$annotatedParameters[$class][$method->name] as $parameterName => $deprecation) { - if (!isset($definedParameters[$parameterName]) && !($doc && preg_match("/\\n\\s+\\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\\\${$parameterName}\\b/", $doc))) { - $deprecations[] = sprintf($deprecation, $class); - } - } - } - - if (!$doc) { - continue; - } - - $finalOrInternal = false; - - foreach (['final', 'internal'] as $annotation) { - if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) { - $message = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; - self::${$annotation.'Methods'}[$class][$method->name] = [$class, $message]; - $finalOrInternal = true; - } - } - - if ($finalOrInternal || $method->isConstructor() || false === strpos($doc, '@param') || StatelessInvocation::class === $class) { - continue; - } - if (!preg_match_all('#\n\s+\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\$([a-zA-Z0-9_\x7f-\xff]++)#', $doc, $matches, PREG_SET_ORDER)) { - continue; - } - if (!isset(self::$annotatedParameters[$class][$method->name])) { - $definedParameters = []; - foreach ($method->getParameters() as $parameter) { - $definedParameters[$parameter->name] = true; - } - } - foreach ($matches as list(, $parameterType, $parameterName)) { - if (!isset($definedParameters[$parameterName])) { - $parameterType = trim($parameterType); - self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its parent class "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, $method->class); - } - } - } - - return $deprecations; - } - - public function checkCase(\ReflectionClass $refl, $file, $class) - { - $real = explode('\\', $class.strrchr($file, '.')); - $tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file)); - - $i = \count($tail) - 1; - $j = \count($real) - 1; - - while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { - --$i; - --$j; - } - - array_splice($tail, 0, $i + 1); - - if (!$tail) { - return; - } - - $tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail); - $tailLen = \strlen($tail); - $real = $refl->getFileName(); - - if (2 === self::$caseCheck) { - $real = $this->darwinRealpath($real); - } - - if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) - && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) - ) { - return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)]; - } - } - - /** - * `realpath` on MacOSX doesn't normalize the case of characters. - */ - private function darwinRealpath($real) - { - $i = 1 + strrpos($real, '/'); - $file = substr($real, $i); - $real = substr($real, 0, $i); - - if (isset(self::$darwinCache[$real])) { - $kDir = $real; - } else { - $kDir = strtolower($real); - - if (isset(self::$darwinCache[$kDir])) { - $real = self::$darwinCache[$kDir][0]; - } else { - $dir = getcwd(); - chdir($real); - $real = getcwd().'/'; - chdir($dir); - - $dir = $real; - $k = $kDir; - $i = \strlen($dir) - 1; - while (!isset(self::$darwinCache[$k])) { - self::$darwinCache[$k] = [$dir, []]; - self::$darwinCache[$dir] = &self::$darwinCache[$k]; - - while ('/' !== $dir[--$i]) { - } - $k = substr($k, 0, ++$i); - $dir = substr($dir, 0, $i--); - } - } - } - - $dirFiles = self::$darwinCache[$kDir][1]; - - if (!isset($dirFiles[$file]) && ') : eval()\'d code' === substr($file, -17)) { - // Get the file name from "file_name.php(123) : eval()'d code" - $file = substr($file, 0, strrpos($file, '(', -17)); - } - - if (isset($dirFiles[$file])) { - return $real .= $dirFiles[$file]; - } - - $kFile = strtolower($file); - - if (!isset($dirFiles[$kFile])) { - foreach (scandir($real, 2) as $f) { - if ('.' !== $f[0]) { - $dirFiles[$f] = $f; - if ($f === $file) { - $kFile = $k = $file; - } elseif ($f !== $k = strtolower($f)) { - $dirFiles[$k] = $f; - } - } - } - self::$darwinCache[$kDir][1] = $dirFiles; - } - - return $real .= $dirFiles[$kFile]; - } - - /** - * `class_implements` includes interfaces from the parents so we have to manually exclude them. - * - * @param string $class - * @param string|false $parent - * - * @return string[] - */ - private function getOwnInterfaces($class, $parent) - { - $ownInterfaces = class_implements($class, false); - - if ($parent) { - foreach (class_implements($parent, false) as $interface) { - unset($ownInterfaces[$interface]); - } - } - - foreach ($ownInterfaces as $interface) { - foreach (class_implements($interface) as $interface) { - unset($ownInterfaces[$interface]); - } - } - - return $ownInterfaces; - } -} diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php deleted file mode 100644 index 0134a7142305..000000000000 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ /dev/null @@ -1,719 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\Exception\FatalThrowableError; -use Symfony\Component\Debug\Exception\FlattenException; -use Symfony\Component\Debug\Exception\OutOfMemoryException; -use Symfony\Component\Debug\Exception\SilencedErrorContext; -use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; -use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ErrorHandler::class, \Symfony\Component\ErrorHandler\ErrorHandler::class), E_USER_DEPRECATED); - -/** - * A generic ErrorHandler for the PHP engine. - * - * Provides five bit fields that control how errors are handled: - * - thrownErrors: errors thrown as \ErrorException - * - loggedErrors: logged errors, when not @-silenced - * - scopedErrors: errors thrown or logged with their local context - * - tracedErrors: errors logged with their stack trace - * - screamedErrors: never @-silenced errors - * - * Each error level can be logged by a dedicated PSR-3 logger object. - * Screaming only applies to logging. - * Throwing takes precedence over logging. - * Uncaught exceptions are logged as E_ERROR. - * E_DEPRECATED and E_USER_DEPRECATED levels never throw. - * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. - * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. - * As errors have a performance cost, repeated errors are all logged, so that the developer - * can see them and weight them as more important to fix than others of the same level. - * - * @author Nicolas Grekas - * @author Grégoire Pineau - * - * @final since Symfony 4.3 - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ErrorHandler instead. - */ -class ErrorHandler -{ - private $levels = [ - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'User Deprecated', - E_NOTICE => 'Notice', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Runtime Notice', - E_WARNING => 'Warning', - E_USER_WARNING => 'User Warning', - E_COMPILE_WARNING => 'Compile Warning', - E_CORE_WARNING => 'Core Warning', - E_USER_ERROR => 'User Error', - E_RECOVERABLE_ERROR => 'Catchable Fatal Error', - E_COMPILE_ERROR => 'Compile Error', - E_PARSE => 'Parse Error', - E_ERROR => 'Error', - E_CORE_ERROR => 'Core Error', - ]; - - private $loggers = [ - E_DEPRECATED => [null, LogLevel::INFO], - E_USER_DEPRECATED => [null, LogLevel::INFO], - E_NOTICE => [null, LogLevel::WARNING], - E_USER_NOTICE => [null, LogLevel::WARNING], - E_STRICT => [null, LogLevel::WARNING], - E_WARNING => [null, LogLevel::WARNING], - E_USER_WARNING => [null, LogLevel::WARNING], - E_COMPILE_WARNING => [null, LogLevel::WARNING], - E_CORE_WARNING => [null, LogLevel::WARNING], - E_USER_ERROR => [null, LogLevel::CRITICAL], - E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], - E_COMPILE_ERROR => [null, LogLevel::CRITICAL], - E_PARSE => [null, LogLevel::CRITICAL], - E_ERROR => [null, LogLevel::CRITICAL], - E_CORE_ERROR => [null, LogLevel::CRITICAL], - ]; - - private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED - private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED - private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE - private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE - private $loggedErrors = 0; - private $traceReflector; - - private $isRecursive = 0; - private $isRoot = false; - private $exceptionHandler; - private $bootstrappingLogger; - - private static $reservedMemory; - private static $toStringException = null; - private static $silencedErrorCache = []; - private static $silencedErrorCount = 0; - private static $exitCode = 0; - - /** - * Registers the error handler. - * - * @param self|null $handler The handler to register - * @param bool $replace Whether to replace or not any existing handler - * - * @return self The registered error handler - */ - public static function register(self $handler = null, $replace = true) - { - if (null === self::$reservedMemory) { - self::$reservedMemory = str_repeat('x', 10240); - register_shutdown_function(__CLASS__.'::handleFatalError'); - } - - if ($handlerIsNew = null === $handler) { - $handler = new static(); - } - - if (null === $prev = set_error_handler([$handler, 'handleError'])) { - restore_error_handler(); - // Specifying the error types earlier would expose us to https://bugs.php.net/63206 - set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors); - $handler->isRoot = true; - } - - if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) { - $handler = $prev[0]; - $replace = false; - } - if (!$replace && $prev) { - restore_error_handler(); - $handlerIsRegistered = \is_array($prev) && $handler === $prev[0]; - } else { - $handlerIsRegistered = true; - } - if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) { - restore_exception_handler(); - if (!$handlerIsRegistered) { - $handler = $prev[0]; - } elseif ($handler !== $prev[0] && $replace) { - set_exception_handler([$handler, 'handleException']); - $p = $prev[0]->setExceptionHandler(null); - $handler->setExceptionHandler($p); - $prev[0]->setExceptionHandler($p); - } - } else { - $handler->setExceptionHandler($prev); - } - - $handler->throwAt(E_ALL & $handler->thrownErrors, true); - - return $handler; - } - - public function __construct(BufferingLogger $bootstrappingLogger = null) - { - if ($bootstrappingLogger) { - $this->bootstrappingLogger = $bootstrappingLogger; - $this->setDefaultLogger($bootstrappingLogger); - } - $this->traceReflector = new \ReflectionProperty('Exception', 'trace'); - $this->traceReflector->setAccessible(true); - } - - /** - * Sets a logger to non assigned errors levels. - * - * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels - * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants - * @param bool $replace Whether to replace or not any existing logger - */ - public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false) - { - $loggers = []; - - if (\is_array($levels)) { - foreach ($levels as $type => $logLevel) { - if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { - $loggers[$type] = [$logger, $logLevel]; - } - } - } else { - if (null === $levels) { - $levels = E_ALL; - } - foreach ($this->loggers as $type => $log) { - if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { - $log[0] = $logger; - $loggers[$type] = $log; - } - } - } - - $this->setLoggers($loggers); - } - - /** - * Sets a logger for each error level. - * - * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map - * - * @return array The previous map - * - * @throws \InvalidArgumentException - */ - public function setLoggers(array $loggers) - { - $prevLogged = $this->loggedErrors; - $prev = $this->loggers; - $flush = []; - - foreach ($loggers as $type => $log) { - if (!isset($prev[$type])) { - throw new \InvalidArgumentException('Unknown error type: '.$type); - } - if (!\is_array($log)) { - $log = [$log]; - } elseif (!\array_key_exists(0, $log)) { - throw new \InvalidArgumentException('No logger provided'); - } - if (null === $log[0]) { - $this->loggedErrors &= ~$type; - } elseif ($log[0] instanceof LoggerInterface) { - $this->loggedErrors |= $type; - } else { - throw new \InvalidArgumentException('Invalid logger provided'); - } - $this->loggers[$type] = $log + $prev[$type]; - - if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { - $flush[$type] = $type; - } - } - $this->reRegister($prevLogged | $this->thrownErrors); - - if ($flush) { - foreach ($this->bootstrappingLogger->cleanLogs() as $log) { - $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR; - if (!isset($flush[$type])) { - $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); - } elseif ($this->loggers[$type][0]) { - $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); - } - } - } - - return $prev; - } - - /** - * Sets a user exception handler. - * - * @param callable $handler A handler that will be called on Exception - * - * @return callable|null The previous exception handler - */ - public function setExceptionHandler(callable $handler = null) - { - $prev = $this->exceptionHandler; - $this->exceptionHandler = $handler; - - return $prev; - } - - /** - * Sets the PHP error levels that throw an exception when a PHP error occurs. - * - * @param int $levels A bit field of E_* constants for thrown errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function throwAt($levels, $replace = false) - { - $prev = $this->thrownErrors; - $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED; - if (!$replace) { - $this->thrownErrors |= $prev; - } - $this->reRegister($prev | $this->loggedErrors); - - return $prev; - } - - /** - * Sets the PHP error levels for which local variables are preserved. - * - * @param int $levels A bit field of E_* constants for scoped errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function scopeAt($levels, $replace = false) - { - $prev = $this->scopedErrors; - $this->scopedErrors = (int) $levels; - if (!$replace) { - $this->scopedErrors |= $prev; - } - - return $prev; - } - - /** - * Sets the PHP error levels for which the stack trace is preserved. - * - * @param int $levels A bit field of E_* constants for traced errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function traceAt($levels, $replace = false) - { - $prev = $this->tracedErrors; - $this->tracedErrors = (int) $levels; - if (!$replace) { - $this->tracedErrors |= $prev; - } - - return $prev; - } - - /** - * Sets the error levels where the @-operator is ignored. - * - * @param int $levels A bit field of E_* constants for screamed errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function screamAt($levels, $replace = false) - { - $prev = $this->screamedErrors; - $this->screamedErrors = (int) $levels; - if (!$replace) { - $this->screamedErrors |= $prev; - } - - return $prev; - } - - /** - * Re-registers as a PHP error handler if levels changed. - */ - private function reRegister($prev) - { - if ($prev !== $this->thrownErrors | $this->loggedErrors) { - $handler = set_error_handler('var_dump'); - $handler = \is_array($handler) ? $handler[0] : null; - restore_error_handler(); - if ($handler === $this) { - restore_error_handler(); - if ($this->isRoot) { - set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors); - } else { - set_error_handler([$this, 'handleError']); - } - } - } - } - - /** - * Handles errors by filtering then logging them according to the configured bit fields. - * - * @param int $type One of the E_* constants - * @param string $message - * @param string $file - * @param int $line - * - * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself - * - * @throws \ErrorException When $this->thrownErrors requests so - * - * @internal - */ - public function handleError($type, $message, $file, $line) - { - // @deprecated to be removed in Symfony 5.0 - if (\PHP_VERSION_ID >= 70300 && $message && '"' === $message[0] && 0 === strpos($message, '"continue') && preg_match('/^"continue(?: \d++)?" targeting switch is equivalent to "break(?: \d++)?"\. Did you mean to use "continue(?: \d++)?"\?$/', $message)) { - $type = E_DEPRECATED; - } - - // Level is the current error reporting level to manage silent error. - $level = error_reporting(); - $silenced = 0 === ($level & $type); - // Strong errors are not authorized to be silenced. - $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; - $log = $this->loggedErrors & $type; - $throw = $this->thrownErrors & $type & $level; - $type &= $level | $this->screamedErrors; - - if (!$type || (!$log && !$throw)) { - return !$silenced && $type && $log; - } - $scope = $this->scopedErrors & $type; - - if (4 < $numArgs = \func_num_args()) { - $context = $scope ? (func_get_arg(4) ?: []) : []; - } else { - $context = []; - } - - if (isset($context['GLOBALS']) && $scope) { - $e = $context; // Whatever the signature of the method, - unset($e['GLOBALS'], $context); // $context is always a reference in 5.3 - $context = $e; - } - - if (false !== strpos($message, "class@anonymous\0")) { - $logMessage = $this->levels[$type].': '.(new FlattenException())->setMessage($message)->getMessage(); - } else { - $logMessage = $this->levels[$type].': '.$message; - } - - if (null !== self::$toStringException) { - $errorAsException = self::$toStringException; - self::$toStringException = null; - } elseif (!$throw && !($type & $level)) { - if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) { - $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5), $type, $file, $line, false) : []; - $errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace); - } elseif (isset(self::$silencedErrorCache[$id][$message])) { - $lightTrace = null; - $errorAsException = self::$silencedErrorCache[$id][$message]; - ++$errorAsException->count; - } else { - $lightTrace = []; - $errorAsException = null; - } - - if (100 < ++self::$silencedErrorCount) { - self::$silencedErrorCache = $lightTrace = []; - self::$silencedErrorCount = 1; - } - if ($errorAsException) { - self::$silencedErrorCache[$id][$message] = $errorAsException; - } - if (null === $lightTrace) { - return; - } - } else { - $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); - - if ($throw || $this->tracedErrors & $type) { - $backtrace = $errorAsException->getTrace(); - $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw); - $this->traceReflector->setValue($errorAsException, $lightTrace); - } else { - $this->traceReflector->setValue($errorAsException, []); - $backtrace = []; - } - } - - if ($throw) { - if (E_USER_ERROR & $type) { - for ($i = 1; isset($backtrace[$i]); ++$i) { - if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) - && '__toString' === $backtrace[$i]['function'] - && '->' === $backtrace[$i]['type'] - && !isset($backtrace[$i - 1]['class']) - && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) - ) { - // Here, we know trigger_error() has been called from __toString(). - // PHP triggers a fatal error when throwing from __toString(). - // A small convention allows working around the limitation: - // given a caught $e exception in __toString(), quitting the method with - // `return trigger_error($e, E_USER_ERROR);` allows this error handler - // to make $e get through the __toString() barrier. - - foreach ($context as $e) { - if ($e instanceof \Throwable && $e->__toString() === $message) { - self::$toStringException = $e; - - return true; - } - } - - // Display the original error message instead of the default one. - $this->handleException($errorAsException); - - // Stop the process by giving back the error to the native handler. - return false; - } - } - } - - throw $errorAsException; - } - - if ($this->isRecursive) { - $log = 0; - } else { - if (!\defined('HHVM_VERSION')) { - $currentErrorHandler = set_error_handler('var_dump'); - restore_error_handler(); - } - - try { - $this->isRecursive = true; - $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; - $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []); - } finally { - $this->isRecursive = false; - - if (!\defined('HHVM_VERSION')) { - set_error_handler($currentErrorHandler); - } - } - } - - return !$silenced && $type && $log; - } - - /** - * Handles an exception by logging then forwarding it to another handler. - * - * @param \Exception|\Throwable $exception An exception to handle - * @param array $error An array as returned by error_get_last() - * - * @internal - */ - public function handleException($exception, array $error = null) - { - if (null === $error) { - self::$exitCode = 255; - } - if (!$exception instanceof \Exception) { - $exception = new FatalThrowableError($exception); - } - $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR; - $handlerException = null; - - if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) { - if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) { - $message = (new FlattenException())->setMessage($message)->getMessage(); - } - if ($exception instanceof FatalErrorException) { - if ($exception instanceof FatalThrowableError) { - $error = [ - 'type' => $type, - 'message' => $message, - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), - ]; - } else { - $message = 'Fatal '.$message; - } - } elseif ($exception instanceof \ErrorException) { - $message = 'Uncaught '.$message; - } else { - $message = 'Uncaught Exception: '.$message; - } - } - if ($this->loggedErrors & $type) { - try { - $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]); - } catch (\Throwable $handlerException) { - } - } - if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { - foreach ($this->getFatalErrorHandlers() as $handler) { - if ($e = $handler->handleError($error, $exception)) { - $exception = $e; - break; - } - } - } - $exceptionHandler = $this->exceptionHandler; - $this->exceptionHandler = null; - try { - if (null !== $exceptionHandler) { - return $exceptionHandler($exception); - } - $handlerException = $handlerException ?: $exception; - } catch (\Throwable $handlerException) { - } - if ($exception === $handlerException) { - self::$reservedMemory = null; // Disable the fatal error handler - throw $exception; // Give back $exception to the native handler - } - $this->handleException($handlerException); - } - - /** - * Shutdown registered function for handling PHP fatal errors. - * - * @param array $error An array as returned by error_get_last() - * - * @internal - */ - public static function handleFatalError(array $error = null) - { - if (null === self::$reservedMemory) { - return; - } - - $handler = self::$reservedMemory = null; - $handlers = []; - $previousHandler = null; - $sameHandlerLimit = 10; - - while (!\is_array($handler) || !$handler[0] instanceof self) { - $handler = set_exception_handler('var_dump'); - restore_exception_handler(); - - if (!$handler) { - break; - } - restore_exception_handler(); - - if ($handler !== $previousHandler) { - array_unshift($handlers, $handler); - $previousHandler = $handler; - } elseif (0 === --$sameHandlerLimit) { - $handler = null; - break; - } - } - foreach ($handlers as $h) { - set_exception_handler($h); - } - if (!$handler) { - return; - } - if ($handler !== $h) { - $handler[0]->setExceptionHandler($h); - } - $handler = $handler[0]; - $handlers = []; - - if ($exit = null === $error) { - $error = error_get_last(); - } - - if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { - // Let's not throw anymore but keep logging - $handler->throwAt(0, true); - $trace = isset($error['backtrace']) ? $error['backtrace'] : null; - - if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { - $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace); - } else { - $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); - } - } else { - $exception = null; - } - - try { - if (null !== $exception) { - self::$exitCode = 255; - $handler->handleException($exception, $error); - } - } catch (FatalErrorException $e) { - // Ignore this re-throw - } - - if ($exit && self::$exitCode) { - $exitCode = self::$exitCode; - register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); - } - } - - /** - * Gets the fatal error handlers. - * - * Override this method if you want to define more fatal error handlers. - * - * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface - */ - protected function getFatalErrorHandlers() - { - return [ - new UndefinedFunctionFatalErrorHandler(), - new UndefinedMethodFatalErrorHandler(), - new ClassNotFoundFatalErrorHandler(), - ]; - } - - /** - * Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader. - */ - private function cleanTrace($backtrace, $type, $file, $line, $throw) - { - $lightTrace = $backtrace; - - for ($i = 0; isset($backtrace[$i]); ++$i) { - if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { - $lightTrace = \array_slice($lightTrace, 1 + $i); - break; - } - } - if (class_exists(DebugClassLoader::class, false)) { - for ($i = \count($lightTrace) - 2; 0 < $i; --$i) { - if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) { - array_splice($lightTrace, --$i, 2); - } - } - } - if (!($throw || $this->scopedErrors & $type)) { - for ($i = 0; isset($lightTrace[$i]); ++$i) { - unset($lightTrace[$i]['args'], $lightTrace[$i]['object']); - } - } - - return $lightTrace; - } -} diff --git a/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php b/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php deleted file mode 100644 index 21e2c0db53a9..000000000000 --- a/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ClassNotFoundException::class, \Symfony\Component\ErrorHandler\Exception\ClassNotFoundException::class), E_USER_DEPRECATED); - -/** - * Class (or Trait or Interface) Not Found Exception. - * - * @author Konstanton Myakshin - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\ClassNotFoundException instead. - */ -class ClassNotFoundException extends FatalErrorException -{ - public function __construct(string $message, \ErrorException $previous) - { - parent::__construct( - $message, - $previous->getCode(), - $previous->getSeverity(), - $previous->getFile(), - $previous->getLine(), - null, - true, - null, - $previous->getPrevious() - ); - $this->setTrace($previous->getTrace()); - } -} diff --git a/src/Symfony/Component/Debug/Exception/FatalErrorException.php b/src/Symfony/Component/Debug/Exception/FatalErrorException.php deleted file mode 100644 index 23c2ede7eb96..000000000000 --- a/src/Symfony/Component/Debug/Exception/FatalErrorException.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FatalErrorException::class, \Symfony\Component\ErrorHandler\Exception\FatalErrorException::class), E_USER_DEPRECATED); - -/** - * Fatal Error Exception. - * - * @author Konstanton Myakshin - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\FatalErrorException instead. - */ -class FatalErrorException extends \ErrorException -{ - public function __construct(string $message, int $code, int $severity, string $filename, int $lineno, int $traceOffset = null, bool $traceArgs = true, array $trace = null, \Throwable $previous = null) - { - parent::__construct($message, $code, $severity, $filename, $lineno, $previous); - - if (null !== $trace) { - if (!$traceArgs) { - foreach ($trace as &$frame) { - unset($frame['args'], $frame['this'], $frame); - } - } - - $this->setTrace($trace); - } elseif (null !== $traceOffset) { - if (\function_exists('xdebug_get_function_stack')) { - $trace = xdebug_get_function_stack(); - if (0 < $traceOffset) { - array_splice($trace, -$traceOffset); - } - - foreach ($trace as &$frame) { - if (!isset($frame['type'])) { - // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 - if (isset($frame['class'])) { - $frame['type'] = '::'; - } - } elseif ('dynamic' === $frame['type']) { - $frame['type'] = '->'; - } elseif ('static' === $frame['type']) { - $frame['type'] = '::'; - } - - // XDebug also has a different name for the parameters array - if (!$traceArgs) { - unset($frame['params'], $frame['args']); - } elseif (isset($frame['params']) && !isset($frame['args'])) { - $frame['args'] = $frame['params']; - unset($frame['params']); - } - } - - unset($frame); - $trace = array_reverse($trace); - } else { - $trace = []; - } - - $this->setTrace($trace); - } - } - - protected function setTrace($trace) - { - $traceReflector = new \ReflectionProperty('Exception', 'trace'); - $traceReflector->setAccessible(true); - $traceReflector->setValue($this, $trace); - } -} diff --git a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php b/src/Symfony/Component/Debug/Exception/FatalThrowableError.php deleted file mode 100644 index d7d36ac17e31..000000000000 --- a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FatalThrowableError::class, \Symfony\Component\ErrorHandler\Exception\FatalThrowableError::class), E_USER_DEPRECATED); - -/** - * Fatal Throwable Error. - * - * @author Nicolas Grekas - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\FatalThrowableError instead. - */ -class FatalThrowableError extends FatalErrorException -{ - private $originalClassName; - - public function __construct(\Throwable $e) - { - $this->originalClassName = \get_class($e); - - if ($e instanceof \ParseError) { - $severity = E_PARSE; - } elseif ($e instanceof \TypeError) { - $severity = E_RECOVERABLE_ERROR; - } else { - $severity = E_ERROR; - } - - \ErrorException::__construct( - $e->getMessage(), - $e->getCode(), - $severity, - $e->getFile(), - $e->getLine(), - $e->getPrevious() - ); - - $this->setTrace($e->getTrace()); - } - - public function getOriginalClassName(): string - { - return $this->originalClassName; - } -} diff --git a/src/Symfony/Component/Debug/Exception/FlattenException.php b/src/Symfony/Component/Debug/Exception/FlattenException.php deleted file mode 100644 index f7bdcde9656c..000000000000 --- a/src/Symfony/Component/Debug/Exception/FlattenException.php +++ /dev/null @@ -1,368 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; -use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FlattenException::class, \Symfony\Component\ErrorRenderer\Exception\FlattenException::class), E_USER_DEPRECATED); - -/** - * FlattenException wraps a PHP Error or Exception to be able to serialize it. - * - * Basically, this class removes all objects from the trace. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorRenderer\Exception\FlattenException instead. - */ -class FlattenException -{ - private $message; - private $code; - private $previous; - private $trace; - private $traceAsString; - private $class; - private $statusCode; - private $headers; - private $file; - private $line; - - /** - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorRenderer\Exception::createFromThrowable() instead. - */ - public static function create(\Exception $exception, $statusCode = null, array $headers = []) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.4, use Symfony\Component\ErrorRenderer\Exception::createFromThrowable() instead.', __METHOD__), E_USER_DEPRECATED); - - return static::createFromThrowable($exception, $statusCode, $headers); - } - - public static function createFromThrowable(\Throwable $exception, int $statusCode = null, array $headers = []): self - { - $e = new static(); - $e->setMessage($exception->getMessage()); - $e->setCode($exception->getCode()); - - if ($exception instanceof HttpExceptionInterface) { - $statusCode = $exception->getStatusCode(); - $headers = array_merge($headers, $exception->getHeaders()); - } elseif ($exception instanceof RequestExceptionInterface) { - $statusCode = 400; - } - - if (null === $statusCode) { - $statusCode = 500; - } - - $e->setStatusCode($statusCode); - $e->setHeaders($headers); - $e->setTraceFromThrowable($exception); - $e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception)); - $e->setFile($exception->getFile()); - $e->setLine($exception->getLine()); - - $previous = $exception->getPrevious(); - - if ($previous instanceof \Throwable) { - $e->setPrevious(static::createFromThrowable($previous)); - } - - return $e; - } - - public function toArray() - { - $exceptions = []; - foreach (array_merge([$this], $this->getAllPrevious()) as $exception) { - $exceptions[] = [ - 'message' => $exception->getMessage(), - 'class' => $exception->getClass(), - 'trace' => $exception->getTrace(), - ]; - } - - return $exceptions; - } - - public function getStatusCode() - { - return $this->statusCode; - } - - /** - * @return $this - */ - public function setStatusCode($code) - { - $this->statusCode = $code; - - return $this; - } - - public function getHeaders() - { - return $this->headers; - } - - /** - * @return $this - */ - public function setHeaders(array $headers) - { - $this->headers = $headers; - - return $this; - } - - public function getClass() - { - return $this->class; - } - - /** - * @return $this - */ - public function setClass($class) - { - $this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class; - - return $this; - } - - public function getFile() - { - return $this->file; - } - - /** - * @return $this - */ - public function setFile($file) - { - $this->file = $file; - - return $this; - } - - public function getLine() - { - return $this->line; - } - - /** - * @return $this - */ - public function setLine($line) - { - $this->line = $line; - - return $this; - } - - public function getMessage() - { - return $this->message; - } - - /** - * @return $this - */ - public function setMessage($message) - { - if (false !== strpos($message, "class@anonymous\0")) { - $message = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) { - return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0]; - }, $message); - } - - $this->message = $message; - - return $this; - } - - public function getCode() - { - return $this->code; - } - - /** - * @return $this - */ - public function setCode($code) - { - $this->code = $code; - - return $this; - } - - public function getPrevious() - { - return $this->previous; - } - - /** - * @return $this - */ - public function setPrevious(self $previous) - { - $this->previous = $previous; - - return $this; - } - - public function getAllPrevious() - { - $exceptions = []; - $e = $this; - while ($e = $e->getPrevious()) { - $exceptions[] = $e; - } - - return $exceptions; - } - - public function getTrace() - { - return $this->trace; - } - - /** - * @deprecated since 4.1, use {@see setTraceFromThrowable()} instead. - */ - public function setTraceFromException(\Exception $exception) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use "setTraceFromThrowable()" instead.', __METHOD__), E_USER_DEPRECATED); - - $this->setTraceFromThrowable($exception); - } - - public function setTraceFromThrowable(\Throwable $throwable) - { - $this->traceAsString = $throwable->getTraceAsString(); - - return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine()); - } - - /** - * @return $this - */ - public function setTrace($trace, $file, $line) - { - $this->trace = []; - $this->trace[] = [ - 'namespace' => '', - 'short_class' => '', - 'class' => '', - 'type' => '', - 'function' => '', - 'file' => $file, - 'line' => $line, - 'args' => [], - ]; - foreach ($trace as $entry) { - $class = ''; - $namespace = ''; - if (isset($entry['class'])) { - $parts = explode('\\', $entry['class']); - $class = array_pop($parts); - $namespace = implode('\\', $parts); - } - - $this->trace[] = [ - 'namespace' => $namespace, - 'short_class' => $class, - 'class' => isset($entry['class']) ? $entry['class'] : '', - 'type' => isset($entry['type']) ? $entry['type'] : '', - 'function' => isset($entry['function']) ? $entry['function'] : null, - 'file' => isset($entry['file']) ? $entry['file'] : null, - 'line' => isset($entry['line']) ? $entry['line'] : null, - 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [], - ]; - } - - return $this; - } - - private function flattenArgs($args, $level = 0, &$count = 0) - { - $result = []; - foreach ($args as $key => $value) { - if (++$count > 1e4) { - return ['array', '*SKIPPED over 10000 entries*']; - } - if ($value instanceof \__PHP_Incomplete_Class) { - // is_object() returns false on PHP<=7.1 - $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)]; - } elseif (\is_object($value)) { - $result[$key] = ['object', \get_class($value)]; - } elseif (\is_array($value)) { - if ($level > 10) { - $result[$key] = ['array', '*DEEP NESTED ARRAY*']; - } else { - $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)]; - } - } elseif (null === $value) { - $result[$key] = ['null', null]; - } elseif (\is_bool($value)) { - $result[$key] = ['boolean', $value]; - } elseif (\is_int($value)) { - $result[$key] = ['integer', $value]; - } elseif (\is_float($value)) { - $result[$key] = ['float', $value]; - } elseif (\is_resource($value)) { - $result[$key] = ['resource', get_resource_type($value)]; - } else { - $result[$key] = ['string', (string) $value]; - } - } - - return $result; - } - - private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) - { - $array = new \ArrayObject($value); - - return $array['__PHP_Incomplete_Class_Name']; - } - - public function getTraceAsString() - { - return $this->traceAsString; - } - - public function getAsString() - { - $message = ''; - $next = false; - - foreach (array_reverse(array_merge([$this], $this->getAllPrevious())) as $exception) { - if ($next) { - $message .= 'Next '; - } else { - $next = true; - } - $message .= $exception->getClass(); - - if ('' != $exception->getMessage()) { - $message .= ': '.$exception->getMessage(); - } - - $message .= ' in '.$exception->getFile().':'.$exception->getLine(). - "\nStack trace:\n".$exception->getTraceAsString()."\n\n"; - } - - return rtrim($message); - } -} diff --git a/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php b/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php deleted file mode 100644 index 5b02d52ad8bb..000000000000 --- a/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', OutOfMemoryException::class, \Symfony\Component\ErrorHandler\Exception\OutOfMemoryException::class), E_USER_DEPRECATED); - -/** - * Out of memory exception. - * - * @author Nicolas Grekas - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\OutOfMemoryException instead. - */ -class OutOfMemoryException extends FatalErrorException -{ -} diff --git a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php b/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php deleted file mode 100644 index 7ed3d7eb5254..000000000000 --- a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', SilencedErrorContext::class, \Symfony\Component\ErrorHandler\Exception\SilencedErrorContext::class), E_USER_DEPRECATED); - -/** - * Data Object that represents a Silenced Error. - * - * @author Grégoire Pineau - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext instead. - */ -class SilencedErrorContext implements \JsonSerializable -{ - public $count = 1; - - private $severity; - private $file; - private $line; - private $trace; - - public function __construct(int $severity, string $file, int $line, array $trace = [], int $count = 1) - { - $this->severity = $severity; - $this->file = $file; - $this->line = $line; - $this->trace = $trace; - $this->count = $count; - } - - public function getSeverity() - { - return $this->severity; - } - - public function getFile() - { - return $this->file; - } - - public function getLine() - { - return $this->line; - } - - public function getTrace() - { - return $this->trace; - } - - public function JsonSerialize() - { - return [ - 'severity' => $this->severity, - 'file' => $this->file, - 'line' => $this->line, - 'trace' => $this->trace, - 'count' => $this->count, - ]; - } -} diff --git a/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php b/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php deleted file mode 100644 index fefd7d248eda..000000000000 --- a/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedFunctionException::class, \Symfony\Component\ErrorHandler\Exception\UndefinedFunctionException::class), E_USER_DEPRECATED); - -/** - * Undefined Function Exception. - * - * @author Konstanton Myakshin - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\UndefinedFunctionException instead. - */ -class UndefinedFunctionException extends FatalErrorException -{ - public function __construct(string $message, \ErrorException $previous) - { - parent::__construct( - $message, - $previous->getCode(), - $previous->getSeverity(), - $previous->getFile(), - $previous->getLine(), - null, - true, - null, - $previous->getPrevious() - ); - $this->setTrace($previous->getTrace()); - } -} diff --git a/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php b/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php deleted file mode 100644 index 48559415868d..000000000000 --- a/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedMethodException::class, \Symfony\Component\ErrorHandler\Exception\UndefinedMethodException::class), E_USER_DEPRECATED); - -/** - * Undefined Method Exception. - * - * @author Grégoire Pineau - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\UndefinedMethodException instead. - */ -class UndefinedMethodException extends FatalErrorException -{ - public function __construct(string $message, \ErrorException $previous) - { - parent::__construct( - $message, - $previous->getCode(), - $previous->getSeverity(), - $previous->getFile(), - $previous->getLine(), - null, - true, - null, - $previous->getPrevious() - ); - $this->setTrace($previous->getTrace()); - } -} diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php deleted file mode 100644 index 2be65ab6fe46..000000000000 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ /dev/null @@ -1,470 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -use Symfony\Component\Debug\Exception\FlattenException; -use Symfony\Component\Debug\Exception\OutOfMemoryException; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ExceptionHandler::class, \Symfony\Component\ErrorHandler\ExceptionHandler::class), E_USER_DEPRECATED); - -/** - * ExceptionHandler converts an exception to a Response object. - * - * It is mostly useful in debug mode to replace the default PHP/XDebug - * output with something prettier and more useful. - * - * As this class is mainly used during Kernel boot, where nothing is yet - * available, the Response content is always HTML. - * - * @author Fabien Potencier - * @author Nicolas Grekas - * - * @final since Symfony 4.3 - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ExceptionHandler instead. - */ -class ExceptionHandler -{ - private const GHOST_ADDONS = [ - '02-14' => self::GHOST_HEART, - '02-29' => self::GHOST_PLUS, - '10-18' => self::GHOST_GIFT, - ]; - - private const GHOST_GIFT = 'M124.005 5.36c.396-.715 1.119-1.648-.124-1.873-.346-.177-.692-.492-1.038-.141-.769.303-1.435.728-.627 1.523.36.514.685 1.634 1.092 1.758.242-.417.47-.842.697-1.266zm-1.699 1.977c-.706-1.26-1.274-2.612-2.138-3.774-1.051-1.123-3.122-.622-3.593.825-.625 1.431.724 3.14 2.251 2.96 1.159.02 2.324.072 3.48-.011zm5.867.043c1.502-.202 2.365-2.092 1.51-3.347-.757-1.34-2.937-1.387-3.698-.025-.659 1.1-1.23 2.25-1.835 3.38 1.336.077 2.686.06 4.023-.008zm2.487 1.611c.512-.45 2.494-.981.993-1.409-.372-.105-.805-.59-1.14-.457-.726.902-1.842 1.432-3.007 1.376-.228.075-1.391-.114-1.077.1.822.47 1.623.979 2.474 1.395.595-.317 1.173-.667 1.757-1.005zm-11.696.255l1.314-.765c-1.338-.066-2.87.127-3.881-.95-.285-.319-.559-.684-.954-.282-.473.326-1.929.66-.808 1.058.976.576 1.945 1.167 2.946 1.701.476-.223.926-.503 1.383-.762zm6.416 2.846c.567-.456 1.942-.89 1.987-1.38-1.282-.737-2.527-1.56-3.87-2.183-.461-.175-.835.094-1.207.328-1.1.654-2.225 1.267-3.288 1.978 1.39.86 2.798 1.695 4.219 2.504.725-.407 1.44-.83 2.16-1.247zm5.692 1.423l1.765-1.114c-.005-1.244.015-2.488-.019-3.732a77.306 77.306 0 0 0-3.51 2.084c-.126 1.282-.062 2.586-.034 3.876.607-.358 1.2-.741 1.798-1.114zm-13.804-.784c.06-1.06.19-2.269-1.09-2.583-.807-.376-1.926-1.341-2.548-1.332-.02 1.195-.01 2.39-.011 3.585 1.192.744 2.364 1.524 3.582 2.226.119-.616.041-1.269.067-1.896zm8.541 4.105l2.117-1.336c-.003-1.284.05-2.57-.008-3.853-.776.223-1.662.91-2.48 1.337l-1.834 1.075c.012 1.37-.033 2.744.044 4.113.732-.427 1.443-.887 2.161-1.336zm-2.957-.72v-2.057c-1.416-.828-2.828-1.664-4.25-2.482-.078 1.311-.033 2.627-.045 3.94 1.416.887 2.817 1.798 4.25 2.655.057-.683.036-1.372.045-2.057zm8.255 2.755l1.731-1.153c-.024-1.218.06-2.453-.062-3.658-1.2.685-2.358 1.464-3.537 2.195.028 1.261-.058 2.536.072 3.786.609-.373 1.2-.777 1.796-1.17zm-13.851-.683l-.014-1.916c-1.193-.746-2.37-1.517-3.58-2.234-.076 1.224-.033 2.453-.044 3.679 1.203.796 2.392 1.614 3.61 2.385.048-.636.024-1.276.028-1.914zm8.584 4.199l2.102-1.396c-.002-1.298.024-2.596-.01-3.893-1.427.88-2.843 1.775-4.25 2.686-.158 1.253-.055 2.545-.056 3.811.437.266 1.553-.912 2.214-1.208zm-2.988-.556c-.085-.894.365-2.154-.773-2.5-1.146-.727-2.288-1.46-3.45-2.163-.17 1.228.008 2.508-.122 3.751a79.399 79.399 0 0 0 4.278 2.885c.117-.641.044-1.32.067-1.973zm-4.872-.236l-5.087-3.396c.002-3.493-.047-6.988.015-10.48.85-.524 1.753-.954 2.627-1.434-.564-1.616.25-3.58 1.887-4.184 1.372-.563 3.025-.055 3.9 1.13l1.906-.978 1.916.987c.915-1.086 2.483-1.706 3.842-1.097 1.631.573 2.52 2.532 1.936 4.145.88.497 1.837.886 2.644 1.492.036 3.473 0 6.946-.003 10.419-3.374 2.233-6.693 4.55-10.122 6.699-.997 0-1.858-1.083-2.783-1.522a735.316 735.316 0 0 1-2.678-1.781z'; - private const GHOST_HEART = 'M125.914 8.305c3.036-8.71 14.933 0 0 11.2-14.932-11.2-3.036-19.91 0-11.2z'; - private const GHOST_PLUS = 'M111.368 8.97h7.324V1.645h7.512v7.323h7.324v7.513h-7.324v7.323h-7.512v-7.323h-7.324z'; - - private $debug; - private $charset; - private $handler; - private $caughtBuffer; - private $caughtLength; - private $fileLinkFormat; - - public function __construct(bool $debug = true, string $charset = null, $fileLinkFormat = null) - { - $this->debug = $debug; - $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; - $this->fileLinkFormat = $fileLinkFormat; - } - - /** - * Registers the exception handler. - * - * @param bool $debug Enable/disable debug mode, where the stack trace is displayed - * @param string|null $charset The charset used by exception messages - * @param string|null $fileLinkFormat The IDE link template - * - * @return static - */ - public static function register($debug = true, $charset = null, $fileLinkFormat = null) - { - $handler = new static($debug, $charset, $fileLinkFormat); - - $prev = set_exception_handler([$handler, 'handle']); - if (\is_array($prev) && $prev[0] instanceof ErrorHandler) { - restore_exception_handler(); - $prev[0]->setExceptionHandler([$handler, 'handle']); - } - - return $handler; - } - - /** - * Sets a user exception handler. - * - * @param callable $handler An handler that will be called on Exception - * - * @return callable|null The previous exception handler if any - */ - public function setHandler(callable $handler = null) - { - $old = $this->handler; - $this->handler = $handler; - - return $old; - } - - /** - * Sets the format for links to source files. - * - * @param string|FileLinkFormatter $fileLinkFormat The format for links to source files - * - * @return string The previous file link format - */ - public function setFileLinkFormat($fileLinkFormat) - { - $old = $this->fileLinkFormat; - $this->fileLinkFormat = $fileLinkFormat; - - return $old; - } - - /** - * Sends a response for the given Exception. - * - * To be as fail-safe as possible, the exception is first handled - * by our simple exception handler, then by the user exception handler. - * The latter takes precedence and any output from the former is cancelled, - * if and only if nothing bad happens in this handling path. - */ - public function handle(\Exception $exception) - { - if (null === $this->handler || $exception instanceof OutOfMemoryException) { - $this->sendPhpResponse($exception); - - return; - } - - $caughtLength = $this->caughtLength = 0; - - ob_start(function ($buffer) { - $this->caughtBuffer = $buffer; - - return ''; - }); - - $this->sendPhpResponse($exception); - while (null === $this->caughtBuffer && ob_end_flush()) { - // Empty loop, everything is in the condition - } - if (isset($this->caughtBuffer[0])) { - ob_start(function ($buffer) { - if ($this->caughtLength) { - // use substr_replace() instead of substr() for mbstring overloading resistance - $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength); - if (isset($cleanBuffer[0])) { - $buffer = $cleanBuffer; - } - } - - return $buffer; - }); - - echo $this->caughtBuffer; - $caughtLength = ob_get_length(); - } - $this->caughtBuffer = null; - - try { - ($this->handler)($exception); - $this->caughtLength = $caughtLength; - } catch (\Exception $e) { - if (!$caughtLength) { - // All handlers failed. Let PHP handle that now. - throw $exception; - } - } - } - - /** - * Sends the error associated with the given Exception as a plain PHP response. - * - * This method uses plain PHP functions like header() and echo to output - * the response. - * - * @param \Throwable|FlattenException $exception A \Throwable or FlattenException instance - */ - public function sendPhpResponse($exception) - { - if ($exception instanceof \Throwable) { - $exception = FlattenException::createFromThrowable($exception); - } - - if (!headers_sent()) { - header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); - foreach ($exception->getHeaders() as $name => $value) { - header($name.': '.$value, false); - } - header('Content-Type: text/html; charset='.$this->charset); - } - - echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); - } - - /** - * Gets the full HTML content associated with the given exception. - * - * @param \Exception|FlattenException $exception An \Exception or FlattenException instance - * - * @return string The HTML content as a string - */ - public function getHtml($exception) - { - if (!$exception instanceof FlattenException) { - $exception = FlattenException::create($exception); - } - - return $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); - } - - /** - * Gets the HTML content associated with the given exception. - * - * @return string The content as a string - */ - public function getContent(FlattenException $exception) - { - switch ($exception->getStatusCode()) { - case 404: - $title = 'Sorry, the page you are looking for could not be found.'; - break; - default: - $title = $this->debug ? $this->escapeHtml($exception->getMessage()) : 'Whoops, looks like something went wrong.'; - } - - if (!$this->debug) { - return << -

$title

- -EOF; - } - - $content = ''; - try { - $count = \count($exception->getAllPrevious()); - $total = $count + 1; - foreach ($exception->toArray() as $position => $e) { - $ind = $count - $position + 1; - $class = $this->formatClass($e['class']); - $message = nl2br($this->escapeHtml($e['message'])); - $content .= sprintf(<<<'EOF' -
- - - -EOF - , $ind, $total, $class, $message); - foreach ($e['trace'] as $trace) { - $content .= '\n"; - } - - $content .= "\n
-

- (%d/%d) - %s -

-

%s

-
'; - if ($trace['function']) { - $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); - } - if (isset($trace['file']) && isset($trace['line'])) { - $content .= $this->formatPath($trace['file'], $trace['line']); - } - $content .= "
\n
\n"; - } - } catch (\Exception $e) { - // something nasty happened and we cannot throw an exception anymore - if ($this->debug) { - $e = FlattenException::create($e); - $title = sprintf('Exception thrown when handling an exception (%s: %s)', $e->getClass(), $this->escapeHtml($e->getMessage())); - } else { - $title = 'Whoops, looks like something went wrong.'; - } - } - - $symfonyGhostImageContents = $this->getSymfonyGhostAsSvg(); - - return << -
-
-

$title

-
$symfonyGhostImageContents
-
-
- - -
- $content -
-EOF; - } - - /** - * Gets the stylesheet associated with the given exception. - * - * @return string The stylesheet as a string - */ - public function getStylesheet(FlattenException $exception) - { - if (!$this->debug) { - return <<<'EOF' - body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; } - .container { margin: 30px; max-width: 600px; } - h1 { color: #dc3545; font-size: 24px; } -EOF; - } - - return <<<'EOF' - body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; } - - a { cursor: pointer; text-decoration: none; } - a:hover { text-decoration: underline; } - abbr[title] { border-bottom: none; cursor: help; text-decoration: none; } - - code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; } - - table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; } - table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } - table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; } - table th { background-color: #E0E0E0; font-weight: bold; text-align: left; } - - .hidden-xs-down { display: none; } - .block { display: block; } - .break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; } - .text-muted { color: #999; } - - .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } - .container::after { content: ""; display: table; clear: both; } - - .exception-summary { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; } - - .exception-message-wrapper { display: flex; align-items: center; min-height: 70px; } - .exception-message { flex-grow: 1; padding: 30px 0; } - .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } - .exception-message.long { font-size: 18px; } - .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; } - .exception-message a:hover { border-bottom-color: #ffffff; } - - .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } - - .trace + .trace { margin-top: 30px; } - .trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } - - .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } - - .trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; } - .trace-class { color: #B0413E; } - .trace-type { padding: 0 2px; } - .trace-method { color: #B0413E; font-weight: bold; } - .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } - - @media (min-width: 575px) { - .hidden-xs-down { display: initial; } - } -EOF; - } - - private function decorate($content, $css) - { - return << - - - - - - - - $content - - -EOF; - } - - private function formatClass($class) - { - $parts = explode('\\', $class); - - return sprintf('%s', $class, array_pop($parts)); - } - - private function formatPath($path, $line) - { - $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path); - $fmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); - - if (!$fmt) { - return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); - } - - if (\is_string($fmt)) { - $i = strpos($f = $fmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f); - $fmt = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE); - - for ($i = 1; isset($fmt[$i]); ++$i) { - if (0 === strpos($path, $k = $fmt[$i++])) { - $path = substr_replace($path, $fmt[$i], 0, \strlen($k)); - break; - } - } - - $link = strtr($fmt[0], ['%f' => $path, '%l' => $line]); - } else { - try { - $link = $fmt->format($path, $line); - } catch (\Exception $e) { - return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); - } - } - - return sprintf('in %s%s', $this->escapeHtml($link), $file, 0 < $line ? ' line '.$line : ''); - } - - /** - * Formats an array as a string. - * - * @param array $args The argument array - * - * @return string - */ - private function formatArgs(array $args) - { - $result = []; - foreach ($args as $key => $item) { - if ('object' === $item[0]) { - $formattedValue = sprintf('object(%s)', $this->formatClass($item[1])); - } elseif ('array' === $item[0]) { - $formattedValue = sprintf('array(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); - } elseif ('null' === $item[0]) { - $formattedValue = 'null'; - } elseif ('boolean' === $item[0]) { - $formattedValue = ''.strtolower(var_export($item[1], true)).''; - } elseif ('resource' === $item[0]) { - $formattedValue = 'resource'; - } else { - $formattedValue = str_replace("\n", '', $this->escapeHtml(var_export($item[1], true))); - } - - $result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escapeHtml($key), $formattedValue); - } - - return implode(', ', $result); - } - - /** - * HTML-encodes a string. - */ - private function escapeHtml($str) - { - return htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset); - } - - private function getSymfonyGhostAsSvg() - { - return ''.$this->addElementToGhost().''; - } - - private function addElementToGhost() - { - if (!isset(self::GHOST_ADDONS[date('m-d')])) { - return ''; - } - - return ''; - } -} diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php deleted file mode 100644 index 8211d5d0e267..000000000000 --- a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php +++ /dev/null @@ -1,197 +0,0 @@ - - * - * 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 Composer\Autoload\ClassLoader as ComposerClassLoader; -use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; -use Symfony\Component\Debug\DebugClassLoader; -use Symfony\Component\Debug\Exception\ClassNotFoundException; -use Symfony\Component\Debug\Exception\FatalErrorException; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ClassNotFoundFatalErrorHandler::class, \Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler::class), E_USER_DEPRECATED); - -/** - * ErrorHandler for classes that do not exist. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler instead. - */ -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 (['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(string $class): array - { - if (!\is_array($functions = spl_autoload_functions())) { - return []; - } - - // find Symfony and Composer autoloaders - $classes = []; - - 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); - } - - private function findClassInPath(string $path, string $class, string $prefix): array - { - if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { - return []; - } - - $classes = []; - $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; - } - - private function convertFileToClass(string $path, string $file, string $prefix): ?string - { - $candidates = [ - // namespaced class - $namespacedClass = str_replace([$path.\DIRECTORY_SEPARATOR, '.php', '/'], ['', '', '\\'], $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; - } - } - - try { - require_once $file; - } catch (\Throwable $e) { - return null; - } - - foreach ($candidates as $candidate) { - if ($this->classExists($candidate)) { - return $candidate; - } - } - - return null; - } - - private function classExists(string $class): bool - { - return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); - } -} diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php deleted file mode 100644 index a1c4a8ce547c..000000000000 --- a/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * 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; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FatalErrorHandlerInterface::class, \Symfony\Component\ErrorHandler\FatalErrorHandler\FatalErrorHandlerInterface::class), E_USER_DEPRECATED); - -/** - * Attempts to convert fatal errors to exceptions. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\FatalErrorHandlerInterface instead. - */ -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/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php deleted file mode 100644 index dc7cb85bf360..000000000000 --- a/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * 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\UndefinedFunctionException; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedFunctionFatalErrorHandler::class, \Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedFunctionFatalErrorHandler::class), E_USER_DEPRECATED); - -/** - * ErrorHandler for undefined functions. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedFunctionFatalErrorHandler instead. - */ -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 = []; - 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/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php deleted file mode 100644 index 5972327c498a..000000000000 --- a/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * 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; - -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedMethodFatalErrorHandler::class, \Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedMethodFatalErrorHandler::class), E_USER_DEPRECATED); - -/** - * ErrorHandler for undefined methods. - * - * @author Grégoire Pineau - * - * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedMethodFatalErrorHandler instead. - */ -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 = []; - 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); - } -} diff --git a/src/Symfony/Component/Debug/LICENSE b/src/Symfony/Component/Debug/LICENSE deleted file mode 100644 index a677f43763ca..000000000000 --- a/src/Symfony/Component/Debug/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2019 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/src/Symfony/Component/Debug/README.md b/src/Symfony/Component/Debug/README.md deleted file mode 100644 index a1d16175c1a1..000000000000 --- a/src/Symfony/Component/Debug/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Debug Component -=============== - -The Debug component provides tools to ease debugging PHP code. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/debug/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php deleted file mode 100644 index 6cca7a2e19a3..000000000000 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ /dev/null @@ -1,451 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Debug\DebugClassLoader; - -/** - * @group legacy - */ -class DebugClassLoaderTest extends TestCase -{ - /** - * @var int Error reporting level before running tests - */ - private $errorReporting; - - private $loader; - - protected function setUp() - { - $this->errorReporting = error_reporting(E_ALL); - $this->loader = new ClassLoader(); - spl_autoload_register([$this->loader, 'loadClass'], true, true); - DebugClassLoader::enable(); - } - - protected function tearDown() - { - DebugClassLoader::disable(); - spl_autoload_unregister([$this->loader, 'loadClass']); - error_reporting($this->errorReporting); - } - - public function testIdempotence() - { - DebugClassLoader::enable(); - - $functions = spl_autoload_functions(); - foreach ($functions as $function) { - if (\is_array($function) && $function[0] instanceof DebugClassLoader) { - $reflClass = new \ReflectionClass($function[0]); - $reflProp = $reflClass->getProperty('classLoader'); - $reflProp->setAccessible(true); - - $this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0])); - - return; - } - } - - $this->fail('DebugClassLoader did not register'); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage boo - */ - public function testThrowingClass() - { - try { - class_exists(__NAMESPACE__.'\Fixtures\Throwing'); - $this->fail('Exception expected'); - } catch (\Exception $e) { - $this->assertSame('boo', $e->getMessage()); - } - - // the second call also should throw - class_exists(__NAMESPACE__.'\Fixtures\Throwing'); - } - - /** - * @expectedException \RuntimeException - */ - public function testNameCaseMismatch() - { - class_exists(__NAMESPACE__.'\TestingCaseMismatch', true); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage Case mismatch between class and real file names - */ - public function testFileCaseMismatch() - { - if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) { - $this->markTestSkipped('Can only be run on case insensitive filesystems'); - } - - class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); - } - - /** - * @expectedException \RuntimeException - */ - public function testPsr4CaseMismatch() - { - class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true); - } - - public function testNotPsr0() - { - $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true)); - } - - public function testNotPsr0Bis() - { - $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true)); - } - - public function testClassAlias() - { - $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true)); - } - - /** - * @dataProvider provideDeprecatedSuper - */ - public function testDeprecatedSuper($class, $super, $type) - { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_DEPRECATED); - - class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = [ - 'type' => E_USER_DEPRECATED, - 'message' => 'The "Test\Symfony\Component\Debug\Tests\\'.$class.'" class '.$type.' "Symfony\Component\Debug\Tests\Fixtures\\'.$super.'" that is deprecated but this is a test deprecation notice.', - ]; - - $this->assertSame($xError, $lastError); - } - - public function provideDeprecatedSuper() - { - return [ - ['DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'], - ['DeprecatedParentClass', 'DeprecatedClass', 'extends'], - ]; - } - - public function testInterfaceExtendsDeprecatedInterface() - { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); - - class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = [ - 'type' => E_USER_NOTICE, - 'message' => '', - ]; - - $this->assertSame($xError, $lastError); - } - - public function testDeprecatedSuperInSameNamespace() - { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); - - class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = [ - 'type' => E_USER_NOTICE, - 'message' => '', - ]; - - $this->assertSame($xError, $lastError); - } - - public function testExtendedFinalClass() - { - $deprecations = []; - set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); - $e = error_reporting(E_USER_DEPRECATED); - - require __DIR__.'/Fixtures/FinalClasses.php'; - - $i = 1; - while (class_exists($finalClass = __NAMESPACE__.'\\Fixtures\\FinalClass'.$i++, false)) { - spl_autoload_call($finalClass); - class_exists('Test\\'.__NAMESPACE__.'\\Extends'.substr($finalClass, strrpos($finalClass, '\\') + 1), true); - } - - error_reporting($e); - restore_error_handler(); - - $this->assertSame([ - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass1" class is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass1".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass2" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass2".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass3" class is considered final comment with @@@ and ***. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass3".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass4" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass4".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass5" class is considered final multiline comment. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass5".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass6" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass6".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass7" class is considered final another multiline comment... It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass7".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass8" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass8".', - ], $deprecations); - } - - public function testExtendedFinalMethod() - { - $deprecations = []; - set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); - $e = error_reporting(E_USER_DEPRECATED); - - class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true); - - error_reporting($e); - restore_error_handler(); - - $xError = [ - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".', - 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".', - ]; - - $this->assertSame($xError, $deprecations); - } - - public function testExtendedDeprecatedMethodDoesntTriggerAnyNotice() - { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); - - class_exists('Test\\'.__NAMESPACE__.'\\ExtendsAnnotatedClass', true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $this->assertSame(['type' => E_USER_NOTICE, 'message' => ''], $lastError); - } - - public function testInternalsUse() - { - $deprecations = []; - set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); - $e = error_reporting(E_USER_DEPRECATED); - - class_exists('Test\\'.__NAMESPACE__.'\\ExtendsInternals', true); - - error_reporting($e); - restore_error_handler(); - - $this->assertSame($deprecations, [ - 'The "Symfony\Component\Debug\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".', - 'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".', - 'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait" trait is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".', - 'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass::internalMethod()" method is considered internal. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".', - ]); - } - - public function testExtendedMethodDefinesNewParameters() - { - $deprecations = []; - set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); - $e = error_reporting(E_USER_DEPRECATED); - - class_exists(__NAMESPACE__.'\\Fixtures\SubClassWithAnnotatedParameters', true); - - error_reporting($e); - restore_error_handler(); - - $this->assertSame([ - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::quzMethod()" method will require a new "Quz $quz" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::isSymfony()" method will require a new "true $yes" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', - ], $deprecations); - } - - public function testUseTraitWithInternalMethod() - { - $deprecations = []; - set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); - $e = error_reporting(E_USER_DEPRECATED); - - class_exists('Test\\'.__NAMESPACE__.'\\UseTraitWithInternalMethod', true); - - error_reporting($e); - restore_error_handler(); - - $this->assertSame([], $deprecations); - } - - public function testVirtualUse() - { - $deprecations = []; - set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); - $e = error_reporting(E_USER_DEPRECATED); - - class_exists('Test\\'.__NAMESPACE__.'\\ExtendsVirtual', true); - - error_reporting($e); - restore_error_handler(); - - $this->assertSame([ - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::sameLineInterfaceMethodNoBraces()".', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::newLineInterfaceMethod()": Some description!', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::newLineInterfaceMethodNoBraces()": Description.', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::invalidInterfaceMethod()".', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::invalidInterfaceMethodNoBraces()".', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::complexInterfaceMethod($arg, ...$args)".', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::complexInterfaceMethodTyped($arg, int ...$args)": Description ...', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodNoBraces()".', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodTyped(int $arg)": Description.', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodTypedNoBraces()".', - 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtual" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualSubInterface::subInterfaceMethod()".', - ], $deprecations); - } - - public function testVirtualUseWithMagicCall() - { - $deprecations = []; - set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); - $e = error_reporting(E_USER_DEPRECATED); - - class_exists('Test\\'.__NAMESPACE__.'\\ExtendsVirtualMagicCall', true); - - error_reporting($e); - restore_error_handler(); - - $this->assertSame([], $deprecations); - } - - public function testEvaluatedCode() - { - $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\DefinitionInEvaluatedCode', true)); - } -} - -class ClassLoader -{ - public function loadClass($class) - { - } - - public function getClassMap() - { - return [__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php']; - } - - public function findFile($class) - { - $fixtureDir = __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR; - - if (__NAMESPACE__.'\TestingUnsilencing' === $class) { - eval('-- parse error --'); - } elseif (__NAMESPACE__.'\TestingStacking' === $class) { - eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }'); - } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) { - eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}'); - } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) { - return $fixtureDir.'psr4'.\DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php'; - } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) { - return $fixtureDir.'reallyNotPsr0.php'; - } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) { - return $fixtureDir.'notPsr0Bis.php'; - } elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) { - eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); - } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); - } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}'); - } elseif ('Test\\'.__NAMESPACE__.'\NonDeprecatedInterfaceClass' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}'); - } elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class Float {}'); - } elseif (0 === strpos($class, 'Test\\'.__NAMESPACE__.'\ExtendsFinalClass')) { - $classShortName = substr($class, strrpos($class, '\\') + 1); - eval('namespace Test\\'.__NAMESPACE__.'; class '.$classShortName.' extends \\'.__NAMESPACE__.'\Fixtures\\'.substr($classShortName, 7).' {}'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsAnnotatedClass' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass { - public function deprecatedMethod() { } - }'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent { - use \\'.__NAMESPACE__.'\Fixtures\InternalTrait; - - public function internalMethod() { } - }'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }'); - } elseif ('Test\\'.__NAMESPACE__.'\UseTraitWithInternalMethod' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class UseTraitWithInternalMethod { use \\'.__NAMESPACE__.'\Fixtures\TraitWithInternalMethod; }'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtual' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtual extends ExtendsVirtualParent implements \\'.__NAMESPACE__.'\Fixtures\VirtualSubInterface { - public function ownClassMethod() { } - public function classMethod() { } - public function sameLineInterfaceMethodNoBraces() { } - }'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualParent' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualParent extends ExtendsVirtualAbstract { - public function ownParentMethod() { } - public function traitMethod() { } - public function sameLineInterfaceMethod() { } - public function staticMethodNoBraces() { } // should be static - }'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualAbstract' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstract extends ExtendsVirtualAbstractBase { - public static function staticMethod() { } - public function ownAbstractMethod() { } - public function interfaceMethod() { } - }'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualAbstractBase' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstractBase extends \\'.__NAMESPACE__.'\Fixtures\VirtualClass implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface { - public function ownAbstractBaseMethod() { } - }'); - } elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualMagicCall' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualMagicCall extends \\'.__NAMESPACE__.'\Fixtures\VirtualClassMagicCall implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface { - }'); - } - } -} diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php deleted file mode 100644 index cae9878868b9..000000000000 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ /dev/null @@ -1,562 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use PHPUnit\Framework\TestCase; -use Psr\Log\LogLevel; -use Psr\Log\NullLogger; -use Symfony\Component\Debug\BufferingLogger; -use Symfony\Component\Debug\ErrorHandler; -use Symfony\Component\Debug\Exception\SilencedErrorContext; -use Symfony\Component\Debug\Tests\Fixtures\ErrorHandlerThatUsesThePreviousOne; -use Symfony\Component\Debug\Tests\Fixtures\LoggerThatSetAnErrorHandler; - -/** - * ErrorHandlerTest. - * - * @author Robert Schönthal - * @author Nicolas Grekas - * - * @group legacy - */ -class ErrorHandlerTest extends TestCase -{ - public function testRegister() - { - $handler = ErrorHandler::register(); - - try { - $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler); - $this->assertSame($handler, ErrorHandler::register()); - - $newHandler = new ErrorHandler(); - - $this->assertSame($handler, ErrorHandler::register($newHandler, false)); - $h = set_error_handler('var_dump'); - restore_error_handler(); - $this->assertSame([$handler, 'handleError'], $h); - - try { - $this->assertSame($newHandler, ErrorHandler::register($newHandler, true)); - $h = set_error_handler('var_dump'); - restore_error_handler(); - $this->assertSame([$newHandler, 'handleError'], $h); - } catch (\Exception $e) { - } - - restore_error_handler(); - restore_exception_handler(); - - if (isset($e)) { - throw $e; - } - } catch (\Exception $e) { - } - - restore_error_handler(); - restore_exception_handler(); - - if (isset($e)) { - throw $e; - } - } - - public function testErrorGetLast() - { - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $handler = ErrorHandler::register(); - $handler->setDefaultLogger($logger); - $handler->screamAt(E_ALL); - - try { - @trigger_error('Hello', E_USER_WARNING); - $expected = [ - 'type' => E_USER_WARNING, - 'message' => 'Hello', - 'file' => __FILE__, - 'line' => __LINE__ - 5, - ]; - $this->assertSame($expected, error_get_last()); - } catch (\Exception $e) { - restore_error_handler(); - restore_exception_handler(); - - throw $e; - } - } - - public function testNotice() - { - ErrorHandler::register(); - - try { - self::triggerNotice($this); - $this->fail('ErrorException expected'); - } catch (\ErrorException $exception) { - // if an exception is thrown, the test passed - $this->assertEquals(E_NOTICE, $exception->getSeverity()); - $this->assertEquals(__FILE__, $exception->getFile()); - $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); - - $trace = $exception->getTrace(); - - $this->assertEquals(__FILE__, $trace[0]['file']); - $this->assertEquals(__CLASS__, $trace[0]['class']); - $this->assertEquals('triggerNotice', $trace[0]['function']); - $this->assertEquals('::', $trace[0]['type']); - - $this->assertEquals(__FILE__, $trace[0]['file']); - $this->assertEquals(__CLASS__, $trace[1]['class']); - $this->assertEquals(__FUNCTION__, $trace[1]['function']); - $this->assertEquals('->', $trace[1]['type']); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - // dummy function to test trace in error handler. - private static function triggerNotice($that) - { - $that->assertSame('', $foo.$foo.$bar); - } - - public function testConstruct() - { - try { - $handler = ErrorHandler::register(); - $handler->throwAt(3, true); - $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0)); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testDefaultLogger() - { - try { - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $handler = ErrorHandler::register(); - - $handler->setDefaultLogger($logger, E_NOTICE); - $handler->setDefaultLogger($logger, [E_USER_NOTICE => LogLevel::CRITICAL]); - - $loggers = [ - E_DEPRECATED => [null, LogLevel::INFO], - E_USER_DEPRECATED => [null, LogLevel::INFO], - E_NOTICE => [$logger, LogLevel::WARNING], - E_USER_NOTICE => [$logger, LogLevel::CRITICAL], - E_STRICT => [null, LogLevel::WARNING], - E_WARNING => [null, LogLevel::WARNING], - E_USER_WARNING => [null, LogLevel::WARNING], - E_COMPILE_WARNING => [null, LogLevel::WARNING], - E_CORE_WARNING => [null, LogLevel::WARNING], - E_USER_ERROR => [null, LogLevel::CRITICAL], - E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], - E_COMPILE_ERROR => [null, LogLevel::CRITICAL], - E_PARSE => [null, LogLevel::CRITICAL], - E_ERROR => [null, LogLevel::CRITICAL], - E_CORE_ERROR => [null, LogLevel::CRITICAL], - ]; - $this->assertSame($loggers, $handler->setLoggers([])); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testHandleError() - { - try { - $handler = ErrorHandler::register(); - $handler->throwAt(0, true); - $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, [])); - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(3, true); - $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, [])); - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(3, true); - try { - $handler->handleError(4, 'foo', 'foo.php', 12, []); - } catch (\ErrorException $e) { - $this->assertSame('Parse Error: foo', $e->getMessage()); - $this->assertSame(4, $e->getSeverity()); - $this->assertSame('foo.php', $e->getFile()); - $this->assertSame(12, $e->getLine()); - } - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(E_USER_DEPRECATED, true); - $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, [])); - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(E_DEPRECATED, true); - $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, [])); - - restore_error_handler(); - restore_exception_handler(); - - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - - $warnArgCheck = function ($logLevel, $message, $context) { - $this->assertEquals('info', $logLevel); - $this->assertEquals('User Deprecated: foo', $message); - $this->assertArrayHasKey('exception', $context); - $exception = $context['exception']; - $this->assertInstanceOf(\ErrorException::class, $exception); - $this->assertSame('User Deprecated: foo', $exception->getMessage()); - $this->assertSame(E_USER_DEPRECATED, $exception->getSeverity()); - }; - - $logger - ->expects($this->once()) - ->method('log') - ->willReturnCallback($warnArgCheck) - ; - - $handler = ErrorHandler::register(); - $handler->setDefaultLogger($logger, E_USER_DEPRECATED); - $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, [])); - - restore_error_handler(); - restore_exception_handler(); - - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - - $line = null; - $logArgCheck = function ($level, $message, $context) use (&$line) { - $this->assertEquals('Notice: Undefined variable: undefVar', $message); - $this->assertArrayHasKey('exception', $context); - $exception = $context['exception']; - $this->assertInstanceOf(SilencedErrorContext::class, $exception); - $this->assertSame(E_NOTICE, $exception->getSeverity()); - $this->assertSame(__FILE__, $exception->getFile()); - $this->assertSame($line, $exception->getLine()); - $this->assertNotEmpty($exception->getTrace()); - $this->assertSame(1, $exception->count); - }; - - $logger - ->expects($this->once()) - ->method('log') - ->willReturnCallback($logArgCheck) - ; - - $handler = ErrorHandler::register(); - $handler->setDefaultLogger($logger, E_NOTICE); - $handler->screamAt(E_NOTICE); - unset($undefVar); - $line = __LINE__ + 1; - @$undefVar++; - - restore_error_handler(); - restore_exception_handler(); - } catch (\Exception $e) { - restore_error_handler(); - restore_exception_handler(); - - throw $e; - } - } - - public function testHandleUserError() - { - try { - $handler = ErrorHandler::register(); - $handler->throwAt(0, true); - - $e = null; - $x = new \Exception('Foo'); - - try { - $f = new Fixtures\ToStringThrower($x); - $f .= ''; // Trigger $f->__toString() - } catch (\Exception $e) { - } - - $this->assertSame($x, $e); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testHandleDeprecation() - { - $logArgCheck = function ($level, $message, $context) { - $this->assertEquals(LogLevel::INFO, $level); - $this->assertArrayHasKey('exception', $context); - $exception = $context['exception']; - $this->assertInstanceOf(\ErrorException::class, $exception); - $this->assertSame('User Deprecated: Foo deprecation', $exception->getMessage()); - }; - - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $logger - ->expects($this->once()) - ->method('log') - ->willReturnCallback($logArgCheck) - ; - - $handler = new ErrorHandler(); - $handler->setDefaultLogger($logger); - @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []); - - restore_error_handler(); - } - - public function testHandleException() - { - try { - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $handler = ErrorHandler::register(); - - $exception = new \Exception('foo'); - - $logArgCheck = function ($level, $message, $context) { - $this->assertSame('Uncaught Exception: foo', $message); - $this->assertArrayHasKey('exception', $context); - $this->assertInstanceOf(\Exception::class, $context['exception']); - }; - - $logger - ->expects($this->exactly(2)) - ->method('log') - ->willReturnCallback($logArgCheck) - ; - - $handler->setDefaultLogger($logger, E_ERROR); - - try { - $handler->handleException($exception); - $this->fail('Exception expected'); - } catch (\Exception $e) { - $this->assertSame($exception, $e); - } - - $handler->setExceptionHandler(function ($e) use ($exception) { - $this->assertSame($exception, $e); - }); - - $handler->handleException($exception); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testBootstrappingLogger() - { - $bootLogger = new BufferingLogger(); - $handler = new ErrorHandler($bootLogger); - - $loggers = [ - E_DEPRECATED => [$bootLogger, LogLevel::INFO], - E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO], - E_NOTICE => [$bootLogger, LogLevel::WARNING], - E_USER_NOTICE => [$bootLogger, LogLevel::WARNING], - E_STRICT => [$bootLogger, LogLevel::WARNING], - E_WARNING => [$bootLogger, LogLevel::WARNING], - E_USER_WARNING => [$bootLogger, LogLevel::WARNING], - E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING], - E_CORE_WARNING => [$bootLogger, LogLevel::WARNING], - E_USER_ERROR => [$bootLogger, LogLevel::CRITICAL], - E_RECOVERABLE_ERROR => [$bootLogger, LogLevel::CRITICAL], - E_COMPILE_ERROR => [$bootLogger, LogLevel::CRITICAL], - E_PARSE => [$bootLogger, LogLevel::CRITICAL], - E_ERROR => [$bootLogger, LogLevel::CRITICAL], - E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL], - ]; - - $this->assertSame($loggers, $handler->setLoggers([])); - - $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, []); - - $logs = $bootLogger->cleanLogs(); - - $this->assertCount(1, $logs); - $log = $logs[0]; - $this->assertSame('info', $log[0]); - $this->assertSame('Deprecated: Foo message', $log[1]); - $this->assertArrayHasKey('exception', $log[2]); - $exception = $log[2]['exception']; - $this->assertInstanceOf(\ErrorException::class, $exception); - $this->assertSame('Deprecated: Foo message', $exception->getMessage()); - $this->assertSame(__FILE__, $exception->getFile()); - $this->assertSame(123, $exception->getLine()); - $this->assertSame(E_DEPRECATED, $exception->getSeverity()); - - $bootLogger->log(LogLevel::WARNING, 'Foo message', ['exception' => $exception]); - - $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $mockLogger->expects($this->once()) - ->method('log') - ->with(LogLevel::WARNING, 'Foo message', ['exception' => $exception]); - - $handler->setLoggers([E_DEPRECATED => [$mockLogger, LogLevel::WARNING]]); - } - - public function testSettingLoggerWhenExceptionIsBuffered() - { - $bootLogger = new BufferingLogger(); - $handler = new ErrorHandler($bootLogger); - - $exception = new \Exception('Foo message'); - - $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $mockLogger->expects($this->once()) - ->method('log') - ->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', ['exception' => $exception]); - - $handler->setExceptionHandler(function () use ($handler, $mockLogger) { - $handler->setDefaultLogger($mockLogger); - }); - - $handler->handleException($exception); - } - - public function testHandleFatalError() - { - try { - $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - $handler = ErrorHandler::register(); - - $error = [ - 'type' => E_PARSE, - 'message' => 'foo', - 'file' => 'bar', - 'line' => 123, - ]; - - $logArgCheck = function ($level, $message, $context) { - $this->assertEquals('Fatal Parse Error: foo', $message); - $this->assertArrayHasKey('exception', $context); - $this->assertInstanceOf(\Exception::class, $context['exception']); - }; - - $logger - ->expects($this->once()) - ->method('log') - ->willReturnCallback($logArgCheck) - ; - - $handler->setDefaultLogger($logger, E_PARSE); - - $handler->handleFatalError($error); - - restore_error_handler(); - restore_exception_handler(); - } catch (\Exception $e) { - restore_error_handler(); - restore_exception_handler(); - - throw $e; - } - } - - public function testHandleErrorException() - { - $exception = new \Error("Class 'IReallyReallyDoNotExistAnywhereInTheRepositoryISwear' not found"); - - $handler = new ErrorHandler(); - $handler->setExceptionHandler(function () use (&$args) { - $args = \func_get_args(); - }); - - $handler->handleException($exception); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); - $this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); - } - - /** - * @expectedException \Exception - */ - public function testCustomExceptionHandler() - { - $handler = new ErrorHandler(); - $handler->setExceptionHandler(function ($e) use ($handler) { - $handler->handleException($e); - }); - - $handler->handleException(new \Exception()); - } - - /** - * @dataProvider errorHandlerWhenLoggingProvider - */ - public function testErrorHandlerWhenLogging($previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined) - { - try { - if ($previousHandlerWasDefined) { - set_error_handler('count'); - } - - $logger = $loggerSetsAnotherHandler ? new LoggerThatSetAnErrorHandler() : new NullLogger(); - - $handler = ErrorHandler::register(); - $handler->setDefaultLogger($logger); - - if ($nextHandlerIsDefined) { - $handler = ErrorHandlerThatUsesThePreviousOne::register(); - } - - @trigger_error('foo', E_USER_DEPRECATED); - @trigger_error('bar', E_USER_DEPRECATED); - - $this->assertSame([$handler, 'handleError'], set_error_handler('var_dump')); - - if ($logger instanceof LoggerThatSetAnErrorHandler) { - $this->assertCount(2, $logger->cleanLogs()); - } - - restore_error_handler(); - - if ($previousHandlerWasDefined) { - restore_error_handler(); - } - - if ($nextHandlerIsDefined) { - restore_error_handler(); - } - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function errorHandlerWhenLoggingProvider() - { - foreach ([false, true] as $previousHandlerWasDefined) { - foreach ([false, true] as $loggerSetsAnotherHandler) { - foreach ([false, true] as $nextHandlerIsDefined) { - yield [$previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined]; - } - } - } - } -} diff --git a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php b/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php deleted file mode 100644 index f18809a5a227..000000000000 --- a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php +++ /dev/null @@ -1,391 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\Exception; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Debug\Exception\FatalThrowableError; -use Symfony\Component\Debug\Exception\FlattenException; -use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\ConflictHttpException; -use Symfony\Component\HttpKernel\Exception\GoneHttpException; -use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException; -use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; -use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; -use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; -use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; -use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; -use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; - -/** - * @group legacy - */ -class FlattenExceptionTest extends TestCase -{ - public function testStatusCode() - { - $flattened = FlattenException::create(new \RuntimeException(), 403); - $this->assertEquals('403', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new \RuntimeException()); - $this->assertEquals('500', $flattened->getStatusCode()); - - $flattened = FlattenException::createFromThrowable(new \DivisionByZeroError(), 403); - $this->assertEquals('403', $flattened->getStatusCode()); - - $flattened = FlattenException::createFromThrowable(new \DivisionByZeroError()); - $this->assertEquals('500', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new NotFoundHttpException()); - $this->assertEquals('404', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); - $this->assertEquals('401', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new BadRequestHttpException()); - $this->assertEquals('400', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new NotAcceptableHttpException()); - $this->assertEquals('406', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new ConflictHttpException()); - $this->assertEquals('409', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new MethodNotAllowedHttpException(['POST'])); - $this->assertEquals('405', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new AccessDeniedHttpException()); - $this->assertEquals('403', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new GoneHttpException()); - $this->assertEquals('410', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new LengthRequiredHttpException()); - $this->assertEquals('411', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new PreconditionFailedHttpException()); - $this->assertEquals('412', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new PreconditionRequiredHttpException()); - $this->assertEquals('428', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new ServiceUnavailableHttpException()); - $this->assertEquals('503', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new TooManyRequestsHttpException()); - $this->assertEquals('429', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new UnsupportedMediaTypeHttpException()); - $this->assertEquals('415', $flattened->getStatusCode()); - - if (class_exists(SuspiciousOperationException::class)) { - $flattened = FlattenException::create(new SuspiciousOperationException()); - $this->assertEquals('400', $flattened->getStatusCode()); - } - } - - public function testHeadersForHttpException() - { - $flattened = FlattenException::create(new MethodNotAllowedHttpException(['POST'])); - $this->assertEquals(['Allow' => 'POST'], $flattened->getHeaders()); - - $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); - $this->assertEquals(['WWW-Authenticate' => 'Basic realm="My Realm"'], $flattened->getHeaders()); - - $flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); - $this->assertEquals(['Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'], $flattened->getHeaders()); - - $flattened = FlattenException::create(new ServiceUnavailableHttpException(120)); - $this->assertEquals(['Retry-After' => 120], $flattened->getHeaders()); - - $flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); - $this->assertEquals(['Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'], $flattened->getHeaders()); - - $flattened = FlattenException::create(new TooManyRequestsHttpException(120)); - $this->assertEquals(['Retry-After' => 120], $flattened->getHeaders()); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testFlattenHttpException(\Throwable $exception) - { - $flattened = FlattenException::createFromThrowable($exception); - $flattened2 = FlattenException::createFromThrowable($exception); - - $flattened->setPrevious($flattened2); - - $this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.'); - $this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.'); - $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception'); - } - - public function testWrappedThrowable() - { - $exception = new FatalThrowableError(new \DivisionByZeroError('Ouch', 42)); - $flattened = FlattenException::create($exception); - - $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.'); - $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.'); - $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error'); - } - - public function testThrowable() - { - $error = new \DivisionByZeroError('Ouch', 42); - $flattened = FlattenException::createFromThrowable($error); - - $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.'); - $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.'); - $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error'); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testPrevious(\Throwable $exception) - { - $flattened = FlattenException::createFromThrowable($exception); - $flattened2 = FlattenException::createFromThrowable($exception); - - $flattened->setPrevious($flattened2); - - $this->assertSame($flattened2, $flattened->getPrevious()); - - $this->assertSame([$flattened2], $flattened->getAllPrevious()); - } - - public function testPreviousError() - { - $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42)); - - $flattened = FlattenException::create($exception)->getPrevious(); - - $this->assertEquals($flattened->getMessage(), 'Oh noes!', 'The message is copied from the original exception.'); - $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.'); - $this->assertEquals($flattened->getClass(), 'ParseError', 'The class is set to the class of the original exception'); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testLine(\Throwable $exception) - { - $flattened = FlattenException::createFromThrowable($exception); - $this->assertSame($exception->getLine(), $flattened->getLine()); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testFile(\Throwable $exception) - { - $flattened = FlattenException::createFromThrowable($exception); - $this->assertSame($exception->getFile(), $flattened->getFile()); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testToArray(\Throwable $exception, string $expectedClass) - { - $flattened = FlattenException::createFromThrowable($exception); - $flattened->setTrace([], 'foo.php', 123); - - $this->assertEquals([ - [ - 'message' => 'test', - 'class' => $expectedClass, - 'trace' => [[ - 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123, - 'args' => [], - ]], - ], - ], $flattened->toArray()); - } - - public function testCreate() - { - $exception = new NotFoundHttpException( - 'test', - new \RuntimeException('previous', 123) - ); - - $this->assertSame( - FlattenException::createFromThrowable($exception)->toArray(), - FlattenException::create($exception)->toArray() - ); - } - - public function flattenDataProvider() - { - return [ - [new \Exception('test', 123), 'Exception'], - [new \Error('test', 123), 'Error'], - ]; - } - - public function testArguments() - { - $dh = opendir(__DIR__); - $fh = tmpfile(); - - $incomplete = unserialize('O:14:"BogusTestClass":0:{}'); - - $exception = $this->createException([ - (object) ['foo' => 1], - new NotFoundHttpException(), - $incomplete, - $dh, - $fh, - function () {}, - [1, 2], - ['foo' => 123], - null, - true, - false, - 0, - 0.0, - '0', - '', - INF, - NAN, - ]); - - $flattened = FlattenException::create($exception); - $trace = $flattened->getTrace(); - $args = $trace[1]['args']; - $array = $args[0][1]; - - closedir($dh); - fclose($fh); - - $i = 0; - $this->assertSame(['object', 'stdClass'], $array[$i++]); - $this->assertSame(['object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'], $array[$i++]); - $this->assertSame(['incomplete-object', 'BogusTestClass'], $array[$i++]); - $this->assertSame(['resource', 'stream'], $array[$i++]); - $this->assertSame(['resource', 'stream'], $array[$i++]); - - $args = $array[$i++]; - $this->assertSame($args[0], 'object'); - $this->assertTrue('Closure' === $args[1] || is_subclass_of($args[1], '\Closure'), 'Expect object class name to be Closure or a subclass of Closure.'); - - $this->assertSame(['array', [['integer', 1], ['integer', 2]]], $array[$i++]); - $this->assertSame(['array', ['foo' => ['integer', 123]]], $array[$i++]); - $this->assertSame(['null', null], $array[$i++]); - $this->assertSame(['boolean', true], $array[$i++]); - $this->assertSame(['boolean', false], $array[$i++]); - $this->assertSame(['integer', 0], $array[$i++]); - $this->assertSame(['float', 0.0], $array[$i++]); - $this->assertSame(['string', '0'], $array[$i++]); - $this->assertSame(['string', ''], $array[$i++]); - $this->assertSame(['float', INF], $array[$i++]); - - // assertEquals() does not like NAN values. - $this->assertEquals($array[$i][0], 'float'); - $this->assertTrue(is_nan($array[$i++][1])); - } - - public function testRecursionInArguments() - { - $a = null; - $a = ['foo', [2, &$a]]; - $exception = $this->createException($a); - - $flattened = FlattenException::create($exception); - $trace = $flattened->getTrace(); - $this->assertContains('*DEEP NESTED ARRAY*', serialize($trace)); - } - - public function testTooBigArray() - { - $a = []; - for ($i = 0; $i < 20; ++$i) { - for ($j = 0; $j < 50; ++$j) { - for ($k = 0; $k < 10; ++$k) { - $a[$i][$j][$k] = 'value'; - } - } - } - $a[20] = 'value'; - $a[21] = 'value1'; - $exception = $this->createException($a); - - $flattened = FlattenException::create($exception); - $trace = $flattened->getTrace(); - - $this->assertSame($trace[1]['args'][0], ['array', ['array', '*SKIPPED over 10000 entries*']]); - - $serializeTrace = serialize($trace); - - $this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace); - $this->assertNotContains('*value1*', $serializeTrace); - } - - public function testAnonymousClass() - { - $flattened = FlattenException::create(new class() extends \RuntimeException { - }); - - $this->assertSame('RuntimeException@anonymous', $flattened->getClass()); - - $flattened = FlattenException::create(new \Exception(sprintf('Class "%s" blah.', \get_class(new class() extends \RuntimeException { - })))); - - $this->assertSame('Class "RuntimeException@anonymous" blah.', $flattened->getMessage()); - } - - public function testToStringEmptyMessage() - { - $exception = new \RuntimeException(); - - $flattened = FlattenException::create($exception); - - $this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString()); - $this->assertSame($exception->__toString(), $flattened->getAsString()); - } - - public function testToString() - { - $test = function ($a, $b, $c, $d) { - return new \RuntimeException('This is a test message'); - }; - - $exception = $test('foo123', 1, null, 1.5); - - $flattened = FlattenException::create($exception); - - $this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString()); - $this->assertSame($exception->__toString(), $flattened->getAsString()); - } - - public function testToStringParent() - { - $exception = new \LogicException('This is message 1'); - $exception = new \RuntimeException('This is messsage 2', 500, $exception); - - $flattened = FlattenException::create($exception); - - $this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString()); - $this->assertSame($exception->__toString(), $flattened->getAsString()); - } - - private function createException($foo) - { - return new \Exception(); - } -} diff --git a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php b/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php deleted file mode 100644 index 0bfb680d9bba..000000000000 --- a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php +++ /dev/null @@ -1,148 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Debug\Exception\OutOfMemoryException; -use Symfony\Component\Debug\ExceptionHandler; -use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -require_once __DIR__.'/HeaderMock.php'; - -/** - * @group legacy - */ -class ExceptionHandlerTest extends TestCase -{ - protected function setUp() - { - testHeader(); - } - - protected function tearDown() - { - testHeader(); - } - - /** - * @group legacy - */ - public function testDebug() - { - $handler = new ExceptionHandler(false); - - ob_start(); - $handler->sendPhpResponse(new \RuntimeException('Foo')); - $response = ob_get_clean(); - - $this->assertContains('Whoops, looks like something went wrong.', $response); - $this->assertNotContains('
', $response); - - $handler = new ExceptionHandler(true); - - ob_start(); - $handler->sendPhpResponse(new \RuntimeException('Foo')); - $response = ob_get_clean(); - - $this->assertContains('

Foo

', $response); - $this->assertContains('
', $response); - - // taken from https://www.owasp.org/index.php/Cross-site_Scripting_(XSS) - $htmlWithXss = ' click me! '; - ob_start(); - $handler->sendPhpResponse(new \RuntimeException($htmlWithXss)); - $response = ob_get_clean(); - - $this->assertContains(sprintf('

%s

', htmlspecialchars($htmlWithXss, ENT_COMPAT | ENT_SUBSTITUTE, 'UTF-8')), $response); - } - - public function testStatusCode() - { - $handler = new ExceptionHandler(false, 'iso8859-1'); - - ob_start(); - $handler->sendPhpResponse(new NotFoundHttpException('Foo')); - $response = ob_get_clean(); - - $this->assertContains('Sorry, the page you are looking for could not be found.', $response); - - $expectedHeaders = [ - ['HTTP/1.0 404', true, null], - ['Content-Type: text/html; charset=iso8859-1', true, null], - ]; - - $this->assertSame($expectedHeaders, testHeader()); - } - - public function testHeaders() - { - $handler = new ExceptionHandler(false, 'iso8859-1'); - - ob_start(); - $handler->sendPhpResponse(new MethodNotAllowedHttpException(['POST'])); - $response = ob_get_clean(); - - $expectedHeaders = [ - ['HTTP/1.0 405', true, null], - ['Allow: POST', false, null], - ['Content-Type: text/html; charset=iso8859-1', true, null], - ]; - - $this->assertSame($expectedHeaders, testHeader()); - } - - public function testNestedExceptions() - { - $handler = new ExceptionHandler(true); - ob_start(); - $handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); - $response = ob_get_clean(); - - $this->assertStringMatchesFormat('%A

Foo

%A

Bar

%A', $response); - } - - public function testHandle() - { - $exception = new \Exception('foo'); - - $handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock(); - $handler - ->expects($this->exactly(2)) - ->method('sendPhpResponse'); - - $handler->handle($exception); - - $handler->setHandler(function ($e) use ($exception) { - $this->assertSame($exception, $e); - }); - - $handler->handle($exception); - } - - public function testHandleOutOfMemoryException() - { - $exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__); - - $handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock(); - $handler - ->expects($this->once()) - ->method('sendPhpResponse'); - - $handler->setHandler(function ($e) { - $this->fail('OutOfMemoryException should bypass the handler'); - }); - - $handler->handle($exception); - } -} diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php deleted file mode 100644 index 498fafd47cd0..000000000000 --- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php +++ /dev/null @@ -1,183 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\FatalErrorHandler; - -use Composer\Autoload\ClassLoader as ComposerClassLoader; -use PHPUnit\Framework\TestCase; -use Symfony\Component\Debug\DebugClassLoader; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; - -/** - * @group legacy - */ -class ClassNotFoundFatalErrorHandlerTest extends TestCase -{ - public static function setUpBeforeClass() - { - foreach (spl_autoload_functions() as $function) { - if (!\is_array($function)) { - continue; - } - - // get class loaders wrapped by DebugClassLoader - if ($function[0] instanceof DebugClassLoader) { - $function = $function[0]->getClassLoader(); - } - - if ($function[0] instanceof ComposerClassLoader) { - $function[0]->add('Symfony_Component_Debug_Tests_Fixtures', \dirname(\dirname(\dirname(\dirname(\dirname(__DIR__)))))); - break; - } - } - } - - /** - * @dataProvider provideClassNotFoundData - */ - public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null) - { - if ($autoloader) { - // Unregister all autoloaders to ensure the custom provided - // autoloader is the only one to be used during the test run. - $autoloaders = spl_autoload_functions(); - array_map('spl_autoload_unregister', $autoloaders); - spl_autoload_register($autoloader); - } - - $handler = new ClassNotFoundFatalErrorHandler(); - - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - if ($autoloader) { - spl_autoload_unregister($autoloader); - array_map('spl_autoload_register', $autoloaders); - } - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); - $this->assertSame($translatedMessage, $exception->getMessage()); - $this->assertSame($error['type'], $exception->getSeverity()); - $this->assertSame($error['file'], $exception->getFile()); - $this->assertSame($error['line'], $exception->getLine()); - } - - public function provideClassNotFoundData() - { - $autoloader = new ComposerClassLoader(); - $autoloader->add('Symfony\Component\Debug\Exception\\', realpath(__DIR__.'/../../Exception')); - $autoloader->add('Symfony_Component_Debug_Tests_Fixtures', realpath(__DIR__.'/../../Tests/Fixtures')); - - $debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']); - - return [ - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'WhizBangFactory\' not found', - ], - "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?", - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found', - ], - "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'UndefinedFunctionException\' not found', - ], - "Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - [$debugClassLoader, 'loadClass'], - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'PEARClass\' not found', - ], - "Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?", - [$debugClassLoader, 'loadClass'], - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ], - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - [$debugClassLoader, 'loadClass'], - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ], - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - [$autoloader, 'loadClass'], - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ], - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - [$debugClassLoader, 'loadClass'], - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ], - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", - function ($className) { /* do nothing here */ }, - ], - ]; - } - - public function testCannotRedeclareClass() - { - if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) { - $this->markTestSkipped('Can only be run on case insensitive filesystems'); - } - - require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP'; - - $error = [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\RequiredTwice\' not found', - ]; - - $handler = new ClassNotFoundFatalErrorHandler(); - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); - } -} diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php deleted file mode 100644 index b827c4c67aeb..000000000000 --- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\FatalErrorHandler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; - -/** - * @group legacy - */ -class UndefinedFunctionFatalErrorHandlerTest extends TestCase -{ - /** - * @dataProvider provideUndefinedFunctionData - */ - public function testUndefinedFunction($error, $translatedMessage) - { - $handler = new UndefinedFunctionFatalErrorHandler(); - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception); - // class names are case insensitive and PHP do not return the same - $this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage())); - $this->assertSame($error['type'], $exception->getSeverity()); - $this->assertSame($error['file'], $exception->getFile()); - $this->assertSame($error['line'], $exception->getLine()); - } - - public function provideUndefinedFunctionData() - { - return [ - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function test_namespaced_function()', - ], - "Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', - ], - "Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function foo()', - ], - 'Attempted to call function "foo" from the global namespace.', - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()', - ], - 'Attempted to call function "foo" from namespace "Foo\Bar\Baz".', - ], - ]; - } -} - -function test_namespaced_function() -{ -} diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php deleted file mode 100644 index 7ea1b1f95e78..000000000000 --- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\FatalErrorHandler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; - -/** - * @group legacy - */ -class UndefinedMethodFatalErrorHandlerTest extends TestCase -{ - /** - * @dataProvider provideUndefinedMethodData - */ - public function testUndefinedMethod($error, $translatedMessage) - { - $handler = new UndefinedMethodFatalErrorHandler(); - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception); - $this->assertSame($translatedMessage, $exception->getMessage()); - $this->assertSame($error['type'], $exception->getSeverity()); - $this->assertSame($error['file'], $exception->getFile()); - $this->assertSame($error['line'], $exception->getLine()); - } - - public function provideUndefinedMethodData() - { - return [ - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined method SplObjectStorage::what()', - ], - 'Attempted to call an undefined method named "what" of class "SplObjectStorage".', - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined method SplObjectStorage::walid()', - ], - "Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?", - ], - [ - [ - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined method SplObjectStorage::offsetFet()', - ], - "Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?", - ], - [ - [ - 'type' => 1, - 'message' => 'Call to undefined method class@anonymous::test()', - 'file' => '/home/possum/work/symfony/test.php', - 'line' => 11, - ], - 'Attempted to call an undefined method named "test" of class "class@anonymous".', - ], - ]; - } -} diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/AnnotatedClass.php b/src/Symfony/Component/Debug/Tests/Fixtures/AnnotatedClass.php deleted file mode 100644 index 4eecb6d3f166..000000000000 --- a/src/Symfony/Component/Debug/Tests/Fixtures/AnnotatedClass.php +++ /dev/null @@ -1,13 +0,0 @@ -exception = $e; - } - - public function __toString() - { - try { - throw $this->exception; - } catch (\Exception $e) { - // Using user_error() here is on purpose so we do not forget - // that this alias also should work alongside with trigger_error(). - return trigger_error($e, E_USER_ERROR); - } - } -} diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/TraitWithAnnotatedParameters.php b/src/Symfony/Component/Debug/Tests/Fixtures/TraitWithAnnotatedParameters.php deleted file mode 100644 index 5eb5fbf84bc9..000000000000 --- a/src/Symfony/Component/Debug/Tests/Fixtures/TraitWithAnnotatedParameters.php +++ /dev/null @@ -1,13 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -function headers_sent() -{ - return false; -} - -function header($str, $replace = true, $status = null) -{ - Tests\testHeader($str, $replace, $status); -} - -namespace Symfony\Component\Debug\Tests; - -function testHeader() -{ - static $headers = []; - - if (!$h = \func_get_args()) { - $h = $headers; - $headers = []; - - return $h; - } - - $headers[] = \func_get_args(); -} diff --git a/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php b/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php deleted file mode 100644 index 2d6ce564d23a..000000000000 --- a/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use Symfony\Component\Debug\ExceptionHandler; - -class MockExceptionHandler extends ExceptionHandler -{ - public $e; - - public function handle(\Exception $e) - { - $this->e = $e; - } -} diff --git a/src/Symfony/Component/Debug/Tests/phpt/debug_class_loader.phpt b/src/Symfony/Component/Debug/Tests/phpt/debug_class_loader.phpt deleted file mode 100644 index 041affc14df6..000000000000 --- a/src/Symfony/Component/Debug/Tests/phpt/debug_class_loader.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -Test DebugClassLoader with previously loaded parents ---FILE-- - ---EXPECTF-- -%A -The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod". -The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod". diff --git a/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt b/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt deleted file mode 100644 index 25167a2c6aae..000000000000 --- a/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -Test catching fatal errors when handlers are nested ---INI-- -display_errors=0 ---FILE-- - ---EXPECTF-- -%A -object(Symfony\Component\Debug\Exception\ClassNotFoundException)#%d (8) { - ["message":protected]=> - string(131) "Attempted to load class "missing" from namespace "Symfony\Component\Debug". -Did you forget a "use" statement for another namespace?" - ["string":"Exception":private]=> - string(0) "" - ["code":protected]=> - int(0) - ["file":protected]=> - string(%d) "%s" - ["line":protected]=> - int(%d) - ["trace":"Exception":private]=> - array(%d) {%A} - ["previous":"Exception":private]=> - NULL - ["severity":protected]=> - int(1) -} diff --git a/src/Symfony/Component/Debug/Tests/phpt/exception_rethrown.phpt b/src/Symfony/Component/Debug/Tests/phpt/exception_rethrown.phpt deleted file mode 100644 index b743d93ad7c8..000000000000 --- a/src/Symfony/Component/Debug/Tests/phpt/exception_rethrown.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -Test rethrowing in custom exception handler ---FILE-- -setDefaultLogger(new TestLogger()); -ini_set('display_errors', 1); - -throw new \Exception('foo'); -?> ---EXPECTF-- -Uncaught Exception: foo -123 -Fatal error: Uncaught %s:25 -Stack trace: -%a diff --git a/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt b/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt deleted file mode 100644 index d0fa2411e6bd..000000000000 --- a/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -Test catching fatal errors when handlers are nested ---FILE-- -setExceptionHandler('print_r'); - -if (true) { - class Broken implements \JsonSerializable - { - } -} - -?> ---EXPECTF-- -array(1) { - [0]=> - string(37) "Error and exception handlers do match" -} -%A -object(Symfony\Component\Debug\Exception\FatalErrorException)#%d (%d) { - ["message":protected]=> - string(179) "Error: Class Symfony\Component\Debug\Broken contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (JsonSerializable::jsonSerialize)" -%a -} diff --git a/src/Symfony/Component/Debug/composer.json b/src/Symfony/Component/Debug/composer.json deleted file mode 100644 index 1f977bad7d37..000000000000 --- a/src/Symfony/Component/Debug/composer.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "symfony/debug", - "type": "library", - "description": "Symfony Debug Component", - "keywords": [], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": "^7.2.9", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": "<4.4" - }, - "require-dev": { - "symfony/http-kernel": "^4.4|^5.0" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\Debug\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - } -} diff --git a/src/Symfony/Component/Debug/phpunit.xml.dist b/src/Symfony/Component/Debug/phpunit.xml.dist deleted file mode 100644 index a51bbff93586..000000000000 --- a/src/Symfony/Component/Debug/phpunit.xml.dist +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - ./Tests/ - - - ./Resources/ext/tests/ - - - - - - ./ - - ./Tests - ./vendor - - - -