Version: current

      1 <?php
      2 /**
      3  * @package sapphire
      4  * @subpackage dev
      5  */
      6 class SS_Backtrace {
      7
      8     /**
      9      * Return debug_backtrace() results with functions filtered
     10      * specific to the debugging system, and not the trace.
     11      *
     12      * @param null|array $ignoredFunctions If an array, filter these functions out of the trace
     13      * @return array
     14      */
     15     static function filtered_backtrace($ignoredFunctions = null) {
     16         return self::filter_backtrace(debug_backtrace(), $ignoredFunctions);
     17     }
     18
     19     /**
     20      * Filter a backtrace so that it doesn't show the calls to the
     21      * debugging system, which is useless information.
     22      *
     23      * @param array $bt Backtrace to filter
     24      * @param null|array $ignoredFunctions List of extra functions to filter out
     25      * @return array
     26      */
     27     static function filter_backtrace($bt, $ignoredFunctions = null) {
     28         $defaultIgnoredFunctions = array(
     29             'SS_Log::log',
     30             'SS_Backtrace::backtrace',
     31             'SS_Backtrace::filtered_backtrace',
     32             'Zend_Log_Writer_Abstract->write',
     33             'Zend_Log->log',
     34             'Zend_Log->__call',
     35             'Zend_Log->err',
     36             'DebugView->writeTrace',
     37             'CliDebugView->writeTrace',
     38             'Debug::emailError',
     39             'Debug::warningHandler',
     40             'Debug::noticeHandler',
     41             'Debug::fatalHandler',
     42             'errorHandler',
     43             'Debug::showError',
     44             'Debug::backtrace',
     45             'exceptionHandler'
     46         );
     47
     48         if($ignoredFunctions) foreach($ignoredFunctions as $ignoredFunction) {
     49             $defaultIgnoredFunctions[] = $ignoredFunction;
     50         }
     51
     52         while($bt && in_array(self::full_func_name($bt[0]), $defaultIgnoredFunctions)) {
     53             array_shift($bt);
     54         }
     55
     56         return $bt;
     57     }
     58
     59     /**
     60      * Render or return a backtrace from the given scope.
     61      *
     62      * @param unknown_type $returnVal
     63      * @param unknown_type $ignoreAjax
     64      * @return unknown
     65      */
     66     static function backtrace($returnVal = false, $ignoreAjax = false, $ignoredFunctions = null) {
     67         $plainText = Director::is_cli() || (Director::is_ajax() && !$ignoreAjax);
     68         $result = self::get_rendered_backtrace(debug_backtrace(), $plainText, $ignoredFunctions);
     69         if($returnVal) {
     70             return $result;
     71         } else {
     72             echo $result;
     73         }
     74     }
     75
     76     static function any_to_string($arg) {
     77         if (!is_object($arg)) {
     78             if (is_array($arg)) {
     79                 $arg = '[' . join('::', array_map(array('SS_Backtrace', 'any_to_string'), $arg)) . ']';
     80             }
     81             $args[] = (string) $arg;
     82         }
     83         elseif (method_exists($arg, '__toString')) {
     84             $args[] = (string) $arg;
     85         }
     86         else {
     87             $args[] = '{' . get_class($arg) . '}';
     88         }
     89
     90     }
     91
     92     /**
     93      * Return the full function name.  If showArgs is set to true, a string representation of the arguments will be shown
     94      */
     95     static function full_func_name($item, $showArgs = false) {
     96         $funcName = '';
     97         if(isset($item['class'])) $funcName .= $item['class'];
     98         if(isset($item['type'])) $funcName .= $item['type'];
     99         if(isset($item['function'])) $funcName .= $item['function'];
    100
    101         if($showArgs && isset($item['args'])) {
    102             $args = array();
    103             foreach($item['args'] as $arg) {
    104                 $args[] = self::any_to_string($arg);
    105             }
    106
    107             $funcName .= "(" . implode(",", $args)  .")";
    108         }
    109
    110         return $funcName;
    111     }
    112
    113     /**
    114      * Render a backtrace array into an appropriate plain-text or HTML string.
    115      *
    116      * @param string $bt The trace array, as returned by debug_backtrace() or Exception::getTrace()
    117      * @param boolean $plainText Set to false for HTML output, or true for plain-text output
    118      * @param array List of functions that should be ignored. If not set, a default is provided
    119      * @return string The rendered backtrace
    120      */
    121     static function get_rendered_backtrace($bt, $plainText = false, $ignoredFunctions = null) {
    122         $bt = self::filter_backtrace($bt, $ignoredFunctions);
    123         $result = "<ul>";
    124         foreach($bt as $item) {
    125             if($plainText) {
    126                 $result .= self::full_func_name($item,true) . "\n";
    127                 if(isset($item['line']) && isset($item['file'])) $result .= "line $item[line] of " . basename($item['file']) . "\n";
    128                 $result .= "\n";
    129             } else {
    130                 if ($item['function'] == 'user_error') {
    131                     $name = $item['args'][0];
    132                 } else {
    133                     $name = self::full_func_name($item,true);
    134                 }
    135                 $result .= "<li><b>" . htmlentities($name) . "</b>\n<br />\n";
    136                 $result .= isset($item['line']) ? "Line $item[line] of " : '';
    137                 $result .=  isset($item['file']) ? htmlentities(basename($item['file'])) : '';
    138                 $result .= "</li>\n";
    139             }
    140         }
    141         $result .= "</ul>";
    142         return $result;
    143     }
    144
    145 }

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -