Skip to content

Commit

Permalink
bug #31303 [VarDumper] Use \ReflectionReference for determining if a …
Browse files Browse the repository at this point in the history
…key is a reference (php >= 7.4) (dorumd, nicolas-grekas)

This PR was merged into the 3.4 branch.

Discussion
----------

[VarDumper] Use \ReflectionReference for determining if a key is a reference (php >= 7.4)

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #31135
| License       | MIT
| Doc PR        | -

Prepare for PHP 7.4: use ReflectionReference in VarCloner

Commits
-------

40f24ef [VarDumper] finish PHP 7.4 support and add tests
e99a6b8 [VarDumper] Use \ReflectionReference for determining if a key is a reference (php >= 7.4)
  • Loading branch information
nicolas-grekas committed Jul 23, 2019
2 parents bfe4e16 + 40f24ef commit b8d03ca
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/Symfony/Component/VarDumper/Caster/Caster.php
Expand Up @@ -68,8 +68,8 @@ public static function castObject($obj, $class, $hasDebugInfo = false)
foreach ($a as $k => $v) {
if (isset($k[0]) ? "\0" !== $k[0] : \PHP_VERSION_ID >= 70200) {
if (!isset($publicProperties[$class])) {
foreach (get_class_vars($class) as $prop => $v) {
$publicProperties[$class][$prop] = true;
foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) {
$publicProperties[$class][$prop->name] = true;
}
}
if (!isset($publicProperties[$class][$k])) {
Expand Down
12 changes: 9 additions & 3 deletions src/Symfony/Component/VarDumper/Cloner/Stub.php
Expand Up @@ -49,11 +49,17 @@ public function __sleep()
$properties = [];

if (!isset(self::$defaultProperties[$c = \get_class($this)])) {
self::$defaultProperties[$c] = get_class_vars($c);
$defaultProperties = get_class_vars($c);

foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) {
unset(self::$defaultProperties[$c][$k]);
foreach ((new \ReflectionClass($c))->getProperties(\ReflectionProperty::IS_PUBLIC) as $v) {
if ($v->isStatic()) {
unset($defaultProperties[$v->name]);
} elseif (!isset($defaultProperties[$v->name])) {
$defaultProperties[$v->name] = null;
}
}

self::$defaultProperties[$c] = $defaultProperties;
}

foreach (self::$defaultProperties[$c] as $k => $v) {
Expand Down
13 changes: 10 additions & 3 deletions src/Symfony/Component/VarDumper/Cloner/VarCloner.php
Expand Up @@ -42,7 +42,7 @@ protected function doClone($var)
$currentDepth = 0; // Current tree depth
$currentDepthFinalIndex = 0; // Final $queue index for current tree depth
$minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached
$cookie = (object) []; // Unique object used to detect hard references
$cookie = (object) []; // Unique object used to detect hard references
$a = null; // Array cast for nested structures
$stub = null; // Stub capturing the main properties of an original item value
// or null if the original value is used directly
Expand Down Expand Up @@ -86,8 +86,15 @@ protected function doClone($var)
}
foreach ($vals as $k => $v) {
// $v is the original value or a stub object in case of hard references
$refs[$k] = $cookie;
if ($zvalIsRef = $vals[$k] === $cookie) {

if (\PHP_VERSION_ID >= 70400) {
$zvalIsRef = null !== \ReflectionReference::fromArrayElement($vals, $k);
} else {
$refs[$k] = $cookie;
$zvalIsRef = $vals[$k] === $cookie;
}

if ($zvalIsRef) {
$vals[$k] = &$stub; // Break hard references to make $queue completely
unset($stub); // independent from the original structure
if ($v instanceof Stub && isset($hardRefs[spl_object_hash($v)])) {
Expand Down
68 changes: 68 additions & 0 deletions src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Tests\Fixtures\Php74;

/**
* @author Nicolas Grekas <p@tchwork.com>
Expand Down Expand Up @@ -431,6 +432,73 @@ public function testCaster()
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
)
EOTXT;
$this->assertStringMatchesFormat($expected, print_r($clone, true));
}

/**
* @requires PHP 7.4
*/
public function testPhp74()
{
$data = new Php74();

$cloner = new VarCloner();
$clone = $cloner->cloneVar($data);

$expected = <<<'EOTXT'
Symfony\Component\VarDumper\Cloner\Data Object
(
[data:Symfony\Component\VarDumper\Cloner\Data:private] => Array
(
[0] => Array
(
[0] => Symfony\Component\VarDumper\Cloner\Stub Object
(
[type] => 4
[class] => Symfony\Component\VarDumper\Tests\Fixtures\Php74
[value] =>
[cut] => 0
[handle] => %i
[refCount] => 0
[position] => 1
[attr] => Array
(
)
)
)
[1] => Array
(
[p1] => 123
[p2] => Symfony\Component\VarDumper\Cloner\Stub Object
(
[type] => 4
[class] => stdClass
[value] =>
[cut] => 0
[handle] => %i
[refCount] => 0
[position] => 0
[attr] => Array
(
)
)
)
)
[position:Symfony\Component\VarDumper\Cloner\Data:private] => 0
[key:Symfony\Component\VarDumper\Cloner\Data:private] => 0
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
)
EOTXT;
$this->assertStringMatchesFormat($expected, print_r($clone, true));
}
Expand Down
14 changes: 14 additions & 0 deletions src/Symfony/Component/VarDumper/Tests/Fixtures/Php74.php
@@ -0,0 +1,14 @@
<?php

namespace Symfony\Component\VarDumper\Tests\Fixtures;

class Php74
{
public $p1 = 123;
public \stdClass $p2;

public function __construct()
{
$this->p2 = new \stdClass();
}
}

0 comments on commit b8d03ca

Please sign in to comment.