Programming - - By Troels Knak-Nielsen

How to Expose PHP’s Private Parts

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:

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 parse($input);
      $this->id--;
      list($value, $input) = $this->parse($input);
      if (substr($key['value'], 0, 3) === "
Sponsors