Skip to content

Commit

Permalink
feature #33053 [ErrorHandler] Rework fatal errors (fancyweb)
Browse files Browse the repository at this point in the history
This PR was merged into the 4.4 branch.

Discussion
----------

[ErrorHandler] Rework fatal errors

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #32605
| License       | MIT
| Doc PR        | -

Built on top of #33038 so review only the second commit : d5c3f7e

The goals of this PR is to replace current "fatal error handlers" with "error enhancers" since all our current fatal error handlers works on \Error since PHP7.

That means we won't use the FatalErrorException anymore, so we will be able to remove it (once we don't need it in the rest of the codebase).

The final goal btw is to handle \Throwable everywhere in the code so we can remove FatalThrowableError & FatalErrorException classes.

Commits
-------

aaa0cdf [ErrorHandler] Rework fatal error handlers
  • Loading branch information
Tobion committed Oct 11, 2019
2 parents 28f9536 + aaa0cdf commit 38b9a27
Show file tree
Hide file tree
Showing 37 changed files with 546 additions and 632 deletions.
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/FrameworkBundle/Console/Application.php
Expand Up @@ -20,7 +20,7 @@
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;
Expand Down Expand Up @@ -208,7 +208,7 @@ private function renderRegistrationErrors(InputInterface $input, OutputInterface

foreach ($this->registrationErrors as $error) {
if (!$error instanceof \Exception) {
$error = new FatalThrowableError($error);
$error = new ErrorException($error);
}

$this->doRenderException($error, $output);
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/Console/Application.php
Expand Up @@ -44,7 +44,7 @@
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;
Expand Down Expand Up @@ -129,7 +129,7 @@ public function run(InputInterface $input = null, OutputInterface $output = null

$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());
Expand Down
Expand Up @@ -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
{
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/Debug/Exception/FatalErrorException.php
Expand Up @@ -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
{
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/Debug/Exception/FatalThrowableError.php
Expand Up @@ -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
{
Expand Down
Expand Up @@ -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
{
Expand Down
Expand Up @@ -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
{
Expand Down
Expand Up @@ -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
{
Expand Down
Expand Up @@ -14,14 +14,14 @@
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
{
Expand Down
Expand Up @@ -14,14 +14,14 @@
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
{
Expand Down
33 changes: 33 additions & 0 deletions src/Symfony/Component/ErrorHandler/Error/ClassNotFoundError.php
@@ -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);
}
}
}
Expand Up @@ -9,27 +9,29 @@
* 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) {
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();
Expand Down Expand Up @@ -63,15 +65,24 @@ public function __construct(string $message, int $code, int $severity, string $f
} 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;
}
}
Expand Up @@ -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);
}
}
}
33 changes: 33 additions & 0 deletions src/Symfony/Component/ErrorHandler/Error/UndefinedMethodError.php
@@ -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);
}
}
}

0 comments on commit 38b9a27

Please sign in to comment.