Question

Currently, I have a logger which logs errors together with a backtrace. The logger serializes the backtrace to JSON via json_encode().

Let's look at some hypothetical code...

<?php
    error_reporting(-1);                          // show all errors
    function test($b){
        echo json_encode(debug_backtrace());      // take a backtrace snapshot
    }
    $c = imagecreate(50,50);                      // create a resource...
    test($c);                                     // ...and pass to function
?>

If you run the code above, we will see something like:

Warning: json_encode() [function.json-encode]: type is unsupported, encoded as null in /code/ch6gVw on line 5 [{"file":"/code/ch6gVw","line":8,"function":"test","args":[null]}]

We can notice two things going on here:

  1. The logger itself is causing a warning! Bad bad bad!
  2. The logged data tells us we passed a null to the function?!?!

So, my proposed solution is something like:

foreach($trace as $i=>$v)
    if(is_resource($v))
        $trace[$i] = (string)$v.' ('.get_resource_type($v).')';

The result would look like Resource id #1 (gd)


This, however, may cause some grave issues.

  1. We need to somehow track which arrays we looped through so as to avoid ending up in infinite loops with arrays referencing themselves ($GLOBALS tend to cause this mess).
  2. We would also have to convert resources of object properties, but objects, unlike arrays, are not a copy of the original thing, hence changing the property changes the live object. On the other hand, how safe is it to clone() the object?
  3. Won't such a loop severely slow down the server (backtraces tend to be large, no)?
Was it helpful?

Solution

I ended up with the following function:

function clean_trace($branch){
    if(is_object($branch)){
        // object
        $props = array();
        $branch = clone($branch); // doesn't clone cause some issues?
        foreach($props as $k=>$v)
            $branch->$k = clean_trace($v);
    }elseif(is_array($branch)){
        // array
        foreach($branch as $k=>$v)
            $branch[$k] = clean_trace($v);
    }elseif(is_resource($branch)){
        // resource
        $branch = (string)$branch.' ('.get_resource_type($branch).')';
    }elseif(is_string($branch)){
        // string (ensure it is UTF-8, see: https://bugs.php.net/bug.php?id=47130)
        $branch = utf8_encode($branch);
    }
    // other (hopefully serializable) stuff
    return $branch;
}

You can see it in action here. However, I'm not convinced:

  • It is quite slow (iterating over lots of data)
  • It is quite memory intensive (data needs to be copied to not mess the original)
  • It is not safe in case where arrays/objects reference themselves
    • Example: $a = array(); $a['ref'] = &$a; (PHP does this to some internal variables)
  • I'm concerned that cloning objects may have some serious side-effects (consider the magic method __clone(), an invitation to wreck havoc).

OTHER TIPS

So you are trying to store the backtrace as a data structure that can be used to pretty-print the results later on?

If that isn't needed I'd just store $result = print_r(debug_backtrace(), true) and be done with it.

If not my first shot would be something like:

<?php
error_reporting(-1);
function test($b){
    echo json_encode(clean(debug_backtrace()));
}   
$c = fopen("/tmp/foo", "w");
test($c);


function clean($trace) {
    array_walk_recursive($trace, function(&$element) {
        if(is_object(&$element)) {
            // work around unrealizable elements and preserve typing
            $element = array(get_class($element), (object)$element); 
        } else if(is_resource($element)) {
            $element = get_resource_type($element) . '#'  .(int)$element;
        }   
    }); 
    return $trace;
}   

It's just a rough sketch but I'm not aware of any project that stores backtracks for later inspection in a non textual or already processed format and looking around the mature frameworks didn't bring anything up

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top