Browse > Home / Techie / Blog article: The Greatest PHP Debugging Function of All Time

| Subcribe via RSS

The Greatest PHP Debugging Function of All Time

December 22nd, 2009 Posted in Techie

I normally try to keep the tech stuff to a minimum on the blog, but this function is in high demand from anyone who’s ever had it and then worked on another codebase without it.  Simple, but mind-blowingly useful. Without further ado, I present, YO.

NOTE: This is a re-write of some code I wrote at Yahoo!, so I wanted to give credit to the two folks who had a hand in the debugging system we built there, from which this is derived: Chris and Sara.

You’re welcome, America.

<?php
 
/**
 * The greatest debugging class in history. Here's why:
 * 
 *  - outputs to both the error log and optionally the screen.
 *  - formats output nicely so you can see your debugging output
 *  - shows you where the yo() call is IN THE OUTPUT so if you
 *    leave a yo() in someplace, you can track it down easy-peasy.
 *  - accepts multiple args just like var_dump
 *  - feel free to use the sub functions too (like var_dump_str)
 *   
 * @author Jacob Rosenberg <themasterof@rightuptop.com>
 * @author Sara Golemon
 * @author Chris Draycott
 *
 */
class Yo {
 
 
    /* {{{ */
    /**
     * getCallerLine
     *
     * gets the calling line of the function it was called from,
     * i.e., this function's grandparent.
     *
     * @returns string Name of calling function
     */
    static public function getCallerLine($levels = 2)
    {
        $bt = debug_backtrace();
 
        $file = isset($bt[$levels]['file']) ? $bt[$levels]['file'] : '';
        $line = isset($bt[$levels]['line']) ? $bt[$levels]['line'] : '';
 
        return array($file, $line);
    }
    /* }}} */
 
 
    /* {{{ */
    /**
     * getCaller
     *
     * gets the calling function of the function it was called from,
     * i.e., this function's grandparent.
     *
     * @returns string Name of calling function
     */
    static public function getCaller($levels = 2)
    {
        $bt = debug_backtrace();
 
        if (isset($bt[$levels])) {
            $caller = $bt[$levels]['function'];
 
            if (isset($bt[$levels]['class'])) {
                $caller = $bt[$levels]['class'] . "::" . $caller;
            }
        } else {
            $caller = 'main';
        }
 
        return $caller;
    }
 
 
    /* {{{ */
    /**
     * var_dump_str
     *
     * var_dump, but with returned output.
     * OB capture is gross, but print_r($x,1) sucks compared to var_dump.
     *
     * @param mixed PHP Variable to be var_dump'd
     * @returns string var_dump representation of that variable
     */
    function var_dump_str($var)
    {
        ob_start();
        var_dump($var);
        $out = ob_get_contents();
        ob_end_clean();
        return $out;
    }
    /* }}} */
 
    /**
     * isInternal
     *
     * determines if a user is an internal user.
     *
     * @return bool true if the user is internal and should see debug output
     */
    public static function isInternal()
    {
        // feel free to add some code here that
        // turns on debug output for only a subset of users.
        //
        return true;
    }
 
    /* }}} */
 
    /* {{{ */
    /**
     * yo - figure it out yourself, dummy
     *
     * @param mixed Variable(s) to be dumped (var args)
     * ...
     */
    public static function do_yo()
    {
        $internal = self::isInternal();
 
        $caller = self::getCaller();
 
        if ($internal) {
            echo "<pre><h1>YO!!: $caller</h1>";  
        }
 
        error_log("YO!!:: $caller");
 
        foreach (func_get_args() as $var) {
            $output = ($var === false) ? 'bool(false)' : Yo::var_dump_str($var);
            if ($internal) {
                echo htmlentities($output, ENT_QUOTES, 'UTF-8') . "\n";
            }
            error_log($output);
        }
 
        if ($internal) echo "</pre>";
    }
    /* }}} */
 
    /* {{{ */
    /**
     * yo_bt - figure it out yourself, dummy
     *
     */
    public static function do_yo_bt()
    {
        $internal = self::isInternal();
        $caller = self::getCaller();
 
        if ($internal) {
            echo "<pre><h1>YO bt!!: $caller</h1>";
        }
        error_log("YO bt!!:: $caller");
 
        $bt = debug_backtrace();
 
        foreach ($bt as $line) {
            if ($internal) echo $line['function'] . "\n";
            error_log($line['function']);
        }
 
        if ($internal) echo "</pre>";
    }
    /* }}} */
}
/* }}} */
 
// shortcuts! ------
function yo() { return Yo::do_yo(); }
function yo_bt() { return Yo::do_yo_bt(); }
Bookmark and Share

No related posts.

Tags: , ,
  • I wonder how many shops have written this in some form or another. I now we have.

  • jakeyr

    No doubt. We should petition the PHP devs for an internals version.

  • Sam S

    best. ever. period.

blog comments powered by Disqus