Skip to content

Commit

Permalink
Exposer: includes uninitialized properties [Closes #448]
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Dec 10, 2020
1 parent f7a79ad commit c99ca2e
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 25 deletions.
60 changes: 50 additions & 10 deletions src/Tracy/Dumper/Exposer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,64 @@ final class Exposer
{
public static function exposeObject(object $obj, Value $value, Describer $describer): void
{
$defaults = get_class_vars(get_class($obj));
$tmp = (array) $obj;
$values = $tmp; // bug #79477, PHP < 7.4.6
foreach ($values as $k => $v) {
$refId = $describer->getReferenceId($values, $k);
if (isset($k[0]) && $k[0] === "\x00") {
$info = explode("\00", $k);
$k = end($info);
$type = $info[1] === '*' ? Value::PROP_PROTECTED : $info[1];
$props = self::getProperties(get_class($obj));

foreach (array_diff_key($values, $props) as $k => $v) {
$describer->addPropertyTo(
$value,
(string) $k,
$v,
Value::PROP_DYNAMIC,
$describer->getReferenceId($values, $k)
);
}

foreach ($props as $k => [$name, $type]) {
if (array_key_exists($k, $values)) {
$describer->addPropertyTo(
$value,
$name,
$values[$k],
$type,
$describer->getReferenceId($values, $k)
);
} else {
$type = array_key_exists($k, $defaults) ? Value::PROP_PUBLIC : Value::PROP_DYNAMIC;
$k = (string) $k;
$value->items[] = [$name, new Value(Value::TYPE_TEXT, 'unset'), $type];
}
$describer->addPropertyTo($value, $k, $v, $type, $refId);
}
}


private static function getProperties($class): array
{
static $cache;
if (isset($cache[$class])) {
return $cache[$class];
}
$rc = new \ReflectionClass($class);
$parentProps = $rc->getParentClass() ? self::getProperties($rc->getParentClass()->getName()) : [];
$props = [];

foreach ($rc->getProperties() as $prop) {
$name = $prop->getName();
if ($prop->isStatic()) {
// nothing
} elseif ($prop->isPrivate()) {
$props["\x00" . $class . "\x00" . $name] = [$name, $class];
} elseif ($prop->isProtected()) {
$props["\x00*\x00" . $name] = [$name, Value::PROP_PROTECTED];
} else {
$props[$name] = [$name, Value::PROP_PUBLIC];
unset($parentProps["\x00*\x00" . $name]);
}
}

return $cache[$class] = $props + $parentProps;
}


public static function exposeClosure(\Closure $obj): array
{
$rc = new \ReflectionFunction($obj);
Expand Down
51 changes: 43 additions & 8 deletions tests/Tracy/Dumper.toHtml().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -99,20 +99,55 @@ $obj->{"a\xA0"} = 12;
Assert::match(<<<'XX'
<pre class="tracy-dump"
><span class="tracy-toggle"><span class="tracy-dump-object">Child</span> <span class="tracy-dump-hash">#%d%</span></span>
<div><span class="tracy-dump-indent"> </span><span class="tracy-dump-public">x</span>: <span class="tracy-dump-number">1</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Child">y</span>: <span class="tracy-dump-number">2</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-protected">z</span>: <span class="tracy-dump-number">3</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-public">x2</span>: <span class="tracy-dump-number">4</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-protected">y2</span>: <span class="tracy-dump-number">5</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Child">z2</span>: <span class="tracy-dump-number">6</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Test">y</span>: <span class="tracy-dump-string" title="5 characters">'hello'</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">new</span>: <span class="tracy-dump-number">7</span>
<div><span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">new</span>: <span class="tracy-dump-number">7</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">0</span>: <span class="tracy-dump-number">8</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">1</span>: <span class="tracy-dump-number">9</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">&apos;&apos;</span>: <span class="tracy-dump-number">10</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">'a<span>\x00\n</span>
'</span>: <span class="tracy-dump-number">11</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">'a<span>\xA0</span>'</span>: <span class="tracy-dump-number">12</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-public">x</span>: <span class="tracy-dump-number">1</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Child">y</span>: <span class="tracy-dump-number">2</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-protected">z</span>: <span class="tracy-dump-number">3</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-public">x2</span>: <span class="tracy-dump-number">4</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-protected">y2</span>: <span class="tracy-dump-number">5</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Child">z2</span>: <span class="tracy-dump-number">6</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Test">y</span>: <span class="tracy-dump-string" title="5 characters">'hello'</span>
</div></pre>
XX
, Dumper::toHtml($obj));


if (PHP_VERSION_ID >= 70400) {
require __DIR__ . '/fixtures/DumpClass.74.php';

Assert::match(<<<'XX'
<pre class="tracy-dump"
><span class="tracy-toggle"><span class="tracy-dump-object">Test74</span> <span class="tracy-dump-hash">#%d%</span></span>
<div><span class="tracy-dump-indent"> </span><span class="tracy-dump-public">x</span>: <span class="tracy-dump-number">1</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Test74">y</span>: <span class="tracy-dump-virtual">unset</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-protected">z</span>: <span class="tracy-dump-virtual">unset</span>
</div></pre>
XX
, Dumper::toHtml(new Test74));


$obj = new Child74;
$obj->new = 7;
unset($obj->unset1, $obj->unset2);


Assert::match(<<<'XX'
<pre class="tracy-dump"
><span class="tracy-toggle"><span class="tracy-dump-object">Child74</span> <span class="tracy-dump-hash">#%d%</span></span>
<div><span class="tracy-dump-indent"> </span><span class="tracy-dump-dynamic">new</span>: <span class="tracy-dump-number">7</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-public">x</span>: <span class="tracy-dump-number">2</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Child74">y</span>: <span class="tracy-dump-virtual">unset</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-protected">z</span>: <span class="tracy-dump-virtual">unset</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-public">unset1</span>: <span class="tracy-dump-virtual">unset</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-public">unset2</span>: <span class="tracy-dump-virtual">unset</span>
<span class="tracy-dump-indent"> </span><span class="tracy-dump-private" title="declared in Test74">y</span>: <span class="tracy-dump-virtual">unset</span>
</div></pre>
XX
, Dumper::toHtml($obj));
}
8 changes: 4 additions & 4 deletions tests/Tracy/Dumper.toTerminal().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ $obj->{''} = 10;

Assert::match(<<<XX
\e[1;31mChild\e[0m \e[0m#%d%\e[0m\e[0m
\e[1;30m \e[0m\e[1;37mnew\e[0m: \e[1;32m7\e[0m
\e[1;30m \e[0m\e[1;37m0\e[0m: \e[1;32m8\e[0m
\e[1;30m \e[0m\e[1;37m1\e[0m: \e[1;32m9\e[0m
\e[1;30m \e[0m\e[1;37m''\e[0m: \e[1;32m10\e[0m
\e[1;30m \e[0m\e[1;37mx\e[0m: \e[1;32m1\e[0m
\e[1;30m \e[0m\e[1;37my\e[0m: \e[1;32m2\e[0m
\e[1;30m \e[0m\e[1;37mz\e[0m: \e[1;32m3\e[0m
\e[1;30m \e[0m\e[1;37mx2\e[0m: \e[1;32m4\e[0m
\e[1;30m \e[0m\e[1;37my2\e[0m: \e[1;32m5\e[0m
\e[1;30m \e[0m\e[1;37mz2\e[0m: \e[1;32m6\e[0m
\e[1;30m \e[0m\e[1;37my\e[0m: \e[1;36m'hello'\e[0m
\e[1;30m \e[0m\e[1;37mnew\e[0m: \e[1;32m7\e[0m
\e[1;30m \e[0m\e[1;37m0\e[0m: \e[1;32m8\e[0m
\e[1;30m \e[0m\e[1;37m1\e[0m: \e[1;32m9\e[0m
\e[1;30m \e[0m\e[1;37m''\e[0m: \e[1;32m10\e[0m
XX
, Dumper::toTerminal($obj));

Expand Down
37 changes: 34 additions & 3 deletions tests/Tracy/Dumper.toText().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,47 @@ $obj->{''} = 10;

Assert::match(<<<'XX'
Child #%d%
new: 7
0: 8
1: 9
'': 10
x: 1
y: 2
z: 3
x2: 4
y2: 5
z2: 6
y: 'hello'
XX
, Dumper::toText($obj));


if (PHP_VERSION_ID >= 70400) {
require __DIR__ . '/fixtures/DumpClass.74.php';

Assert::match(<<<'XX'
Test74 #%d%
x: 1
y: unset
z: unset
XX
, Dumper::toText(new Test74));


$obj = new Child74;
$obj->new = 7;
unset($obj->unset1, $obj->unset2);


Assert::match(<<<'XX'
Child74 #%d%
new: 7
0: 8
1: 9
'': 10
x: 2
y: unset
z: unset
unset1: unset
unset2: unset
y: unset
XX
, Dumper::toText($obj));
}
20 changes: 20 additions & 0 deletions tests/Tracy/fixtures/DumpClass.74.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

class Test74
{
public int $x = 1;
private int $y;
protected int $z;
}

class Child74 extends Test74
{
public int $x = 2;
private int $y;
protected int $z;

public $unset1;
public int $unset2 = 1;
}

0 comments on commit c99ca2e

Please sign in to comment.