Skip to content

Commit 18e3906

Browse files
committed
[VarDumper] Fix serialization of stubs with null or uninitialized values
1 parent df4401f commit 18e3906

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

Diff for: Cloner/Internal/NoDefault.php

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\VarDumper\Cloner\Internal;
13+
14+
/**
15+
* Flags a typed property that has no default value.
16+
*
17+
* This dummy object is used to distinguish a property with a default value of null
18+
* from a property that is uninitialized by default.
19+
*
20+
* @internal
21+
*/
22+
enum NoDefault
23+
{
24+
case NoDefault;
25+
}

Diff for: Cloner/Stub.php

+11-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\VarDumper\Cloner;
1313

14+
use Symfony\Component\VarDumper\Cloner\Internal\NoDefault;
15+
1416
/**
1517
* Represents the main properties of a PHP variable.
1618
*
@@ -50,15 +52,20 @@ public function __sleep(): array
5052
$properties = [];
5153

5254
if (!isset(self::$defaultProperties[$c = static::class])) {
53-
self::$defaultProperties[$c] = get_class_vars($c);
55+
$reflection = new \ReflectionClass($c);
56+
self::$defaultProperties[$c] = [];
57+
58+
foreach ($reflection->getProperties() as $p) {
59+
if ($p->isStatic()) {
60+
continue;
61+
}
5462

55-
foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) {
56-
unset(self::$defaultProperties[$c][$k]);
63+
self::$defaultProperties[$c][$p->name] = $p->hasDefaultValue() ? $p->getDefaultValue() : ($p->hasType() ? NoDefault::NoDefault : null);
5764
}
5865
}
5966

6067
foreach (self::$defaultProperties[$c] as $k => $v) {
61-
if ($this->$k !== $v) {
68+
if (NoDefault::NoDefault === $v || $this->$k !== $v) {
6269
$properties[] = $k;
6370
}
6471
}

Diff for: Tests/Cloner/StubTest.php

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\VarDumper\Tests\Cloner;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\VarDumper\Cloner\Stub;
16+
17+
final class StubTest extends TestCase
18+
{
19+
public function testUnserializeNullValue()
20+
{
21+
$stub = new Stub();
22+
$stub->value = null;
23+
24+
$stub = unserialize(serialize($stub));
25+
26+
self::assertNull($stub->value);
27+
}
28+
29+
public function testUnserializeNullInTypedProperty()
30+
{
31+
$stub = new MyStub();
32+
$stub->myProp = null;
33+
34+
$stub = unserialize(serialize($stub));
35+
36+
self::assertNull($stub->myProp);
37+
}
38+
39+
public function testUninitializedStubPropertiesAreLeftUninitialized()
40+
{
41+
$stub = new MyStub();
42+
43+
$stub = unserialize(serialize($stub));
44+
45+
$r = new \ReflectionProperty(MyStub::class, 'myProp');
46+
self::assertFalse($r->isInitialized($stub));
47+
}
48+
}
49+
50+
final class MyStub extends Stub
51+
{
52+
public mixed $myProp;
53+
}

0 commit comments

Comments
 (0)