[VarExporter] fix exporting objects that mutate on __sleep()

This commit is contained in:
Nicolas Grekas 2018-09-05 15:34:16 +02:00
parent e4d7c74845
commit 36e412fdfc
4 changed files with 44 additions and 10 deletions

View File

@ -80,6 +80,9 @@ class Exporter
// Might throw Exception("Serialization of '...' is not allowed") // Might throw Exception("Serialization of '...' is not allowed")
Registry::getClassReflector($class); Registry::getClassReflector($class);
serialize(Registry::$prototypes[$class]); serialize(Registry::$prototypes[$class]);
if (\method_exists($class, '__sleep')) {
Registry::getClassReflector($class, Registry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]);
}
} }
$reflector = Registry::$reflectors[$class]; $reflector = Registry::$reflectors[$class];
$proto = Registry::$prototypes[$class]; $proto = Registry::$prototypes[$class];
@ -110,6 +113,11 @@ class Exporter
$value = null; $value = null;
goto handle_value; goto handle_value;
} }
foreach ($sleep as $name) {
if (\property_exists($value, $name) && !$reflector->hasProperty($name)) {
$arrayValue[$name] = $value->$name;
}
}
$sleep = array_flip($sleep); $sleep = array_flip($sleep);
} }

View File

@ -74,14 +74,9 @@ class Registry
} }
} }
if (null !== $cloneable) { if (null !== self::$cloneable[$class] = $cloneable) {
self::$prototypes[$class] = $proto; // no-op
self::$cloneable[$class] = $cloneable; } elseif ($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) {
return self::$reflectors[$class] = $reflector;
}
if ($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) {
if (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup')) { if (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup')) {
throw new \Exception(sprintf("Serialization of '%s' is not allowed", $class)); throw new \Exception(sprintf("Serialization of '%s' is not allowed", $class));
} }

View File

@ -0,0 +1,17 @@
<?php
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\GoodNight::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\Symfony\Component\VarExporter\Tests\GoodNight::class, true)),
],
null,
[
'*' => [
'good' => [
'night',
],
],
],
$o[0],
[]
);

View File

@ -72,12 +72,14 @@ class VarExporterTest extends TestCase
*/ */
public function testMarshall(string $testName, $value, bool $staticValueExpected = false) public function testMarshall(string $testName, $value, bool $staticValueExpected = false)
{ {
$serializedValue = serialize($value); $dumpedValue = $this->getDump($value);
$isStaticValue = true; $isStaticValue = true;
$marshalledValue = VarExporter::export($value, $isStaticValue); $marshalledValue = VarExporter::export($value, $isStaticValue);
$this->assertSame($staticValueExpected, $isStaticValue); $this->assertSame($staticValueExpected, $isStaticValue);
$this->assertSame($serializedValue, serialize($value)); if ('var-on-sleep' !== $testName) {
$this->assertDumpEquals($dumpedValue, $value);
}
$dump = "<?php\n\nreturn ".$marshalledValue.";\n"; $dump = "<?php\n\nreturn ".$marshalledValue.";\n";
$dump = str_replace(var_export(__FILE__, true), "\\dirname(__DIR__).\\DIRECTORY_SEPARATOR.'VarExporterTest.php'", $dump); $dump = str_replace(var_export(__FILE__, true), "\\dirname(__DIR__).\\DIRECTORY_SEPARATOR.'VarExporterTest.php'", $dump);
@ -165,6 +167,8 @@ class VarExporterTest extends TestCase
$r->setValue($value, 234); $r->setValue($value, 234);
yield array('error', $value); yield array('error', $value);
yield array('var-on-sleep', new GoodNight());
} }
} }
@ -248,3 +252,13 @@ class MyArrayObject extends \ArrayObject
throw new \BadMethodCallException('Calling MyArrayObject::setFlags() is forbidden'); throw new \BadMethodCallException('Calling MyArrayObject::setFlags() is forbidden');
} }
} }
class GoodNight
{
public function __sleep()
{
$this->good = 'night';
return array('good');
}
}