8000 [VarDumper] Fix dumping jsons casted as arrays by nicolas-grekas · Pull Request #19368 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[VarDumper] Fix dumping jsons casted as arrays #19368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading 8000
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
[VarDumper] Fix dumping jsons casted as arrays
  • Loading branch information
nicolas-grekas committed Jul 16, 2016
commit 56549e7a704f38cc73538096f907655b19f457af
4 changes: 3 additions & 1 deletion src/Symfony/Component/VarDumper/Caster/Caster.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ public static function castObject($obj, \ReflectionClass $reflector)
{
if ($reflector->hasMethod('__debugInfo')) {
$a = $obj->__debugInfo();
} elseif ($obj instanceof \Closure) {
$a = array();
} else {
$a = (array) $obj;
}

if ($a) {
$p = array_keys($a);
foreach ($p as $i => $k) {
if (!isset($k[0]) || ("\0" !== $k[0] && !$reflector->hasProperty($k))) {
if (isset($k[0]) && "\0" !== $k[0] && !$reflector->hasProperty($k)) {
$p[$i] = self::PREFIX_DYNAMIC.$k;
} elseif (isset($k[16]) && "\0" === $k[16] && 0 === strpos($k, "\0class@anonymous\0")) {
$p[$i] = "\0".$reflector->getParentClass().'@anonymous'.strrchr($k, "\0");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested)
}

$prefix = Caster::PREFIX_DYNAMIC;
unset($a['name'], $a[$prefix.'0'], $a[$prefix.'this'], $a[$prefix.'parameter'], $a[Caster::PREFIX_VIRTUAL.'extra']);
unset($a['name'], $a[$prefix.'this'], $a[$prefix.'parameter'], $a[Caster::PREFIX_VIRTUAL.'extra']);

return $a;
}
Expand Down
70 changes: 47 additions & 23 deletions src/Symfony/Component/VarDumper/Cloner/VarCloner.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected function doClone($var)
$i = 0; // Current iteration position in $queue
$len = 1; // Length of $queue
$pos = 0; // Number of cloned items past the first level
$refs = 0; // Hard references counter
$refsCounter = 0; // Hard references counter
$queue = array(array($var)); // This breadth-first queue is the return value
$arrayRefs = array(); // Map of queue indexes to stub array objects
$hardRefs = array(); // Map of original zval hashes to stub objects
Expand Down Expand Up @@ -60,27 +60,32 @@ protected function doClone($var)
for ($i = 0; $i < $len; ++$i) {
$indexed = true; // Whether the currently iterated array is numerically indexed or not
$j = -1; // Position in the currently iterated array
$step = $queue[$i]; // Copy of the currently iterated array used for hard references detection
foreach ($step as $k => $v) {
$fromObjCast = array_keys($queue[$i]);
$fromObjCast = array_keys(array_flip($fromObjCast)) !== $fromObjCast;
$refs = $vals = $fromObjCast ? array_values($queue[$i]) : $queue[$i];
foreach ($queue[$i] as $k => $v) {
// $k is the original key
// $v is the original value or a stub object in case of hard references
if ($indexed && $k !== ++$j) {
if ($k !== ++$j) {
$indexed = false;
}
if ($fromObjCast) {
$k = $j;
}
if ($useExt) {
$zval = symfony_zval_info($k, $step);
$zval = symfony_zval_info($k, $refs);
} else {
$step[$k] = $cookie;
if ($zval['zval_isref'] = $queue[$i][$k] === $cookie) {
$refs[$k] = $cookie;
if ($zval['zval_isref'] = $vals[$k] === $cookie) {
$zval['zval_hash'] = $v instanceof Stub ? spl_object_hash($v) : null;
}
$zval['type'] = gettype($v);
}
if ($zval['zval_isref']) {
$queue[$i][$k] = &$stub; // Break hard references to make $queue completely
$vals[$k] = &$stub; // Break hard references to make $queue completely
unset($stub); // independent from the original structure
if (isset($hardRefs[$zval['zval_hash']])) {
$queue[$i][$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($step[$k] = $v);
$vals[$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($refs[$k] = $v);
if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) {
++$v->value->refCount;
}
Expand Down Expand Up @@ -204,18 +209,18 @@ protected function doClone($var)
if (isset($stub)) {
if ($zval['zval_isref']) {
if ($useExt) {
$queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub();
$vals[$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub();
$v->value = $stub;
} else {
$step[$k] = new Stub();
$step[$k]->value = $stub;
$h = spl_object_hash($step[$k]);
$queue[$i][$k] = $hardRefs[$h] = &$step[$k];
$refs[$k] = new Stub();
$refs[$k]->value = $stub;
$h = spl_object_hash($refs[$k]);
$vals[$k] = $hardRefs[$h] = &$refs[$k];
$values[$h] = $v;
}
$queue[$i][$k]->handle = ++$refs;
$vals[$k]->handle = ++$refsCounter;
} else {
$queue[$i][$k] = $stub;
$vals[$k] = $stub;
}

if ($a) {
Expand Down Expand Up @@ -243,19 +248,38 @@ protected function doClone($var)
$stub = $a = null;
} elseif ($zval['zval_isref']) {
if ($useExt) {
$queue[$i][$k] = $hardRefs[$zval['zval_hash']] = new Stub();
$queue[$i][$k]->value = $v;
$vals[$k] = $hardRefs[$zval['zval_hash']] = new Stub();
$vals[$k]->value = $v;
} else {
$step[$k] = $queue[$i][$k] = new Stub();
$step[$k]->value = $v;
$h = spl_object_hash($step[$k]);
$hardRefs[$h] = &$step[$k];
$refs[$k] = $vals[$k] = new Stub();
$refs[$k]->value = $v;
$h = spl_object_hash($refs[$k]);
$hardRefs[$h] = &$refs[$k];
$values[$h] = $v;
}
$queue[$i][$k]->handle = ++$refs;
$vals[$k]->handle = ++$refsCounter;
}
}

if ($fromObjCast) {
$refs = $vals;
$vals = array();
$j = -1;
foreach ($queue[$i] as $k => $v) {
foreach (array($k => $v) as $a => $v) {
}
if ($a !== $k) {
$vals = (object) $vals;
$vals->{$k} = $refs[++$j];
$vals = (array) $vals;
} else {
$vals[$k] = $refs[++$j];
}
}
}

$queue[$i] = $vals;

if (isset($arrayRefs[$i])) {
if ($indexed) {
$arrayRefs[$i]->class = Stub::ARRAY_INDEXED;
Expand Down
39 changes: 39 additions & 0 deletions src/Symfony/Component/VarDumper/Tests/CliDumperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,45 @@ public function testXmlResource()
);
}

public function testJsonCast()
{
$var = (array) json_decode('{"0":{},"1":null}');
foreach ($var as &$v) {
}
$var[] = &$v;
$var[''] = 2;

$this->assertDumpMatchesFormat(
<<<EOTXT
array:4 [
"0" => {}
"1" => &1 null
0 => &1 null
"" => 2
]
EOTXT
,
$var
);
}

public function testObjectCast()
{
$var = (object) array(1 => 1);
$var->{1} = 2;

$this->assertDumpMatchesFormat(
<<<EOTXT
{
+1: 1
+"1": 2
}
EOTXT
,
$var
);
}

public function testClosedResource()
{
if (defined('HHVM_VERSION') && HHVM_VERSION_ID < 30600) {
Expand Down
66 changes: 66 additions & 0 deletions src/Symfony/Component/VarDumper/Tests/VarClonerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,72 @@ public function testClone()
$this->assertStringMatchesFormat($expected, print_r($clone, true));
}

public function testJsonCast()
{
$data = (array) json_decode('{"1":{}}');

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

$expected = <<<'EOTXT'
object(Symfony\Component\VarDumper\Cloner\Data)#%i (4) {
["data":"Symfony\Component\VarDumper\Cloner\Data":private]=>
array(2) {
[0]=>
array(1) {
[0]=>
object(Symfony\Component\VarDumper\Cloner\Stub)#%i (7) {
["type"]=>
string(5) "array"
["class"]=>
string(5) "assoc"
["value"]=>
int(1)
["cut"]=>
int(0)
["handle"]=>
int(0)
["refCount"]=>
int(0)
["position"]=>
int(1)
}
}
[1]=>
array(1) {
["1"]=>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the important line: "1" instead of a plain 1. Only json_decode() can create these.

object(Symfony\Component\VarDumper\Cloner\Stub)#%i (7) {
["type"]=>
string(6) "object"
["class"]=>
string(8) "stdClass"
["value"]=>
NULL
["cut"]=>
int(0)
["handle"]=>
int(%i)
["refCount"]=>
int(0)
["position"]=>
int(0)
}
}
}
["maxDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=>
int(20)
["maxItemsPerDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=>
int(-1)
["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=>
int(-1)
}

EOTXT;
ob_start();
var_dump($clone);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why making a var_dump here ? Shouldn't you be calling the VarDumper instead ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VarClonerTest uses only native primitives.

$this->assertStringMatchesFormat($expected, ob_get_clean());
}

public function testCaster()
{
$cloner = new VarCloner(array(
Expand Down
0