bug #14959 [Debug+VarDumper] Fix handling of PHP7 "Throwable" exceptions (nicolas-grekas)
This PR was merged into the 2.6 branch. Discussion ---------- [Debug+VarDumper] Fix handling of PHP7 "Throwable" exceptions | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - PHP7 may introduce the Throwable interface if the corresponding RFC is accepted (see https://wiki.php.net/rfc/throwable-interface) This PR adds support for it. We should wait for final approval of the RFC before merging it. Commits -------4dc727f
[Debug+VarDumper] Fix handling of PHP7 exception/error modeledf793e
[VarDumper] Cherry-pick code style fixes from 2.7
This commit is contained in:
commit
13c5d7cd0d
@ -14,8 +14,8 @@ namespace Symfony\Component\Debug;
|
|||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Debug\Exception\ContextErrorException;
|
use Symfony\Component\Debug\Exception\ContextErrorException;
|
||||||
use Symfony\Component\Debug\Exception\FatalBaseException;
|
|
||||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||||
|
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
||||||
use Symfony\Component\Debug\Exception\OutOfMemoryException;
|
use Symfony\Component\Debug\Exception\OutOfMemoryException;
|
||||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
||||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
||||||
@ -431,15 +431,15 @@ class ErrorHandler
|
|||||||
/**
|
/**
|
||||||
* Handles an exception by logging then forwarding it to an other handler.
|
* Handles an exception by logging then forwarding it to an other handler.
|
||||||
*
|
*
|
||||||
* @param \Exception|\BaseException $exception An exception to handle
|
* @param \Exception|\Throwable $exception An exception to handle
|
||||||
* @param array $error An array as returned by error_get_last()
|
* @param array $error An array as returned by error_get_last()
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
public function handleException($exception, array $error = null)
|
public function handleException($exception, array $error = null)
|
||||||
{
|
{
|
||||||
if (!$exception instanceof \Exception) {
|
if (!$exception instanceof \Exception) {
|
||||||
$exception = new FatalBaseException($exception);
|
$exception = new FatalThrowableError($exception);
|
||||||
}
|
}
|
||||||
$type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
|
$type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
|
||||||
|
|
||||||
@ -451,15 +451,17 @@ class ErrorHandler
|
|||||||
'level' => error_reporting(),
|
'level' => error_reporting(),
|
||||||
'stack' => $exception->getTrace(),
|
'stack' => $exception->getTrace(),
|
||||||
);
|
);
|
||||||
if ($exception instanceof FatalBaseException) {
|
if ($exception instanceof FatalErrorException) {
|
||||||
$error = array(
|
if ($exception instanceof FatalThrowableError) {
|
||||||
'type' => $type,
|
$error = array(
|
||||||
'message' => $message = $exception->getMessage(),
|
'type' => $type,
|
||||||
'file' => $e['file'],
|
'message' => $message = $exception->getMessage(),
|
||||||
'line' => $e['line'],
|
'file' => $e['file'],
|
||||||
);
|
'line' => $e['line'],
|
||||||
} elseif ($exception instanceof FatalErrorException) {
|
);
|
||||||
$message = 'Fatal '.$exception->getMessage();
|
} else {
|
||||||
|
$message = 'Fatal '.$exception->getMessage();
|
||||||
|
}
|
||||||
} elseif ($exception instanceof \ErrorException) {
|
} elseif ($exception instanceof \ErrorException) {
|
||||||
$message = 'Uncaught '.$exception->getMessage();
|
$message = 'Uncaught '.$exception->getMessage();
|
||||||
if ($exception instanceof ContextErrorException) {
|
if ($exception instanceof ContextErrorException) {
|
||||||
@ -486,9 +488,9 @@ class ErrorHandler
|
|||||||
try {
|
try {
|
||||||
call_user_func($this->exceptionHandler, $exception);
|
call_user_func($this->exceptionHandler, $exception);
|
||||||
} catch (\Exception $handlerException) {
|
} catch (\Exception $handlerException) {
|
||||||
$this->exceptionHandler = null;
|
} catch (\Throwable $handlerException) {
|
||||||
$this->handleException($handlerException);
|
}
|
||||||
} catch (\BaseException $handlerException) {
|
if (isset($handlerException)) {
|
||||||
$this->exceptionHandler = null;
|
$this->exceptionHandler = null;
|
||||||
$this->handleException($handlerException);
|
$this->handleException($handlerException);
|
||||||
}
|
}
|
||||||
|
@ -12,18 +12,18 @@
|
|||||||
namespace Symfony\Component\Debug\Exception;
|
namespace Symfony\Component\Debug\Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Fatal Error Exception.
|
* Fatal Throwable Error.
|
||||||
*
|
*
|
||||||
* @author Nicolas Grekas <p@tchwork.com>
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
*/
|
*/
|
||||||
class FatalBaseException extends FatalErrorException
|
class FatalThrowableError extends FatalErrorException
|
||||||
{
|
{
|
||||||
public function __construct(\BaseException $e)
|
public function __construct(\Throwable $e)
|
||||||
{
|
{
|
||||||
if ($e instanceof \ParseException) {
|
if ($e instanceof \ParseError) {
|
||||||
$message = 'Parse error: '.$e->getMessage();
|
$message = 'Parse error: '.$e->getMessage();
|
||||||
$severity = E_PARSE;
|
$severity = E_PARSE;
|
||||||
} elseif ($e instanceof \TypeException) {
|
} elseif ($e instanceof \TypeError) {
|
||||||
$message = 'Type error: '.$e->getMessage();
|
$message = 'Type error: '.$e->getMessage();
|
||||||
$severity = E_RECOVERABLE_ERROR;
|
$severity = E_RECOVERABLE_ERROR;
|
||||||
} else {
|
} else {
|
@ -63,8 +63,9 @@ class DOMCaster
|
|||||||
|
|
||||||
public static function castException(\DOMException $e, array $a, Stub $stub, $isNested)
|
public static function castException(\DOMException $e, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
if (isset($a["\0*\0code"], self::$errorCodes[$a["\0*\0code"]])) {
|
$k = "\0*\0code";
|
||||||
$a["\0*\0code"] = new ConstStub(self::$errorCodes[$a["\0*\0code"]], $a["\0*\0code"]);
|
if (isset($a[$k], self::$errorCodes[$a[$k]])) {
|
||||||
|
$a[$k] = new ConstStub(self::$errorCodes[$a[$k]], $a[$k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
|
@ -25,34 +25,35 @@ class DoctrineCaster
|
|||||||
{
|
{
|
||||||
public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, $isNested)
|
public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
unset(
|
foreach (array('__cloner__', '__initializer__') as $k) {
|
||||||
$a['__cloner__'],
|
if (array_key_exists($k, $a)) {
|
||||||
$a['__initializer__']
|
unset($a[$k]);
|
||||||
);
|
++$stub->cut;
|
||||||
$stub->cut += 2;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, $isNested)
|
public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
$prefix = "\0Doctrine\\ORM\\Proxy\\Proxy\0";
|
foreach (array('_entityPersister', '_identifier') as $k) {
|
||||||
unset(
|
if (array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) {
|
||||||
$a[$prefix.'_entityPersister'],
|
unset($a[$k]);
|
||||||
$a[$prefix.'_identifier']
|
++$stub->cut;
|
||||||
);
|
}
|
||||||
$stub->cut += 2;
|
}
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, $isNested)
|
public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
$prefix = "\0Doctrine\\ORM\\PersistentCollection\0";
|
foreach (array('snapshot', 'association', 'typeClass') as $k) {
|
||||||
|
if (array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) {
|
||||||
$a[$prefix.'snapshot'] = new CutStub($a[$prefix.'snapshot']);
|
$a[$k] = new CutStub($a[$k]);
|
||||||
$a[$prefix.'association'] = new CutStub($a[$prefix.'association']);
|
}
|
||||||
$a[$prefix.'typeClass'] = new CutStub($a[$prefix.'typeClass']);
|
}
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
@ -40,23 +40,14 @@ class ExceptionCaster
|
|||||||
E_STRICT => 'E_STRICT',
|
E_STRICT => 'E_STRICT',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public static function castError(\Error $e, array $a, Stub $stub, $isNested)
|
||||||
|
{
|
||||||
|
return $e instanceof \Exception ? $a : self::filterExceptionArray($a, "\0Error\0");
|
||||||
|
}
|
||||||
|
|
||||||
public static function castException(\Exception $e, array $a, Stub $stub, $isNested)
|
public static function castException(\Exception $e, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
$xPrefix = PHP_VERSION_ID >= 70000 ? "\0BaseException\0" : "\0Exception\0";
|
return self::filterExceptionArray($a, "\0Exception\0");
|
||||||
$trace = $a[$xPrefix.'trace'];
|
|
||||||
unset($a[$xPrefix.'trace']); // Ensures the trace is always last
|
|
||||||
|
|
||||||
static::filterTrace($trace, static::$traceArgs);
|
|
||||||
|
|
||||||
if (null !== $trace) {
|
|
||||||
$a[$xPrefix.'trace'] = $trace;
|
|
||||||
}
|
|
||||||
if (empty($a[$xPrefix.'previous'])) {
|
|
||||||
unset($a[$xPrefix.'previous']);
|
|
||||||
}
|
|
||||||
unset($a[$xPrefix.'string'], $a["\0+\0xdebug_message"], $a["\0+\0__destructorException"]);
|
|
||||||
|
|
||||||
return $a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function castErrorException(\ErrorException $e, array $a, Stub $stub, $isNested)
|
public static function castErrorException(\ErrorException $e, array $a, Stub $stub, $isNested)
|
||||||
@ -70,20 +61,21 @@ class ExceptionCaster
|
|||||||
|
|
||||||
public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, $isNested)
|
public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
$xPrefix = PHP_VERSION_ID >= 70000 ? "\0BaseException\0" : "\0Exception\0";
|
$prefix = "\0*\0";
|
||||||
$b = (array) $a[$xPrefix.'previous'];
|
$xPrefix = "\0Exception\0";
|
||||||
|
|
||||||
if (isset($a[$xPrefix.'trace'][0])) {
|
if (isset($a[$xPrefix.'previous'], $a[$xPrefix.'trace'][0])) {
|
||||||
|
$b = (array) $a[$xPrefix.'previous'];
|
||||||
$b[$xPrefix.'trace'][0] += array(
|
$b[$xPrefix.'trace'][0] += array(
|
||||||
'file' => $b["\0*\0file"],
|
'file' => $b[$prefix.'file'],
|
||||||
'line' => $b["\0*\0line"],
|
'line' => $b[$prefix.'line'],
|
||||||
);
|
);
|
||||||
array_splice($b[$xPrefix.'trace'], -1 - count($a[$xPrefix.'trace']));
|
array_splice($b[$xPrefix.'trace'], -1 - count($a[$xPrefix.'trace']));
|
||||||
static::filterTrace($b[$xPrefix.'trace'], false);
|
static::filterTrace($b[$xPrefix.'trace'], false);
|
||||||
$a["\0~\0trace"] = $b[$xPrefix.'trace'];
|
$a["\0~\0trace"] = $b[$xPrefix.'trace'];
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($a[$xPrefix.'trace'], $a[$xPrefix.'previous'], $a["\0*\0code"], $a["\0*\0file"], $a["\0*\0line"]);
|
unset($a[$xPrefix.'trace'], $a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']);
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
@ -118,4 +110,26 @@ class ExceptionCaster
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function filterExceptionArray(array $a, $xPrefix)
|
||||||
|
{
|
||||||
|
if (isset($a[$xPrefix.'trace'])) {
|
||||||
|
$trace = $a[$xPrefix.'trace'];
|
||||||
|
unset($a[$xPrefix.'trace']); // Ensures the trace is always last
|
||||||
|
} else {
|
||||||
|
$trace = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
static::filterTrace($trace, static::$traceArgs);
|
||||||
|
|
||||||
|
if (null !== $trace) {
|
||||||
|
$a[$xPrefix.'trace'] = $trace;
|
||||||
|
}
|
||||||
|
if (empty($a[$xPrefix.'previous'])) {
|
||||||
|
unset($a[$xPrefix.'previous']);
|
||||||
|
}
|
||||||
|
unset($a[$xPrefix.'string'], $a["\0+\0xdebug_message"], $a["\0+\0__destructorException"]);
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,21 +78,21 @@ class PdoCaster
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$m = "\0~\0";
|
$prefix = "\0~\0";
|
||||||
$a += array(
|
$a += array(
|
||||||
$m.'inTransaction' => method_exists($c, 'inTransaction'),
|
$prefix.'inTransaction' => method_exists($c, 'inTransaction'),
|
||||||
$m.'errorInfo' => $c->errorInfo(),
|
$prefix.'errorInfo' => $c->errorInfo(),
|
||||||
$m.'attributes' => $attr,
|
$prefix.'attributes' => $attr,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($a[$m.'inTransaction']) {
|
if ($a[$prefix.'inTransaction']) {
|
||||||
$a[$m.'inTransaction'] = $c->inTransaction();
|
$a[$prefix.'inTransaction'] = $c->inTransaction();
|
||||||
} else {
|
} else {
|
||||||
unset($a[$m.'inTransaction']);
|
unset($a[$prefix.'inTransaction']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($a[$m.'errorInfo'][1], $a[$m.'errorInfo'][2])) {
|
if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) {
|
||||||
unset($a[$m.'errorInfo']);
|
unset($a[$prefix.'errorInfo']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$c->setAttribute(\PDO::ATTR_ERRMODE, $errmode);
|
$c->setAttribute(\PDO::ATTR_ERRMODE, $errmode);
|
||||||
@ -102,11 +102,11 @@ class PdoCaster
|
|||||||
|
|
||||||
public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, $isNested)
|
public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
$m = "\0~\0";
|
$prefix = "\0~\0";
|
||||||
$a[$m.'errorInfo'] = $c->errorInfo();
|
$a[$prefix.'errorInfo'] = $c->errorInfo();
|
||||||
|
|
||||||
if (!isset($a[$m.'errorInfo'][1], $a[$m.'errorInfo'][2])) {
|
if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) {
|
||||||
unset($a[$m.'errorInfo']);
|
unset($a[$prefix.'errorInfo']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
|
@ -22,14 +22,15 @@ class SplCaster
|
|||||||
{
|
{
|
||||||
public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested)
|
public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
|
$prefix = "\0~\0";
|
||||||
$class = $stub->class;
|
$class = $stub->class;
|
||||||
$flags = $c->getFlags();
|
$flags = $c->getFlags();
|
||||||
|
|
||||||
$b = array(
|
$b = array(
|
||||||
"\0~\0flag::STD_PROP_LIST" => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
|
$prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
|
||||||
"\0~\0flag::ARRAY_AS_PROPS" => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
|
$prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
|
||||||
"\0~\0iteratorClass" => $c->getIteratorClass(),
|
$prefix.'iteratorClass' => $c->getIteratorClass(),
|
||||||
"\0~\0storage" => $c->getArrayCopy(),
|
$prefix.'storage' => $c->getArrayCopy(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($class === 'ArrayObject') {
|
if ($class === 'ArrayObject') {
|
||||||
@ -68,12 +69,13 @@ class SplCaster
|
|||||||
|
|
||||||
public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, $isNested)
|
public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
|
$prefix = "\0~\0";
|
||||||
$mode = $c->getIteratorMode();
|
$mode = $c->getIteratorMode();
|
||||||
$c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE);
|
$c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE);
|
||||||
|
|
||||||
$a += array(
|
$a += array(
|
||||||
"\0~\0mode" => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_KEEP) ? 'IT_MODE_KEEP' : 'IT_MODE_DELETE'), $mode),
|
$prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_KEEP) ? 'IT_MODE_KEEP' : 'IT_MODE_DELETE'), $mode),
|
||||||
"\0~\0dllist" => iterator_to_array($c),
|
$prefix.'dllist' => iterator_to_array($c),
|
||||||
);
|
);
|
||||||
$c->setIteratorMode($mode);
|
$c->setIteratorMode($mode);
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ abstract class AbstractCloner implements ClonerInterface
|
|||||||
|
|
||||||
'ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException',
|
'ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException',
|
||||||
'Exception' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException',
|
'Exception' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException',
|
||||||
|
'Error' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castError',
|
||||||
'Symfony\Component\DependencyInjection\ContainerInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
|
'Symfony\Component\DependencyInjection\ContainerInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
|
||||||
'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException',
|
'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException',
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user