I’ve been tinkering with dumping PHP objects, and have found myself constantly running into a brick wall. The output from print_r and friends is fine in some contexts, but for larger structures, it would be nice to tidy the output up a bit and wrap it in some HTML.
However, these functions have a certain privilege in that they can access private/protected variables. This is not something that can be circumvented in plain PHP (save through some exotic extensions, which I’d rather not rely on). It seems that 5.3.0 may introduce a functionality in the Reflection API, but nobody knows when 5.3.0 will be out.
So I realised that it is possible to serialize any object to a string, which will include private and protected variables alike. All I had to do was then to parse the serialized string, and I’d gain that arcane insight. One would perhaps assume that such a parser already exists. Maybe it does, but I couldn’t find it (I did find a Java implementation though). The format is fairly simple though, so I spend half a Sunday afternoon on it. One very nice bonus, compared to print_r is that the format handles recursion, which is very handy
for dumping the output in a presentable form.
So, without further ado, here’s the code:
< ?php
/**
* Exports variable information, including private/protected variables
and with recursion-protection.
* Since this is built upon PHP serialization functionality,
unserializable objects may cause trouble.
*/
class XrayVision {
protected $id;
function export($object) {
$this->id = 1;
list($value, $input) = $this->parse(serialize($object));
return $value;
}
protected function parse($input) {
if (substr($input, 0, 2) === 'N;') {
return array(array('type' => 'null', 'id' => $this->id++,
'value' => null), substr($input, 2));
}
$pos = strpos($input, ':');
$type = substr($input, 0, $pos);
$input = substr($input, $pos + 1);
switch ($type) {
case 's':
return $this->s($input);
case 'i':
return $this->i($input);
case 'd':
return $this->d($input);
case 'b':
return $this->b($input);
case 'O':
return $this->o($input);
case 'a':
return $this->a($input);
case 'r':
return $this->r($input);
}
throw new Exception("Unhandled type '$type'");
}
protected function s($input) {
$pos = strpos($input, ':');
$length = substr($input, 0, $pos);
$input = substr($input, $pos + 1);
$value = substr($input, 1, $length);
return array(array('type' => 'string', 'id' => $this->id++,
'value' => $value), substr($input, $length + 3));
}
protected function i($input) {
$pos = strpos($input, ';');
$value = (integer) substr($input, 0, $pos);
return array(array('type' => 'integer', 'id' => $this->id++,
'value' => $value), substr($input, $pos + 1));
}
protected function d($input) {
$pos = strpos($input, ';');
$value = (float) substr($input, 0, $pos);
return array(array('type' => 'float', 'id' => $this->id++, 'value'
=> $value), substr($input, $pos + 1));
}
protected function b($input) {
$pos = strpos($input, ';');
$value = substr($input, 0, $pos) === '1';
return array(array('type' => 'boolean', 'id' => $this->id++,
'value' => $value), substr($input, $pos + 1));
}
protected function r($input) {
$pos = strpos($input, ';');
$value = (integer) substr($input, 0, $pos);
return array(array('type' => 'recursion', 'id' => $this->id++,
'value' => $value), substr($input, $pos + 1));
}
protected function o($input) {
$id = $this->id++;
$pos = strpos($input, ':');
$name_length = substr($input, 0, $pos);
$input = substr($input, $pos + 1);
$name = substr($input, 1, $name_length);
$input = substr($input, $name_length + 3);
$pos = strpos($input, ':');
$length = (int) substr($input, 0, $pos);
$input = substr($input, $pos + 2);
$values = array();
for ($ii=0; $ii < $length; $ii++) {
list($key, $input) = $this->parse($input);
$this->id--;
list($value, $input) = $this->parse($input);
if (substr($key['value'], 0, 3) === "\000*\000") {
$values['protected:' . substr($key['value'], 3)] = $value;
} elseif ($pos = strrpos($key['value'], "\000")) {
$values['private:' . substr($key['value'], $pos + 1)] = $value;
} else {
$values[str_replace("\000", ':', $key['value'])] = $value;
}
}
return array(
array('type' => 'object', 'id' => $id, 'class' => $name, 'value'
=> $values),
substr($input, 1));
}
protected function a($input) {
$id = $this->id++;
$pos = strpos($input, ':');
$length = (int) substr($input, 0, $pos);
$input = substr($input, $pos + 2);
$values = array();
for ($ii=0; $ii < $length; $ii++) {
list($key, $input) = $this->parse($input);
$this->id--;
list($value, $input) = $this->parse($input);
$values[$key['value']] = $value;
}
return array(
array('type' => 'array', 'id' => $id, 'value' => $values),
substr($input, 1));
}
}
?>
There are at least two known issues with this technique. The first is that resources are serialized into integers. For a dump, this doesn’t really matter, since a resource is meaningless outside the running process. The other problem is with objects that implements __sleep. Since this function may have side-effects, you can potentially mess up your program for objects that use this feature. In my experience, it’s a seldom used functionality anyway, so it doesn’t really bother me that much.







Interesting! I’d like to add it to my debug tool set, but there is a parse error:
November 11th, 2008 at 7:25 pm
Sorry about that. Wordpress keeps messing with my code. I’ve posted it to pastebin now, where it doesn’t get hurt.Seems we got it working now.
November 11th, 2008 at 7:34 pm
I have similar functionality in my debug toolset, but it works with a simple cast of objects to an array:
$subject=(array)$object;. This exposes the private variables as well.November 11th, 2008 at 9:17 pm
You really should try
__toString()instead of some serialization magic : http://www.hudzilla.org/phpbook/read.php/6_14_5Don’t loose your sunday afternoon for that…
November 12th, 2008 at 12:01 am
hmm. you could have saved yourself a lot of work by simply using PEAR’s var_dump package ( http://pear.php.net/package/Var_Dump ) with it you can render to text, html or xml and carry on with the important work!
November 12th, 2008 at 12:47 am
@Wolfgang
That’s a very nice little secret trick, you got there! Thanks for sharing – Now I’m just a bit annoyed, I didn’t know about this beforehand.
November 12th, 2008 at 2:00 am
Your code doesn’t work. Using your code, the object is nothing. Using
print_ryou can at least see that there is one NULL protected propertyOutput:
November 12th, 2008 at 5:17 am
Or just install xdebug (which you should have installed on development environments already) and use
var_dump().November 12th, 2008 at 9:19 am
Also, ReflectionProperty is not part of 5.3. It’s already in 5.2.
November 12th, 2008 at 9:23 am
To finish up, explain how this class helps with your stated issue:
Because I see no functionality for pretty-printing here anywhere.
And lastly, if I interpret your confusing English correctly, you’re asserting that regular ol’ print_r() has some issue preventing it from accessing private and protected variables? Are you sure this is true? Because I’m pretty sure it’s not true, and the burden of proof is on you here, my friend.
November 12th, 2008 at 9:40 am
I’d love to see more PHP blog posts here, but it’s quite clear that there’s a lot of hostility around here. I never before considered the PHP community elitist, but some of the comments on this blog are quite convincing.
Obviously it’s good to have reader feedback on easier ways to do things, but there’s no reason for those suggestions to border on personal attacks like a lot of the responses to Troels’s posts. It really makes a case for disabling comments and sharing feedback through PMs only . . .
November 13th, 2008 at 6:42 am
I definitely do not mean any personal offense to the author. But the article is misleading, confusingly written, the code example inadequate to address the issues raised as the article’s thesis, the example solves a non-existant problem in a very inefficient manner, and there are some factual errors.
November 13th, 2008 at 8:06 am
@Ken Guest
Thanks for mentioning it – I haven’t seen that package for some reason. I’m a bit surprised they took the long road and parsed
var_dump’s output, when a serialized string is so much easier to parse.@Josh Johnston
I probably should have mentioned explicitly, but this class just generates an array structure, which describes the variable. This can subsequently be transformed into html or any other kind of output. I left that part out though, since it’s rather trivial. One use case could be to json-encode the structure and send it to the Firebug console. Try the following, to see what I mean:
Which will give you:
@Nick
Burden of proof? I didn’t know I was on trial.
@Tarh
Thanks, though I don’t think it’s fair to judge the entire PHP community on the grounds of one raging madman.
November 13th, 2008 at 8:23 am
Don’t get reflexively defensive. Correct me if I’m wrong, but the basis of this article is:
print_r()does not print out private and protected variables of an object. Hence the need for this class you wrote. Correct?I was pointing out that this assertion about
print_r()is not true.Let’s try an example, this will be using PHP 5.2.4:
Explain how that is substantively different than the output of your class’s
export()method:November 13th, 2008 at 8:38 am
My apologies about the formatting there.
November 13th, 2008 at 8:40 am
Defensive? I find your tone borderline rude, but to let you have the benefit of doubt, I’ll answer none the less.
No, my assertion was the opposite — Namely that
print_ris able to do something that is impossible through the reflection API — At least until 5.3.0 comes out. What I wanted to do, was to generate some pretty HTML representation of variables, rather than the text-based output fromprint_r. Something similar to this for example.November 13th, 2008 at 9:01 am
My apologies, honestly, not trying to be rude. If it’s rude to deconstruct a technical article for accuracy, then we might as well all go home and take up farming for a living.
I understand that I misread the article and you are simply trying to show that parsing a serialized string is easier than formatting the output from print_r().
That being the case, I’d like to re-emphasize that this kind of debugging is aided and enhanced greatly by the xdebug extension, which comes with a nice feature to format the var_dump() output for you with nice HTML and pretty colors. (among many other much more powerful features.)
November 13th, 2008 at 9:25 am
Thanks everyone for being considerate and professional when posting here—getting personal doesn’t help anyone. Also worth noting that we have a “comment preview”, so you can check what your comments look like before posting. I’ve cleaned up a few people’s comments. Hope that helps!
November 13th, 2008 at 9:56 am
The (array) casting of objects is useful. One particular use was to map objects to an array of parameters for prepared sql.
class Person { protected $id; public $firstName; public $lastName; function __construct($firstName, $lastName, $id = null) { $this->id = $id; $this->firstName = $firstName; $this->lastName = $lastName; } } $person = new Person('Homer', 'Simpson'); $map = array( "*id" => 'id', "firstName" => 'firstName', "lastName" => 'lastName'); $params = array_combine($map, array_intersect_key((array)$person, $map)); var_dump($params);No idea how your supposed to get code displaying nicely here
November 14th, 2008 at 2:49 am
$arr = (array)$object;
var_dump($arr);
// Win
November 16th, 2008 at 7:00 am
Casting an object to an array like stated above will expose its private properties aswell as its private properties. Its quite simple:
http://www.phpfi.com/382350
:)
November 26th, 2008 at 3:08 am