[VarExporter] support PHP7.4 __serialize & __unserialize
This commit is contained in:
parent
75b1157633
commit
c7a504c822
@ -67,7 +67,7 @@ final class Instantiator
|
||||
$wrappedInstance = [$reflector->newInstanceWithoutConstructor()];
|
||||
} elseif (null === Registry::$prototypes[$class]) {
|
||||
throw new NotInstantiableTypeException($class);
|
||||
} elseif ($reflector->implementsInterface('Serializable')) {
|
||||
} elseif ($reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__unserialize'))) {
|
||||
$wrappedInstance = [unserialize('C:'.\strlen($class).':"'.$class.'":0:{}')];
|
||||
} else {
|
||||
$wrappedInstance = [unserialize('O:'.\strlen($class).':"'.$class.'":0:{}')];
|
||||
|
@ -74,10 +74,23 @@ class Exporter
|
||||
}
|
||||
|
||||
$class = \get_class($value);
|
||||
$reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class);
|
||||
|
||||
if ($reflector->hasMethod('__serialize')) {
|
||||
if (!$reflector->getMethod('__serialize')->isPublic()) {
|
||||
throw new \Error(sprintf('Call to %s method %s::__serialize()', $reflector->getMethod('__serialize')->isProtected() ? 'protected' : 'private', $class));
|
||||
}
|
||||
|
||||
if (!\is_array($properties = $value->__serialize())) {
|
||||
throw new \Typerror($class.'::__serialize() must return an array');
|
||||
}
|
||||
|
||||
goto prepare_value;
|
||||
}
|
||||
|
||||
$properties = [];
|
||||
$sleep = null;
|
||||
$arrayValue = (array) $value;
|
||||
$reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class);
|
||||
$proto = Registry::$prototypes[$class];
|
||||
|
||||
if (($value instanceof \ArrayIterator || $value instanceof \ArrayObject) && null !== $proto) {
|
||||
@ -154,10 +167,11 @@ class Exporter
|
||||
}
|
||||
}
|
||||
|
||||
prepare_value:
|
||||
$objectsPool[$value] = [$id = \count($objectsPool)];
|
||||
$properties = self::prepare($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic);
|
||||
++$objectsCount;
|
||||
$objectsPool[$value] = [$id, $class, $properties, \method_exists($class, '__wakeup') ? $objectsCount : 0];
|
||||
$objectsPool[$value] = [$id, $class, $properties, \method_exists($class, '__unserialize') ? -$objectsCount : (\method_exists($class, '__wakeup') ? $objectsCount : 0)];
|
||||
|
||||
$value = new Reference($id);
|
||||
|
||||
|
@ -42,8 +42,12 @@ class Hydrator
|
||||
foreach ($properties as $class => $vars) {
|
||||
(self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects);
|
||||
}
|
||||
foreach ($wakeups as $i) {
|
||||
$objects[$i]->__wakeup();
|
||||
foreach ($wakeups as $k => $v) {
|
||||
if (\is_array($v)) {
|
||||
$objects[-$k]->__unserialize($v);
|
||||
} else {
|
||||
$objects[$v]->__wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
@ -86,14 +86,14 @@ class Registry
|
||||
$proto = $reflector->newInstanceWithoutConstructor();
|
||||
$instantiableWithoutConstructor = true;
|
||||
} catch (\ReflectionException $e) {
|
||||
$proto = $reflector->implementsInterface('Serializable') ? 'C:' : 'O:';
|
||||
$proto = $reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__unserialize')) ? 'C:' : 'O:';
|
||||
if ('C:' === $proto && !$reflector->getMethod('unserialize')->isInternal()) {
|
||||
$proto = null;
|
||||
} elseif (false === $proto = @unserialize($proto.\strlen($class).':"'.$class.'":0:{}')) {
|
||||
throw new NotInstantiableTypeException($class);
|
||||
}
|
||||
}
|
||||
if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !\method_exists($class, '__sleep')) {
|
||||
if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !\method_exists($class, '__sleep') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__serialize'))) {
|
||||
try {
|
||||
serialize($proto);
|
||||
} catch (\Exception $e) {
|
||||
@ -103,7 +103,7 @@ class Registry
|
||||
}
|
||||
|
||||
if (null === $cloneable) {
|
||||
if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup'))) {
|
||||
if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__unserialize')))) {
|
||||
throw new NotInstantiableTypeException($class);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@ VarExporter Component
|
||||
|
||||
The VarExporter component allows exporting any serializable PHP data structure to
|
||||
plain PHP code. While doing so, it preserves all the semantics associated with
|
||||
the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`).
|
||||
the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`,
|
||||
`__serialize`, `__unserialize`).
|
||||
|
||||
It also provides an instantiator that allows creating and populating objects
|
||||
without calling their constructor nor any other methods.
|
||||
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
|
||||
$o = [
|
||||
clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Symfony\\Component\\VarExporter\\Tests\\Php74Serializable'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Symfony\\Component\\VarExporter\\Tests\\Php74Serializable')),
|
||||
clone ($p['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')),
|
||||
],
|
||||
null,
|
||||
[],
|
||||
$o[0],
|
||||
[
|
||||
[
|
||||
$o[1],
|
||||
],
|
||||
]
|
||||
);
|
@ -82,7 +82,7 @@ class VarExporterTest extends TestCase
|
||||
$marshalledValue = VarExporter::export($value, $isStaticValue);
|
||||
|
||||
$this->assertSame($staticValueExpected, $isStaticValue);
|
||||
if ('var-on-sleep' !== $testName) {
|
||||
if ('var-on-sleep' !== $testName && 'php74-serializable' !== $testName) {
|
||||
$this->assertDumpEquals($dumpedValue, $value);
|
||||
}
|
||||
|
||||
@ -199,6 +199,8 @@ class VarExporterTest extends TestCase
|
||||
yield ['foo-serializable', new FooSerializable('bar')];
|
||||
|
||||
yield ['private-constructor', PrivateConstructor::create('bar')];
|
||||
|
||||
yield ['php74-serializable', new Php74Serializable()];
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,3 +389,36 @@ class FooSerializable implements \Serializable
|
||||
list($this->foo) = unserialize($str);
|
||||
}
|
||||
}
|
||||
|
||||
class Php74Serializable implements \Serializable
|
||||
{
|
||||
public function __serialize()
|
||||
{
|
||||
return [$this->foo = new \stdClass()];
|
||||
}
|
||||
|
||||
public function __unserialize(array $data)
|
||||
{
|
||||
list($this->foo) = $data;
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function unserialize($ser)
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
}
|
||||
|
@ -69,14 +69,30 @@ final class VarExporter
|
||||
|
||||
$classes = [];
|
||||
$values = [];
|
||||
$wakeups = [];
|
||||
$states = [];
|
||||
foreach ($objectsPool as $i => $v) {
|
||||
list(, $classes[], $values[], $wakeup) = $objectsPool[$v];
|
||||
if ($wakeup) {
|
||||
$wakeups[$wakeup] = $i;
|
||||
if (0 < $wakeup) {
|
||||
$states[$wakeup] = $i;
|
||||
} elseif (0 > $wakeup) {
|
||||
$states[-$wakeup] = [$i, array_pop($values)];
|
||||
$values[] = [];
|
||||
}
|
||||
}
|
||||
ksort($wakeups);
|
||||
ksort($states);
|
||||
|
||||
$wakeups = [null];
|
||||
foreach ($states as $k => $v) {
|
||||
if (\is_array($v)) {
|
||||
$wakeups[-$v[0]] = $v[1];
|
||||
} else {
|
||||
$wakeups[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $wakeups[0]) {
|
||||
unset($wakeups[0]);
|
||||
}
|
||||
|
||||
$properties = [];
|
||||
foreach ($values as $i => $vars) {
|
||||
|
Reference in New Issue
Block a user