Let's face it, debug_backtrace()
output is not very pretty. Did anyone code a wrapper?
And what's your favourite pretty var_dump()
(which is usable in commercial projects, so no GPL (although LGPL is ok))
See also: A more pretty/informative Var_dump alternative in PHP?
Six years - and ten thousand views of this question - later, and I am still using this. It isn't pretty in a way that looks good on screen, like Kint (which is excellent).
It is plain text, which I can email to myself in in automated error reports and can display in the browser's developer console using ChromePhp.
/**
* @brief Returns an HTML formatted string showing details of the backtrace
*
* Example:
*
* F:\Dropbox\programs\Xampp\htdocs\api\q.php:48 e(373, 'beer', 'curry')
* F:\Dropbox\programs\Xampp\htdocs\api\q.php:53 d(26366, 28255, 8364)
* F:\Dropbox\programs\Xampp\htdocs\api\q.php:58 c()
* F:\Dropbox\programs\Xampp\htdocs\api\q.php:63 b(1283, 15488开发者_StackOverflow, 29369)
* F:\Dropbox\programs\Xampp\htdocs\api\q.php:72 a(788, 6077, 25010)
*/
function FormatBacktrace()
{
$result = '<h4>Backtrace</h4>';
foreach (debug_backtrace() as $trace)
{
if ($trace['function'] ==__FUNCTION__)
continue;
$parameters = is_array($trace['args']) ? implode(", ",$trace['args']) : "";
if (array_key_exists('class', $trace))
$result .= sprintf("%s:%s %s::%s(%s)<br>",
$trace['file'],
$trace['line'],
$trace['class'],
$trace['function'],
$parameters);
else
$result .= sprintf("%s:%s %s(%s)<br>",
$trace['file'],
$trace['line'],
$trace['function'],
$parameters);
}
return $result;
}
You also have kint
(github repo) which has a composer
package on the packagist
repository
So either download the library manually or with composer
, it's just a matter of :
$ composer init
$ composer require raveren/kint
$ composer install
Then, instead of ini_set('display_errors', 'On');
, I prefer to use this simple handler in my main (first) include file :
if ( getenv('__project_env__') === 'DEV') {
error_reporting(E_ALL | E_STRICT);
function shutdown_handler() {
$error = error_get_last();
Kint::trace();
Kint::dump($error);
}
register_shutdown_function('shutdown_handler');
} else {
...
}
With __project_env__
being set in Apache's Virtualhost (SetEnv __project_env__ "DEV"
) so as not to pollute the different branches of the git
repository where the project lives with configuration items which are by essence environmental
- In DEV : i get my debugging
- In PROD, it's silent by default
Here is a screenshot of how the trace looks (each step is collapsible):
(source: github.io)
Here is my pretty print wrapper that is intended for non-browser output, ie error logs or the console:
function stackTrace() {
$stack = debug_backtrace();
$output = '';
$stackLen = count($stack);
for ($i = 1; $i < $stackLen; $i++) {
$entry = $stack[$i];
$func = $entry['function'] . '(';
$argsLen = count($entry['args']);
for ($j = 0; $j < $argsLen; $j++) {
$func .= $entry['args'][$j];
if ($j < $argsLen - 1) $func .= ', ';
}
$func .= ')';
$output .= $entry['file'] . ':' . $entry['line'] . ' - ' . $func . PHP_EOL;
}
return $output;
}
The Xdebug extension can print stacktraces with a configurable degree of verbosity.
It also offers some additional var_dump() features such as syntax coloring:
Edit:
Regarding the inclusion of Xdebug in a commercial project.
The Xdebug license has only a few terms and seems pretty permissive.
Xdebug is a C extension. As such re-distributing it or part of it in your project may be somewhat difficult. Depending on your requirements I see a few options:
- Have your end user install Xdebug from a Linux distribution package or a DLL from the site
- Distribute .dll and .so files for all supported platforms
- Have your end user build the source code
- Distribute a custom build of PHP
jhurliman's pretty print stackTrace method above is really great. But for me it was generating lots of PHP Warnings that were also cluttering up the log. I added a little more error and type checking which results in a very nice stack trace in the logs. Here is the modified version of jhurliman's code:
function stackTrace() {
$stack = debug_backtrace();
$output = '';
$stackLen = count($stack);
for ($i = 1; $i < $stackLen; $i++) {
$entry = $stack[$i];
$func = $entry['function'] . '(';
$argsLen = count($entry['args']);
for ($j = 0; $j < $argsLen; $j++) {
$my_entry = $entry['args'][$j];
if (is_string($my_entry)) {
$func .= $my_entry;
}
if ($j < $argsLen - 1) $func .= ', ';
}
$func .= ')';
$entry_file = 'NO_FILE';
if (array_key_exists('file', $entry)) {
$entry_file = $entry['file'];
}
$entry_line = 'NO_LINE';
if (array_key_exists('line', $entry)) {
$entry_line = $entry['line'];
}
$output .= $entry_file . ':' . $entry_line . ' - ' . $func . PHP_EOL;
}
return $output;
}
Gonna add mine to the rest of the answers here.
If you have bootstrap and jquery installed, it's even more useful and compact, but its not neccessary.
function prettyPrintBackTrace() {
$backtrace = "\n<b><u>Full Backtrace</u></b>\n<script>function toggleBackTraceTwirl(self) {\$('span', self).toggleClass('glyphicon-menu-up glyphicon-menu-down');}</script>";
foreach (debug_backtrace() as $key => $trace) {
if(($trace['function'] ==__FUNCTION__) || ($trace['function'] == "fail")) {continue;}
$class = (array_key_exists('class', $trace) ? "class <u>({$trace['class']})</u>" : false);
$exp = explode("/",$trace['file']);
$exp[count($exp)-1] = "<b>" . end($exp) . "</b>";;
$filename = implode("/",array_splice($exp, -4));
$backtrace .= "/{$filename}:{$trace['line']}, ";
if((isset($trace['args'])) && (is_array($trace['args']))) {
if( (is_string($trace['args'][0])) && (substr($trace['args'][0],-4) == ".php") && (count($trace['args'] == 1)) ) {
// It was most likely a php include of some sort.
$exp = explode("/",$trace['args'][0]);
$filename = implode("/",array_splice($exp, -2));
$backtrace .= "function <i>{$trace['function']}(<b>{$filename}</b>)</i>\n";
} else {
// Finish the line and move on.
$backtrace .= "function <i>{$trace['function']}()</i> <a href='#' data-target='#backtraceparameters{$key}' onClick='toggleBackTraceTwirl(this)' data-toggle='collapse'><span class='glyphicon glyphicon-menu-down'></span></a>\n";
$backtrace .= "<div id='backtraceparameters{$key}' class='collapse'>";
$args = array();
foreach($trace['args'] as $key => $val) {
if($val) $args[(!is_numeric($key) ? "key" : false)] = $val;
}
foreach($args as $count => $a) {
$backtrace .= ($count != (count($args) -1) ? "├" : "└");
$value = $a;
if($a === true) $value = "<i>true</i>";
if($a === false) $value = "<i>f alse</i>";
$backtrace .= "─ ".(!is_numeric($count) ? $count." " : false).var_export($value,1)."\n";
}
$backtrace .= "</div>";
}
}
}
return $backtrace;
}
I hope that helps someone. I've tried to make it as compact as possible.
Here's a "pretty print" var_dump
function vdump() {
$args = func_get_args();
$backtrace = debug_backtrace();
$code = file($backtrace[0]['file']);
echo "<pre style='background: #eee; border: 1px solid #aaa; clear: both; overflow: auto; padding: 10px; text-align: left; margin-bottom: 5px'>";
echo "<b>".htmlspecialchars(trim($code[$backtrace[0]['line']-1]))."</b>\n";
echo "\n";
ob_start();
foreach ($args as $arg)
var_dump($arg);
$str = ob_get_contents();
ob_end_clean();
$str = preg_replace('/=>(\s+)/', ' => ', $str);
$str = preg_replace('/ => NULL/', ' → <b style="color: #000">NULL</b>', $str);
$str = preg_replace('/}\n(\s+)\[/', "}\n\n".'$1[', $str);
$str = preg_replace('/ (float|int)\((\-?[\d\.]+)\)/', " <span style='color: #888'>$1</span> <b style='color: brown'>$2</b>", $str);
$str = preg_replace('/array\((\d+)\) {\s+}\n/', "<span style='color: #888'>array•$1</span> <b style='color: brown'>[]</b>", $str);
$str = preg_replace('/ string\((\d+)\) \"(.*)\"/', " <span style='color: #888'>str•$1</span> <b style='color: brown'>'$2'</b>", $str);
$str = preg_replace('/\[\"(.+)\"\] => /', "<span style='color: purple'>'$1'</span> → ", $str);
$str = preg_replace('/object\((\S+)\)#(\d+) \((\d+)\) {/', "<span style='color: #888'>obj•$2</span> <b style='color: #0C9136'>$1[$3]</b> {", $str);
$str = str_replace("bool(false)", "<span style='color:#888'>bool•</span><span style='color: red'>false</span>", $str);
$str = str_replace("bool(true)", "<span style='color:#888'>bool•</span><span style='color: green'>true</span>", $str);
echo $str;
echo "</pre>";
echo "<div class='block tiny_text' style='margin-left: 10px'>";
echo "Sizes: ";
foreach ($args as $k => $arg) {
if ($k > 0) echo ",";
echo count($arg);
}
echo "</div>";
}
Zend_Debug::dump($var);
http://framework.zend.com/manual/en/zend.debug.html
My favorite var_dump
snippet is one I made years ago and have been working on perfecting ever since. I know there are lib's out there that create really pretty fancy dumps with accordion menus and all, but I just want a simple layout, easy to read, maybe a little HTML, and as portable as a single code-snippet method can be. Thus my function:
function preDump() { // use string "noEcho" to just get a string return only
$args = func_get_args();
$doEcho = TRUE; $sb;
if ($args) {
$sb = '<div style="margin: 1em 0;"><fieldset style="display:inline-block;padding:0em 3em 1em 1em;"><legend><b>preDump: '.count($args).' Parameters Found.</b></legend>';
foreach (func_get_args() as $arg) {
if (gettype($arg) == 'string') if ($arg == 'noEcho') { $doEcho = FALSE; $sb = preg_replace('/(preDump: )[0-9]+/', 'preDump: '.(count($args)-1), $sb); continue; }
$sb .= '<pre data-type="'.gettype($arg).'"';
switch (gettype($arg)) {
case "boolean":
case "integer":
$sb .= ' data-dump="json_encode"><p style="border-bottom:1px solid;margin:0;padding:0 0 0 1em;"><b>gettype('.gettype($arg).')</b></p><p>';
$sb .= json_encode($arg);
break;
case "string":
$sb .= ' data-dump="echo"><p style="border-bottom:1px solid;margin:0;padding:0 0 0 1em;"><b>gettype('.gettype($arg).')</b></p><p>';
$sb .= $arg;
break;
default:
$sb .= ' data-dump="var_dump"';
if (is_object($arg)) $sb .= 'data-class="'.get_class($arg).'"';
$sb .= '><p style="border-bottom:1px solid;margin:0;padding:0 0 0 1em;"><b>gettype('.gettype($arg).')';
if (is_object($arg)) $sb .= ' ['.get_class($arg).']';
$sb .= '</b></p><p>';
ob_start();
var_dump($arg);
$sb .= ob_get_clean();
if (ob_get_length()) ob_end_clean();
}
$sb .= '</p></pre>';
}
$sb .= '</fieldset></div>';
}
else {
$sb = '<div style="margin: 1em 0;"><fieldset style="display:inline-block;"><legend><b>preDump: [ERROR]</b></legend><h3>No Parameters Found</h3></fieldset></div>';
}
if ($doEcho) echo($sb);
return $sb;
}
Use is extremely simple. It takes infinite parameters. Also, it shows everything within simple fieldsets
for each preDump
called, as well as separating each parameter into its own pre
tag, thus making it clean and easy to read. Each pre
tag also contains a header showing the gettype
of each parameter, and, if it's an object, it will also show the class name
.
Use as easy as var_dump();
preDump(TRUE, 101, 'this is a string', array( 'array', 'here' ), (object)array ( 'this' => 'is', 'an' => 'object' ), $someXMLvariable);
You can also use it to get the dump as a simple string and then echo when you see fit:
$bob = preDump($someParam1, $someParam2, 'noEcho'); // 'noEcho' causes it to return as string only
精彩评论