[ErrorHandler] Rework fatal error handlers
This commit is contained in:
parent
2b71c6f221
commit
aaa0cdf523
@ -20,7 +20,7 @@ use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalThrowableError;
|
||||
use Symfony\Component\ErrorHandler\Exception\ErrorException;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
@ -208,7 +208,7 @@ class Application extends BaseApplication
|
||||
|
||||
foreach ($this->registrationErrors as $error) {
|
||||
if (!$error instanceof \Exception) {
|
||||
$error = new FatalThrowableError($error);
|
||||
$error = new ErrorException($error);
|
||||
}
|
||||
|
||||
$this->doRenderException($error, $output);
|
||||
|
@ -44,7 +44,7 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Debug\ErrorHandler as LegacyErrorHandler;
|
||||
use Symfony\Component\Debug\Exception\FatalThrowableError as LegacyFatalThrowableError;
|
||||
use Symfony\Component\ErrorHandler\ErrorHandler;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalThrowableError;
|
||||
use Symfony\Component\ErrorHandler\Exception\ErrorException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
@ -129,7 +129,7 @@ class Application implements ResetInterface
|
||||
|
||||
$renderException = function (\Throwable $e) use ($output) {
|
||||
if (!$e instanceof \Exception) {
|
||||
$e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : (class_exists(LegacyFatalThrowableError::class) ? new LegacyFatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()));
|
||||
$e = class_exists(ErrorException::class) ? new ErrorException($e) : (class_exists(LegacyFatalThrowableError::class) ? new LegacyFatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()));
|
||||
}
|
||||
if ($output instanceof ConsoleOutputInterface) {
|
||||
$this->renderException($e, $output->getErrorOutput());
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ClassNotFoundException::class, \Symfony\Component\ErrorHandler\Error\ClassNotFoundError::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Class (or Trait or Interface) Not Found Exception.
|
||||
*
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\ClassNotFoundException instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\ClassNotFoundError instead.
|
||||
*/
|
||||
class ClassNotFoundException extends FatalErrorException
|
||||
{
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FatalErrorException::class, \Symfony\Component\ErrorHandler\Exception\ErrorException::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Fatal Error Exception.
|
||||
*
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\FatalErrorException instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\ErrorException instead.
|
||||
*/
|
||||
class FatalErrorException extends \ErrorException
|
||||
{
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FatalThrowableError::class, \Symfony\Component\ErrorHandler\Exception\ErrorException::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Fatal Throwable Error.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\FatalThrowableError instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\ErrorException instead.
|
||||
*/
|
||||
class FatalThrowableError extends FatalErrorException
|
||||
{
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', OutOfMemoryException::class, \Symfony\Component\ErrorHandler\Error\OutOfMemoryError::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Out of memory exception.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\OutOfMemoryException instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\OutOfMemoryError instead.
|
||||
*/
|
||||
class OutOfMemoryException extends FatalErrorException
|
||||
{
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedFunctionException::class, \Symfony\Component\ErrorHandler\Error\UndefinedFunctionError::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Undefined Function Exception.
|
||||
*
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\UndefinedFunctionException instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\UndefinedFunctionError instead.
|
||||
*/
|
||||
class UndefinedFunctionException extends FatalErrorException
|
||||
{
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedMethodException::class, \Symfony\Component\ErrorHandler\Error\UndefinedMethodError::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Undefined Method Exception.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\UndefinedMethodException instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\UndefinedMethodError instead.
|
||||
*/
|
||||
class UndefinedMethodException extends FatalErrorException
|
||||
{
|
||||
|
@ -14,14 +14,14 @@ 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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedFunctionFatalErrorHandler::class, \Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* ErrorHandler for undefined functions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedFunctionFatalErrorHandler instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer instead.
|
||||
*/
|
||||
class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
{
|
||||
|
@ -14,14 +14,14 @@ 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);
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedMethodFatalErrorHandler::class, \Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* ErrorHandler for undefined methods.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedMethodFatalErrorHandler instead.
|
||||
* @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer instead.
|
||||
*/
|
||||
class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
{
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Error;
|
||||
|
||||
class ClassNotFoundError extends \Error
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(string $message, \Throwable $previous)
|
||||
{
|
||||
parent::__construct($message, $previous->getCode(), $previous->getPrevious());
|
||||
|
||||
foreach ([
|
||||
'file' => $previous->getFile(),
|
||||
'line' => $previous->getLine(),
|
||||
'trace' => $previous->getTrace(),
|
||||
] as $property => $value) {
|
||||
$refl = new \ReflectionProperty(\Error::class, $property);
|
||||
$refl->setAccessible(true);
|
||||
$refl->setValue($this, $value);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,18 +9,22 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Exception;
|
||||
namespace Symfony\Component\ErrorHandler\Error;
|
||||
|
||||
/**
|
||||
* Fatal Error Exception.
|
||||
*
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
*/
|
||||
class FatalErrorException extends \ErrorException
|
||||
class FatalError extends \Error
|
||||
{
|
||||
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)
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $error An array as returned by error_get_last()
|
||||
*/
|
||||
public function __construct(string $message, int $code, array $error, int $traceOffset = null, bool $traceArgs = true, array $trace = null)
|
||||
{
|
||||
parent::__construct($message, $code, $severity, $filename, $lineno, $previous);
|
||||
parent::__construct($message, $code);
|
||||
|
||||
$this->error = $error;
|
||||
|
||||
if (null !== $trace) {
|
||||
if (!$traceArgs) {
|
||||
@ -28,8 +32,6 @@ class FatalErrorException extends \ErrorException
|
||||
unset($frame['args'], $frame['this'], $frame);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setTrace($trace);
|
||||
} elseif (null !== $traceOffset) {
|
||||
if (\function_exists('xdebug_get_function_stack')) {
|
||||
$trace = xdebug_get_function_stack();
|
||||
@ -63,15 +65,24 @@ class FatalErrorException extends \ErrorException
|
||||
} else {
|
||||
$trace = [];
|
||||
}
|
||||
}
|
||||
|
||||
$this->setTrace($trace);
|
||||
foreach ([
|
||||
'file' => $error['file'],
|
||||
'line' => $error['line'],
|
||||
'trace' => $trace,
|
||||
] as $property => $value) {
|
||||
$refl = new \ReflectionProperty(\Error::class, $property);
|
||||
$refl->setAccessible(true);
|
||||
$refl->setValue($this, $value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function setTrace(array $trace): void
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getError(): array
|
||||
{
|
||||
$traceReflector = new \ReflectionProperty('Exception', 'trace');
|
||||
$traceReflector->setAccessible(true);
|
||||
$traceReflector->setValue($this, $trace);
|
||||
return $this->error;
|
||||
}
|
||||
}
|
@ -9,13 +9,8 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Exception;
|
||||
namespace Symfony\Component\ErrorHandler\Error;
|
||||
|
||||
/**
|
||||
* Out of memory exception.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class OutOfMemoryException extends FatalErrorException
|
||||
class OutOfMemoryError extends FatalError
|
||||
{
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Error;
|
||||
|
||||
class UndefinedFunctionError extends \Error
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(string $message, \Throwable $previous)
|
||||
{
|
||||
parent::__construct($message, $previous->getCode(), $previous->getPrevious());
|
||||
|
||||
foreach ([
|
||||
'file' => $previous->getFile(),
|
||||
'line' => $previous->getLine(),
|
||||
'trace' => $previous->getTrace(),
|
||||
] as $property => $value) {
|
||||
$refl = new \ReflectionProperty(\Error::class, $property);
|
||||
$refl->setAccessible(true);
|
||||
$refl->setValue($this, $value);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Error;
|
||||
|
||||
class UndefinedMethodError extends \Error
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(string $message, \Throwable $previous)
|
||||
{
|
||||
parent::__construct($message, $previous->getCode(), $previous->getPrevious());
|
||||
|
||||
foreach ([
|
||||
'file' => $previous->getFile(),
|
||||
'line' => $previous->getLine(),
|
||||
'trace' => $previous->getTrace(),
|
||||
] as $property => $value) {
|
||||
$refl = new \ReflectionProperty(\Error::class, $property);
|
||||
$refl->setAccessible(true);
|
||||
$refl->setValue($this, $value);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,45 +9,45 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\FatalErrorHandler;
|
||||
namespace Symfony\Component\ErrorHandler\ErrorEnhancer;
|
||||
|
||||
use Composer\Autoload\ClassLoader as ComposerClassLoader;
|
||||
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
|
||||
use Symfony\Component\ErrorHandler\DebugClassLoader;
|
||||
use Symfony\Component\ErrorHandler\Exception\ClassNotFoundException;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
use Symfony\Component\ErrorHandler\Error\ClassNotFoundError;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
|
||||
/**
|
||||
* ErrorHandler for classes that do not exist.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
class ClassNotFoundErrorEnhancer implements ErrorEnhancerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleError(array $error, FatalErrorException $exception)
|
||||
public function enhance(\Throwable $error): ?\Throwable
|
||||
{
|
||||
$messageLen = \strlen($error['message']);
|
||||
// Some specific versions of PHP produce a fatal error when extending a not found class.
|
||||
$message = !$error instanceof FatalError ? $error->getMessage() : $error->getError()['message'];
|
||||
$messageLen = \strlen($message);
|
||||
$notFoundSuffix = '\' not found';
|
||||
$notFoundSuffixLen = \strlen($notFoundSuffix);
|
||||
if ($notFoundSuffixLen > $messageLen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
|
||||
if (0 !== substr_compare($message, $notFoundSuffix, -$notFoundSuffixLen)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (['class', 'interface', 'trait'] as $typeName) {
|
||||
$prefix = ucfirst($typeName).' \'';
|
||||
$prefixLen = \strlen($prefix);
|
||||
if (0 !== strpos($error['message'], $prefix)) {
|
||||
if (0 !== strpos($message, $prefix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
|
||||
$fullyQualifiedClassName = substr($message, $prefixLen, -$notFoundSuffixLen);
|
||||
if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) {
|
||||
$className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1);
|
||||
$namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex);
|
||||
@ -69,8 +69,10 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
}
|
||||
$message .= "\nDid you forget a \"use\" statement".$tail;
|
||||
|
||||
return new ClassNotFoundException($message, $exception);
|
||||
return new ClassNotFoundError($message, $error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,7 +83,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
*
|
||||
* @param string $class A class name (without its namespace)
|
||||
*
|
||||
* @return array An array of possible fully qualified class names
|
||||
* Returns an array of possible fully qualified class names
|
||||
*/
|
||||
private function getClassCandidates(string $class): array
|
||||
{
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\ErrorEnhancer;
|
||||
|
||||
interface ErrorEnhancerInterface
|
||||
{
|
||||
/**
|
||||
* Returns an \Throwable instance if the class is able to improve the error, null otherwise.
|
||||
*/
|
||||
public function enhance(\Throwable $error): ?\Throwable;
|
||||
}
|
@ -9,41 +9,44 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\FatalErrorHandler;
|
||||
namespace Symfony\Component\ErrorHandler\ErrorEnhancer;
|
||||
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
use Symfony\Component\ErrorHandler\Exception\UndefinedFunctionException;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
use Symfony\Component\ErrorHandler\Error\UndefinedFunctionError;
|
||||
|
||||
/**
|
||||
* ErrorHandler for undefined functions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
class UndefinedFunctionErrorEnhancer implements ErrorEnhancerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleError(array $error, FatalErrorException $exception)
|
||||
public function enhance(\Throwable $error): ?\Throwable
|
||||
{
|
||||
$messageLen = \strlen($error['message']);
|
||||
if ($error instanceof FatalError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$message = $error->getMessage();
|
||||
$messageLen = \strlen($message);
|
||||
$notFoundSuffix = '()';
|
||||
$notFoundSuffixLen = \strlen($notFoundSuffix);
|
||||
if ($notFoundSuffixLen > $messageLen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
|
||||
if (0 !== substr_compare($message, $notFoundSuffix, -$notFoundSuffixLen)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$prefix = 'Call to undefined function ';
|
||||
$prefixLen = \strlen($prefix);
|
||||
if (0 !== strpos($error['message'], $prefix)) {
|
||||
if (0 !== strpos($message, $prefix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
|
||||
$fullyQualifiedFunctionName = substr($message, $prefixLen, -$notFoundSuffixLen);
|
||||
if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) {
|
||||
$functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1);
|
||||
$namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex);
|
||||
@ -79,6 +82,6 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
$message .= "\nDid you mean to call ".$candidates;
|
||||
}
|
||||
|
||||
return new UndefinedFunctionException($message, $exception);
|
||||
return new UndefinedFunctionError($message, $error);
|
||||
}
|
||||
}
|
@ -9,24 +9,27 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\FatalErrorHandler;
|
||||
namespace Symfony\Component\ErrorHandler\ErrorEnhancer;
|
||||
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
use Symfony\Component\ErrorHandler\Exception\UndefinedMethodException;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
use Symfony\Component\ErrorHandler\Error\UndefinedMethodError;
|
||||
|
||||
/**
|
||||
* ErrorHandler for undefined methods.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
class UndefinedMethodErrorEnhancer implements ErrorEnhancerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleError(array $error, FatalErrorException $exception)
|
||||
public function enhance(\Throwable $error): ?\Throwable
|
||||
{
|
||||
preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches);
|
||||
if ($error instanceof FatalError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$message = $error->getMessage();
|
||||
preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $message, $matches);
|
||||
if (!$matches) {
|
||||
return null;
|
||||
}
|
||||
@ -38,7 +41,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
|
||||
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);
|
||||
return new UndefinedMethodError($message, $error);
|
||||
}
|
||||
|
||||
$candidates = [];
|
||||
@ -61,6 +64,6 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
$message .= "\nDid you mean to call ".$candidates;
|
||||
}
|
||||
|
||||
return new UndefinedMethodException($message, $exception);
|
||||
return new UndefinedMethodError($message, $error);
|
||||
}
|
||||
}
|
@ -13,15 +13,13 @@ namespace Symfony\Component\ErrorHandler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
use Symfony\Component\ErrorHandler\Exception\OutOfMemoryException;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
use Symfony\Component\ErrorHandler\Error\OutOfMemoryError;
|
||||
use Symfony\Component\ErrorHandler\ErrorEnhancer\ClassNotFoundErrorEnhancer;
|
||||
use Symfony\Component\ErrorHandler\ErrorEnhancer\ErrorEnhancerInterface;
|
||||
use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer;
|
||||
use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer;
|
||||
use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext;
|
||||
use Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
|
||||
use Symfony\Component\ErrorHandler\FatalErrorHandler\FatalErrorHandlerInterface;
|
||||
use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
||||
use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
||||
use Symfony\Component\ErrorRenderer\ErrorRenderer\HtmlErrorRenderer;
|
||||
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
|
||||
|
||||
/**
|
||||
* A generic ErrorHandler for the PHP engine.
|
||||
@ -538,64 +536,50 @@ class ErrorHandler
|
||||
/**
|
||||
* Handles an exception by logging then forwarding it to another handler.
|
||||
*
|
||||
* @param array $error An array as returned by error_get_last()
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function handleException(\Throwable $exception, array $error = null)
|
||||
public function handleException(\Throwable $exception)
|
||||
{
|
||||
if (null === $error) {
|
||||
self::$exitCode = 255;
|
||||
}
|
||||
|
||||
$type = ThrowableUtils::getSeverity($exception);
|
||||
$handlerException = null;
|
||||
|
||||
if (($this->loggedErrors & $type) || $exception instanceof \Error) {
|
||||
if (!$exception instanceof FatalError) {
|
||||
self::$exitCode = 255;
|
||||
|
||||
$type = ThrowableUtils::getSeverity($exception);
|
||||
} else {
|
||||
$type = $exception->getError()['type'];
|
||||
}
|
||||
|
||||
if ($this->loggedErrors & $type) {
|
||||
if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
|
||||
$message = $this->parseAnonymousClass($message);
|
||||
}
|
||||
|
||||
if ($exception instanceof FatalErrorException) {
|
||||
if ($exception instanceof FatalError) {
|
||||
$message = 'Fatal '.$message;
|
||||
} elseif ($exception instanceof \Error) {
|
||||
$message = 'Uncaught Error: '.$message;
|
||||
} elseif ($exception instanceof \ErrorException) {
|
||||
$message = 'Uncaught '.$message;
|
||||
} elseif ($exception instanceof \Error) {
|
||||
$error = [
|
||||
'type' => $type,
|
||||
'message' => $message,
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
];
|
||||
$message = 'Uncaught Error: '.$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) {
|
||||
}
|
||||
}
|
||||
|
||||
// temporary until fatal error handlers rework
|
||||
$originalException = $exception;
|
||||
if (!$exception instanceof \Exception) {
|
||||
$exception = new FatalErrorException($exception->getMessage(), $exception->getCode(), $type, $exception->getFile(), $exception->getLine(), null, true, $exception->getTrace());
|
||||
}
|
||||
|
||||
if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
|
||||
foreach ($this->getFatalErrorHandlers() as $handler) {
|
||||
if ($e = $handler->handleError($error, $exception)) {
|
||||
$convertedException = $e;
|
||||
if (!$exception instanceof OutOfMemoryError) {
|
||||
foreach ($this->getErrorEnhancers() as $errorEnhancer) {
|
||||
if ($e = $errorEnhancer->enhance($exception)) {
|
||||
$exception = $e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$exception = $convertedException ?? $originalException;
|
||||
$exceptionHandler = $this->exceptionHandler;
|
||||
if ((!\is_array($exceptionHandler) || !$exceptionHandler[0] instanceof self || 'sendPhpResponse' !== $exceptionHandler[1]) && !\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
|
||||
$this->exceptionHandler = [$this, 'sendPhpResponse'];
|
||||
@ -613,6 +597,7 @@ class ErrorHandler
|
||||
self::$reservedMemory = null; // Disable the fatal error handler
|
||||
throw $exception; // Give back $exception to the native handler
|
||||
}
|
||||
|
||||
$this->handleException($handlerException);
|
||||
}
|
||||
|
||||
@ -673,20 +658,20 @@ class ErrorHandler
|
||||
$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);
|
||||
$fatalError = new OutOfMemoryError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, false, $trace);
|
||||
} else {
|
||||
$exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
|
||||
$fatalError = new FatalError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, true, $trace);
|
||||
}
|
||||
} else {
|
||||
$exception = null;
|
||||
$fatalError = null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null !== $exception) {
|
||||
if (null !== $fatalError) {
|
||||
self::$exitCode = 255;
|
||||
$handler->handleException($exception, $error);
|
||||
$handler->handleException($fatalError);
|
||||
}
|
||||
} catch (FatalErrorException $e) {
|
||||
} catch (FatalError $e) {
|
||||
// Ignore this re-throw
|
||||
}
|
||||
|
||||
@ -730,18 +715,16 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fatal error handlers.
|
||||
* Override this method if you want to define more error enhancers.
|
||||
*
|
||||
* Override this method if you want to define more fatal error handlers.
|
||||
*
|
||||
* @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
|
||||
* @return ErrorEnhancerInterface[]
|
||||
*/
|
||||
protected function getFatalErrorHandlers(): array
|
||||
protected function getErrorEnhancers(): iterable
|
||||
{
|
||||
return [
|
||||
new UndefinedFunctionFatalErrorHandler(),
|
||||
new UndefinedMethodFatalErrorHandler(),
|
||||
new ClassNotFoundFatalErrorHandler(),
|
||||
new UndefinedFunctionErrorEnhancer(),
|
||||
new UndefinedMethodErrorEnhancer(),
|
||||
new ClassNotFoundErrorEnhancer(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Exception;
|
||||
|
||||
/**
|
||||
* Class (or Trait or Interface) Not Found Exception.
|
||||
*
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
@ -13,12 +13,7 @@ namespace Symfony\Component\ErrorHandler\Exception;
|
||||
|
||||
use Symfony\Component\ErrorHandler\ThrowableUtils;
|
||||
|
||||
/**
|
||||
* Fatal Throwable Error.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class FatalThrowableError extends FatalErrorException
|
||||
class ErrorException extends \ErrorException
|
||||
{
|
||||
private $originalClassName;
|
||||
|
||||
@ -26,7 +21,7 @@ class FatalThrowableError extends FatalErrorException
|
||||
{
|
||||
$this->originalClassName = \get_class($e);
|
||||
|
||||
\ErrorException::__construct(
|
||||
parent::__construct(
|
||||
$e->getMessage(),
|
||||
$e->getCode(),
|
||||
ThrowableUtils::getSeverity($e),
|
||||
@ -35,7 +30,9 @@ class FatalThrowableError extends FatalErrorException
|
||||
$e->getPrevious()
|
||||
);
|
||||
|
||||
$this->setTrace($e->getTrace());
|
||||
$refl = new \ReflectionProperty(\Exception::class, 'trace');
|
||||
$refl->setAccessible(true);
|
||||
$refl->setValue($this, $e->getTrace());
|
||||
}
|
||||
|
||||
public function getOriginalClassName(): string
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Exception;
|
||||
|
||||
/**
|
||||
* Undefined Function Exception.
|
||||
*
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Exception;
|
||||
|
||||
/**
|
||||
* Undefined Method Exception.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\FatalErrorHandler;
|
||||
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Attempts to convert fatal errors to exceptions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface FatalErrorHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Attempts to convert an error into an exception.
|
||||
*
|
||||
* @param array $error An array as returned by error_get_last()
|
||||
*
|
||||
* @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise
|
||||
*/
|
||||
public function handleError(array $error, FatalErrorException $exception);
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Tests\ErrorEnhancer;
|
||||
|
||||
use Composer\Autoload\ClassLoader as ComposerClassLoader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\DebugClassLoader;
|
||||
use Symfony\Component\ErrorHandler\Error\ClassNotFoundError;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
use Symfony\Component\ErrorHandler\ErrorEnhancer\ClassNotFoundErrorEnhancer;
|
||||
|
||||
class ClassNotFoundErrorEnhancerTest extends TestCase
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
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_ErrorHandler_Tests_Fixtures', \dirname(__DIR__, 5));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideClassNotFoundData
|
||||
*/
|
||||
public function testEnhance(string $originalMessage, string $enhancedMessage, $autoloader = null)
|
||||
{
|
||||
try {
|
||||
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);
|
||||
}
|
||||
|
||||
$expectedLine = __LINE__ + 1;
|
||||
$error = (new ClassNotFoundErrorEnhancer())->enhance(new \Error($originalMessage));
|
||||
} finally {
|
||||
if ($autoloader) {
|
||||
spl_autoload_unregister($autoloader);
|
||||
array_map('spl_autoload_register', $autoloaders);
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertInstanceOf(ClassNotFoundError::class, $error);
|
||||
$this->assertSame($enhancedMessage, $error->getMessage());
|
||||
$this->assertSame(realpath(__FILE__), $error->getFile());
|
||||
$this->assertSame($expectedLine, $error->getLine());
|
||||
}
|
||||
|
||||
public function provideClassNotFoundData()
|
||||
{
|
||||
$autoloader = new ComposerClassLoader();
|
||||
$autoloader->add('Symfony\Component\ErrorHandler\Error\\', realpath(__DIR__.'/../../Error'));
|
||||
$autoloader->add('Symfony_Component_ErrorHandler_Tests_Fixtures', realpath(__DIR__.'/../../Tests/Fixtures'));
|
||||
|
||||
$debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']);
|
||||
|
||||
return [
|
||||
[
|
||||
'Class \'WhizBangFactory\' not found',
|
||||
"Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
|
||||
],
|
||||
[
|
||||
'Class \'Foo\\Bar\\WhizBangFactory\' not found',
|
||||
"Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
|
||||
],
|
||||
[
|
||||
'Class \'UndefinedFunctionError\' not found',
|
||||
"Attempted to load class \"UndefinedFunctionError\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
|
||||
[$debugClassLoader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
'Class \'PEARClass\' not found',
|
||||
"Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_ErrorHandler_Tests_Fixtures_PEARClass\"?",
|
||||
[$debugClassLoader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
|
||||
"Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
|
||||
[$debugClassLoader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
|
||||
"Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
|
||||
[$autoloader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
|
||||
"Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
|
||||
[$debugClassLoader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
|
||||
"Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
|
||||
function ($className) { /* do nothing here */ },
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testEnhanceWithFatalError()
|
||||
{
|
||||
$error = (new ClassNotFoundErrorEnhancer())->enhance(new FatalError('foo', 0, [
|
||||
'type' => E_ERROR,
|
||||
'message' => "Class 'FooBarCcc' not found",
|
||||
'file' => $expectedFile = realpath(__FILE__),
|
||||
'line' => $expectedLine = __LINE__,
|
||||
]));
|
||||
|
||||
$this->assertInstanceOf(ClassNotFoundError::class, $error);
|
||||
$this->assertSame("Attempted to load class \"FooBarCcc\" from the global namespace.\nDid you forget a \"use\" statement?", $error->getMessage());
|
||||
$this->assertSame($expectedFile, $error->getFile());
|
||||
$this->assertSame($expectedLine, $error->getLine());
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
$enhancer = new ClassNotFoundErrorEnhancer();
|
||||
$error = $enhancer->enhance(new \Error("Class 'Foo\\Bar\\RequiredTwice' not found"));
|
||||
|
||||
$this->assertInstanceOf(ClassNotFoundError::class, $error);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Tests\ErrorEnhancer;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\Error\UndefinedFunctionError;
|
||||
use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer;
|
||||
|
||||
class UndefinedFunctionErrorEnhancerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideUndefinedFunctionData
|
||||
*/
|
||||
public function testEnhance(string $originalMessage, string $enhancedMessage)
|
||||
{
|
||||
$enhancer = new UndefinedFunctionErrorEnhancer();
|
||||
|
||||
$expectedLine = __LINE__ + 1;
|
||||
$error = $enhancer->enhance(new \Error($originalMessage));
|
||||
|
||||
$this->assertInstanceOf(UndefinedFunctionError::class, $error);
|
||||
// class names are case insensitive and PHP do not return the same
|
||||
$this->assertSame(strtolower($enhancedMessage), strtolower($error->getMessage()));
|
||||
$this->assertSame(realpath(__FILE__), $error->getFile());
|
||||
$this->assertSame($expectedLine, $error->getLine());
|
||||
}
|
||||
|
||||
public function provideUndefinedFunctionData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'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\\errorhandler\\tests\\errorenhancer\\test_namespaced_function\"?",
|
||||
],
|
||||
[
|
||||
'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\\errorhandler\\tests\\errorenhancer\\test_namespaced_function\"?",
|
||||
],
|
||||
[
|
||||
'Call to undefined function foo()',
|
||||
'Attempted to call function "foo" from the global namespace.',
|
||||
],
|
||||
[
|
||||
'Call to undefined function Foo\\Bar\\Baz\\foo()',
|
||||
'Attempted to call function "foo" from namespace "Foo\Bar\Baz".',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function test_namespaced_function()
|
||||
{
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Tests\ErrorEnhancer;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\Error\UndefinedMethodError;
|
||||
use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer;
|
||||
|
||||
class UndefinedMethodErrorEnhancerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideUndefinedMethodData
|
||||
*/
|
||||
public function testEnhance(string $originalMessage, string $enhancedMessage)
|
||||
{
|
||||
$enhancer = new UndefinedMethodErrorEnhancer();
|
||||
|
||||
$expectedLine = __LINE__ + 1;
|
||||
$error = $enhancer->enhance(new \Error($originalMessage));
|
||||
|
||||
$this->assertInstanceOf(UndefinedMethodError::class, $error);
|
||||
$this->assertSame($enhancedMessage, $error->getMessage());
|
||||
$this->assertSame(realpath(__FILE__), $error->getFile());
|
||||
$this->assertSame($expectedLine, $error->getLine());
|
||||
}
|
||||
|
||||
public function provideUndefinedMethodData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'Call to undefined method SplObjectStorage::what()',
|
||||
'Attempted to call an undefined method named "what" of class "SplObjectStorage".',
|
||||
],
|
||||
[
|
||||
'Call to undefined method SplObjectStorage::walid()',
|
||||
"Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?",
|
||||
],
|
||||
[
|
||||
'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\"?",
|
||||
],
|
||||
[
|
||||
'Call to undefined method class@anonymous::test()',
|
||||
'Attempted to call an undefined method named "test" of class "class@anonymous".',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@ use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\ErrorHandler\BufferingLogger;
|
||||
use Symfony\Component\ErrorHandler\Error\ClassNotFoundError;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
use Symfony\Component\ErrorHandler\ErrorHandler;
|
||||
use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext;
|
||||
use Symfony\Component\ErrorHandler\Tests\Fixtures\ErrorHandlerThatUsesThePreviousOne;
|
||||
@ -518,7 +520,7 @@ class ErrorHandlerTest extends TestCase
|
||||
$logArgCheck = function ($level, $message, $context) {
|
||||
$this->assertEquals('Fatal Parse Error: foo', $message);
|
||||
$this->assertArrayHasKey('exception', $context);
|
||||
$this->assertInstanceOf(\Exception::class, $context['exception']);
|
||||
$this->assertInstanceOf(FatalError::class, $context['exception']);
|
||||
};
|
||||
|
||||
$logger
|
||||
@ -552,7 +554,7 @@ class ErrorHandlerTest extends TestCase
|
||||
|
||||
$handler->handleException($exception);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\ErrorHandler\Exception\ClassNotFoundException', $args[0]);
|
||||
$this->assertInstanceOf(ClassNotFoundError::class, $args[0]);
|
||||
$this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
|
||||
}
|
||||
|
||||
|
@ -1,180 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Tests\FatalErrorHandler;
|
||||
|
||||
use Composer\Autoload\ClassLoader as ComposerClassLoader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\DebugClassLoader;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
use Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
|
||||
|
||||
class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
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_ErrorHandler_Tests_Fixtures', \dirname(__DIR__, 5));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideClassNotFoundData
|
||||
*/
|
||||
public function testHandleClassNotFound(array $error, string $translatedMessage, callable $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\ErrorHandler\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(): array
|
||||
{
|
||||
$autoloader = new ComposerClassLoader();
|
||||
$autoloader->add('Symfony\Component\ErrorHandler\Exception\\', realpath(__DIR__.'/../../Exception'));
|
||||
$autoloader->add('Symfony_Component_ErrorHandler_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\ErrorHandler\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_ErrorHandler_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\ErrorHandler\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\ErrorHandler\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\ErrorHandler\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\ErrorHandler\Exception\ClassNotFoundException', $exception);
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Tests\FatalErrorHandler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
||||
|
||||
class UndefinedFunctionFatalErrorHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideUndefinedFunctionData
|
||||
*/
|
||||
public function testUndefinedFunction(array $error, string $translatedMessage)
|
||||
{
|
||||
$handler = new UndefinedFunctionFatalErrorHandler();
|
||||
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\ErrorHandler\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(): array
|
||||
{
|
||||
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\\errorhandler\\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\\errorhandler\\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()
|
||||
{
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\ErrorHandler\Tests\FatalErrorHandler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
|
||||
use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
||||
|
||||
class UndefinedMethodFatalErrorHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideUndefinedMethodData
|
||||
*/
|
||||
public function testUndefinedMethod(array $error, string $translatedMessage)
|
||||
{
|
||||
$handler = new UndefinedMethodFatalErrorHandler();
|
||||
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\ErrorHandler\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(): array
|
||||
{
|
||||
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".',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -23,14 +23,12 @@ if (true) {
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
object(Symfony\Component\ErrorHandler\Exception\ClassNotFoundException)#%d (8) {
|
||||
object(Symfony\Component\ErrorHandler\Error\ClassNotFoundError)#%d (7) {
|
||||
["message":protected]=>
|
||||
string(138) "Attempted to load class "missing" from namespace "Symfony\Component\ErrorHandler".
|
||||
Did you forget a "use" statement for another namespace?"
|
||||
["string":"Exception":private]=>
|
||||
["string":"Error":private]=>
|
||||
string(0) ""
|
||||
["code":protected]=>
|
||||
int(0)
|
||||
@ -38,10 +36,8 @@ Did you forget a "use" statement for another namespace?"
|
||||
string(%d) "%s"
|
||||
["line":protected]=>
|
||||
int(%d)
|
||||
["trace":"Exception":private]=>
|
||||
["trace":"Error":private]=>
|
||||
array(%d) {%A}
|
||||
["previous":"Exception":private]=>
|
||||
["previous":"Error":private]=>
|
||||
NULL
|
||||
["severity":protected]=>
|
||||
int(1)
|
||||
}
|
@ -35,7 +35,18 @@ array(1) {
|
||||
[0]=>
|
||||
string(37) "Error and exception handlers do match"
|
||||
}
|
||||
object(Symfony\Component\ErrorHandler\Exception\FatalErrorException)#%d (%d) {
|
||||
object(Symfony\Component\ErrorHandler\Error\FatalError)#%d (%d) {
|
||||
["error":"Symfony\Component\ErrorHandler\Error\FatalError":private]=>
|
||||
array(4) {
|
||||
["type"]=>
|
||||
int(1)
|
||||
["message"]=>
|
||||
string(179) "Class Symfony\Component\ErrorHandler\Broken contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (JsonSerializable::jsonSerialize)"
|
||||
["file"]=>
|
||||
string(%d) "%s"
|
||||
["line"]=>
|
||||
int(%d)
|
||||
}
|
||||
["message":protected]=>
|
||||
string(186) "Error: Class Symfony\Component\ErrorHandler\Broken contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (JsonSerializable::jsonSerialize)"
|
||||
%a
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\ErrorRenderer\Exception;
|
||||
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalThrowableError;
|
||||
use Symfony\Component\ErrorHandler\Exception\ErrorException;
|
||||
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
@ -64,7 +64,7 @@ class FlattenException
|
||||
$e->setStatusCode($statusCode);
|
||||
$e->setHeaders($headers);
|
||||
$e->setTraceFromThrowable($exception);
|
||||
$e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception));
|
||||
$e->setClass($exception instanceof ErrorException ? $exception->getOriginalClassName() : \get_class($exception));
|
||||
$e->setFile($exception->getFile());
|
||||
$e->setLine($exception->getLine());
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
namespace Symfony\Component\ErrorRenderer\Tests\Exception;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalThrowableError;
|
||||
use Symfony\Component\ErrorHandler\Exception\ErrorException;
|
||||
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
|
||||
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
@ -132,7 +132,7 @@ class FlattenExceptionTest extends TestCase
|
||||
|
||||
public function testWrappedThrowable()
|
||||
{
|
||||
$exception = new FatalThrowableError(new \DivisionByZeroError('Ouch', 42));
|
||||
$exception = new ErrorException(new \DivisionByZeroError('Ouch', 42));
|
||||
$flattened = FlattenException::createFromThrowable($exception);
|
||||
|
||||
$this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.');
|
||||
|
@ -16,7 +16,7 @@ use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Event\ConsoleEvent;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\ErrorHandler\ErrorHandler;
|
||||
use Symfony\Component\ErrorHandler\Exception\FatalThrowableError;
|
||||
use Symfony\Component\ErrorHandler\Exception\ErrorException;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
|
||||
@ -113,7 +113,7 @@ class DebugHandlersListener implements EventSubscriberInterface
|
||||
}
|
||||
|
||||
if (!$e instanceof \Exception) {
|
||||
$e = new FatalThrowableError($e);
|
||||
$e = new ErrorException($e);
|
||||
}
|
||||
|
||||
$hasRun = true;
|
||||
@ -127,7 +127,7 @@ class DebugHandlersListener implements EventSubscriberInterface
|
||||
}
|
||||
$this->exceptionHandler = static function (\Throwable $e) use ($app, $output) {
|
||||
if (!$e instanceof \Exception) {
|
||||
$e = new FatalThrowableError($e);
|
||||
$e = new ErrorException($e);
|
||||
}
|
||||
|
||||
$app->renderException($e, $output);
|
||||
|
Reference in New Issue
Block a user