feature #28422 [VarExporter] throw component-specific exceptions (nicolas-grekas)
This PR was merged into the 4.2-dev branch.
Discussion
----------
[VarExporter] throw component-specific exceptions
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
This makes "serializing/unserializing" with the component diverge a bit from native serialize/unserialize (wich can throw plain "Exception" instances), but I think we should still do it.
Commits
-------
2c444927bc
[VarExporter] throw component-specific exceptions
This commit is contained in:
commit
deae538245
@ -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\VarExporter\Exception;
|
||||
|
||||
class ClassNotFoundException extends \Exception implements ExceptionInterface
|
||||
{
|
||||
public function __construct(string $class, \Throwable $previous = null)
|
||||
{
|
||||
parent::__construct(sprintf('Class "%s" not found.', $class), 0, $previous);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?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\VarExporter\Exception;
|
||||
|
||||
interface ExceptionInterface extends \Throwable
|
||||
{
|
||||
}
|
@ -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\VarExporter\Exception;
|
||||
|
||||
class NotInstantiableTypeException extends \Exception implements ExceptionInterface
|
||||
{
|
||||
public function __construct(string $type)
|
||||
{
|
||||
parent::__construct(sprintf('Type "%s" is not instantiable.', $type));
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
@ -31,14 +33,14 @@ class Exporter
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \Exception When a value cannot be serialized
|
||||
* @throws NotInstantiableTypeException When a value cannot be serialized
|
||||
*/
|
||||
public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic)
|
||||
{
|
||||
$refs = $values;
|
||||
foreach ($values as $k => $value) {
|
||||
if (\is_resource($value)) {
|
||||
throw new \Exception(sprintf("Serialization of '%s' resource is not allowed", \get_resource_type($value)));
|
||||
throw new NotInstantiableTypeException(\get_resource_type($value).' resource');
|
||||
}
|
||||
$refs[$k] = $objectsPool;
|
||||
|
||||
@ -77,9 +79,12 @@ class Exporter
|
||||
$arrayValue = (array) $value;
|
||||
|
||||
if (!isset(Registry::$reflectors[$class])) {
|
||||
// Might throw Exception("Serialization of '...' is not allowed")
|
||||
Registry::getClassReflector($class);
|
||||
serialize(Registry::$prototypes[$class]);
|
||||
try {
|
||||
serialize(Registry::$prototypes[$class]);
|
||||
} catch (\Exception $e) {
|
||||
throw new NotInstantiableTypeException($class, $e);
|
||||
}
|
||||
if (\method_exists($class, '__sleep')) {
|
||||
Registry::getClassReflector($class, Registry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]);
|
||||
}
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
use Symfony\Component\VarExporter\Exception\ClassNotFoundException;
|
||||
use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
@ -37,9 +40,7 @@ class Registry
|
||||
|
||||
try {
|
||||
foreach ($serializables as $k => $v) {
|
||||
if (false === $objects[$k] = unserialize($v)) {
|
||||
throw new \Exception(error_get_last()['message'] ?? 'unserialize(): unknown error');
|
||||
}
|
||||
$objects[$k] = unserialize($v);
|
||||
}
|
||||
} finally {
|
||||
ini_set('unserialize_callback_func', $unserializeCallback);
|
||||
@ -64,6 +65,9 @@ class Registry
|
||||
|
||||
public static function getClassReflector($class, $instantiableWithoutConstructor = false, $cloneable = null)
|
||||
{
|
||||
if (!\class_exists($class)) {
|
||||
throw new ClassNotFoundException($class);
|
||||
}
|
||||
$reflector = new \ReflectionClass($class);
|
||||
|
||||
if (self::$instantiableWithoutConstructor[$class] = $instantiableWithoutConstructor || !$reflector->isFinal()) {
|
||||
@ -77,14 +81,14 @@ class Registry
|
||||
if ('C:' === $proto && !$reflector->getMethod('unserialize')->isInternal()) {
|
||||
$proto = null;
|
||||
} elseif (false === $proto = @unserialize($proto.\strlen($class).':"'.$class.'":0:{}')) {
|
||||
throw new \Exception(sprintf("Serialization of '%s' is not allowed", $class));
|
||||
throw new NotInstantiableTypeException($class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$cloneable[$class] = $cloneable) {
|
||||
if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup'))) {
|
||||
throw new \Exception(sprintf("Serialization of '%s' is not allowed", $class));
|
||||
throw new NotInstantiableTypeException($class);
|
||||
}
|
||||
|
||||
self::$cloneable[$class] = $reflector->isCloneable() && !$reflector->hasMethod('__clone');
|
||||
|
@ -16,7 +16,7 @@ It also provides a few improvements over `var_export()`/`serialize()`:
|
||||
|
||||
* the output is PSR-2 compatible;
|
||||
* the output can be re-indented without messing up with `\r` or `\n` in the data
|
||||
* missing classes throw a `ReflectionException` instead of being unserialized to
|
||||
* missing classes throw a `ClassNotFoundException` instead of being unserialized to
|
||||
`PHP_Incomplete_Class` objects;
|
||||
* references involving `SplObjectStorage`, `ArrayObject` or `ArrayIterator`
|
||||
instances are preserved;
|
||||
|
@ -21,8 +21,8 @@ class VarExporterTest extends TestCase
|
||||
use VarDumperTestTrait;
|
||||
|
||||
/**
|
||||
* @expectedException \ReflectionException
|
||||
* @expectedExceptionMessage Class SomeNotExistingClass does not exist
|
||||
* @expectedException \Symfony\Component\VarExporter\Exception\ClassNotFoundException
|
||||
* @expectedExceptionMessage Class "SomeNotExistingClass" not found.
|
||||
*/
|
||||
public function testPhpIncompleteClassesAreForbidden()
|
||||
{
|
||||
@ -36,8 +36,8 @@ class VarExporterTest extends TestCase
|
||||
|
||||
/**
|
||||
* @dataProvider provideFailingSerialization
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessageRegexp Serialization of '.*' is not allowed
|
||||
* @expectedException \Symfony\Component\VarExporter\Exception\NotInstantiableTypeException
|
||||
* @expectedExceptionMessageRegexp Type ".*" is not instantiable.
|
||||
*/
|
||||
public function testFailingSerialization($value)
|
||||
{
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\VarExporter;
|
||||
|
||||
use Symfony\Component\VarExporter\Exception\ExceptionInterface;
|
||||
use Symfony\Component\VarExporter\Internal\Exporter;
|
||||
use Symfony\Component\VarExporter\Internal\Hydrator;
|
||||
use Symfony\Component\VarExporter\Internal\Registry;
|
||||
@ -36,7 +37,7 @@ final class VarExporter
|
||||
*
|
||||
* @return string The value exported as PHP code
|
||||
*
|
||||
* @throws \Exception When the provided value cannot be serialized
|
||||
* @throws ExceptionInterface When the provided value cannot be serialized
|
||||
*/
|
||||
public static function export($value, bool &$isStaticValue = null): string
|
||||
{
|
||||
|
Reference in New Issue
Block a user