[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")
Registry::getClassReflector($class);
serialize(Registry::$prototypes[$class]);
if (\method_exists($class, '__sleep')) {
Registry::getClassReflector($class, Registry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]);
}
}
$reflector = Registry::$reflectors[$class];
$proto = Registry::$prototypes[$class];
@ -110,6 +113,11 @@ class Exporter
$value = null;
goto handle_value;
}
foreach ($sleep as $name) {
if (\property_exists($value, $name) && !$reflector->hasProperty($name)) {
$arrayValue[$name] = $value->$name;
}
}
$sleep = array_flip($sleep);
}

View File

@ -74,14 +74,9 @@ class Registry
}
}
if (null !== $cloneable) {
self::$prototypes[$class] = $proto;
self::$cloneable[$class] = $cloneable;
return self::$reflectors[$class] = $reflector;
}
if ($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) {
if (null !== self::$cloneable[$class] = $cloneable) {
// no-op
} elseif ($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) {
if (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup')) {
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)
{
$serializedValue = serialize($value);
$dumpedValue = $this->getDump($value);
$isStaticValue = true;
$marshalledValue = VarExporter::export($value, $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 = 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);
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');
}
}
class GoodNight
{
public function __sleep()
{
$this->good = 'night';
return array('good');
}
}