Skip to content
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
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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