Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 46 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
46
Dung lượng
1,36 MB
Nội dung
Chapter 5 [ 215 ] Fourth, in addition to using this utility for spot-checking our code, it should be able to handle exceptions as well. Hopefully, the code you have been writing is taking advantage of exceptions. After all, exceptions are the preferred way of handling condition out of our control in PHP 5.x and higher. Besides, exceptions represent an easy way to collect and display a lot of information about the state of the executing program at the point in the logical ow where the exception occurred. Fifth, since various core PHP functions and various modules generate errors instead of exceptions, it would be a nice feature if we could include such errors in our debug output. Just like exceptions being thrown by native PHP methods or our code, errors can represent unforeseen circumstances in the execution of our program. Sixth, in addition to displaying information about currently executing line of code, we would want to look at the sequence of functions and methods executed to arrive at the current line of code. This is called a backtrace and often consists of several methods or functions calling each other. Lastly, in addition to the debug information associated with exceptions and errors, we want to be able to have our utility output any additional variables, objects, or data structures we deem useful in diagnosing the problem. DebugException With that somewhat demanding list of requirements, please take a look at the code in the following listing: <?php class DebugException extends Exception { const PLAIN = 0; const HTML = 1; public static $sourceCodeSpan = 10; public static $outputFormat = self::HTML; public static $errorScope = E_ERROR; protected $addedDebug = array(); public function __construct($message, $code = 0) { // make sure everything is assigned properly parent::__construct($message, $code); // additional debug info? if (func_num_args() > 2) { Download from Wow! eBook www.WoweBook.com Debugging [ 216 ] $this->addedDebug = array_slice(func_get_args(), 2); } } public function __destruct() { // intentionally left blank } // to be called statically once to handle all exceptions & errors public static function init() { // handle all exceptions with this class set_exception_handler(array('DebugException', 'exceptionHandler')); // handle all errors with this class set_error_handler(array('DebugException', 'errorHandler'), self::$errorScope); // auto-detect / guess the output format if (php_sapi_name() == 'cli') { // plain text for CLI use self::$outputFormat = self::PLAIN; } else { // HTML output otherwise self::$outputFormat = self::HTML; } } // unregister error and exception handlers public static function unInit() { // pop exception handler stack restore_exception_handler(); // pop error handler stack restore_error_handler(); } // turn errors into DebugExceptions Download from Wow! eBook www.WoweBook.com Chapter 5 [ 217 ] public static function errorHandler($number, $message, $file, $line, $context) { // convert error to excepton and throw it $debugException = new DebugException($number, 0, $context); // transfer info to DebugException $debugException->file = $file; $debugException->line = $line; // throw the new DebugException throw $debugException; } // catching regular exceptions public static function exceptionHandler($exception) { // explicitly call this class's __toString() self::output($exception); } // collects & outputs the debug info public static function output(Exception $exception) { $output = array(); // output file name and line number $output[] = array('Summary:', 'An exception occurred in file ' . basename($exception->getFile()) . ' on line ' . $exception->getLine() . '.'); // output message $output[] = array('Error message: ', $exception- >getMessage()); // get source code of file that threw exception $sourceExcerpt = self::getSourceExcerpt($exception- >getFile(), $exception->getLine()); $output[] = 'Source code excerpt of lines ' . $sourceExcerpt['start'] Download from Wow! eBook www.WoweBook.com Debugging [ 218 ] . ' through ' . $sourceExcerpt['end'] . ' of file ' . $exception->getFile() . ':'; // highlight syntax for HTML output if (self::$outputFormat == self::HTML) { $output[] = array('', highlight_string(implode('', $sourceExcerpt['source']), TRUE)); } elseif (self::$outputFormat == self::PLAIN) { $output[] = implode('', $sourceExcerpt['source']); } // get backtrace nicely formatted $formattedTraces = self::getFormattedTrace($exception); // get additionally debug info nicely formatted $output = array_merge($output, self::getFormattedDebugInfo($exception)); // format output depending on how $outputFormat is set // output HTML first if (self::$outputFormat == self::HTML) { // have a show/hide link for each trace for ($i = 0; $i < sizeof($formattedTraces); $i++) { $output[] = '<a href="" onclick="var bt = document. getElementById(\'backtrace' . ($i + 1) . '\');if (bt.style.display == \'\') bt.style.display = \'none\';else bt.style.display = \'\';return false;">Backtrace step ' . ($i + 1) . ' (click to toggle):</a>'; $output[] = self::arrayToTable($formattedTraces[$i], 'backtrace' . ($i + 1)); } echo self::arrayToTable($output, null, 'Debug Output', FALSE); // output plain text } elseif (self::$outputFormat == self::PLAIN) { // merge traces into output array $output = array_merge($output, $formattedTraces); // flatten the multi-dimensional array(s) for simple outputting $flattenedOutput = self::flattenArray($output); Download from Wow! eBook www.WoweBook.com Chapter 5 [ 219 ] echo implode(PHP_EOL, $flattenedOutput); } } // extracts +/- $sourceCodeSpan lines from line $line of file $file public static function getSourceExcerpt($file, $line) { // get source code of file that threw exception $source = file($file); // limit source code listing to +/- $sourceCodeSpan lines $startLine = max(0, $line - self::$sourceCodeSpan - 1); $offset = min(2 * self::$sourceCodeSpan + 1, count($source) - $line + self::$sourceCodeSpan + 1); $sourceExcerpt = array_slice($source, $startLine, $offset); if ($startLine > 0) { array_unshift($sourceExcerpt, "<?php\n", "// \n"); } // return source excerpt and start/end lines return array('source' => $sourceExcerpt, 'start' => $startLine, 'end' => $startLine + $offset); } // creates array containing formatted backtrace // uses syntax highlighting for source code if // $outputFormat is HTML public static function getFormattedTrace(Exception $exception) { // init output array of formatted traces $formattedTraces = array(); // get traces from exception $traces = $exception->getTrace(); // init counter $count = 1; // iterate over traces foreach ($traces as $aTrace) { Download from Wow! eBook www.WoweBook.com Debugging [ 220 ] // skip the method where we turned an error into an Exception if ($aTrace['function'] != 'errorHandler') { // init output for this trace $output = array(); $output[] = "Backtrace step $count:"; // output class if given if (array_key_exists('class', $aTrace)) { $output[] = array('Class: ', $aTrace['class']); } // output type if given if (array_key_exists('type', $aTrace)) { $output[] = array('Type: ', $aTrace['type']); } // output function if given if (array_key_exists('function', $aTrace)) { $output[] = array('Function: ', $aTrace['function']); // output argument to function if (array_key_exists('args', $aTrace)) { $output[] = array('', 'with argument(s): ' . implode(', ', $aTrace['args'])); } } // get source code of file that threw exception $sourceExcerpt = self::getSourceExcerpt($aTrace['file'], $aTrace['line']); $output[] = 'Source code excerpt of lines ' . $sourceExcerpt['start'] . ' through ' . $sourceExcerpt['end'] . ' of file ' . $aTrace['file'] . ':'; // highlight syntax for HTML output if (self::$outputFormat == self::HTML) { $output[] = array('', highlight_string(implode('', Download from Wow! eBook www.WoweBook.com Chapter 5 [ 221 ] $sourceExcerpt['source']), TRUE)); } elseif (self::$outputFormat == self::PLAIN) { $output[] = implode('', $sourceExcerpt['source']); } $formattedTraces[] = $output; // increase step counter $count++; } } return $formattedTraces; } // formats the variables & objects passed to the constructor // and stored in $addedDebug. Uses syntax highlighting for // source code if $outputFormat is HTML public static function getFormattedDebugInfo(Exception $exception) { // init output array $output = array(); // only the DebugException class has the addedDebug property if (get_class($exception) == __CLASS__) { if (count($exception->addedDebug) > 0) { $output[] = 'Additional debug info:'; } // iterate over each variable foreach ($exception->addedDebug as $addBug) { foreach ($addBug as $debugLabel => $debugVar) { // format with print_r if (self::$outputFormat == self::HTML) { $output[] = array($debugLabel, '<pre>' . print_r($debugVar, TRUE) . '</pre>'); } elseif (self::$outputFormat == self::PLAIN) { $output[] = array($debugLabel, print_r($debugVar, TRUE)); } } } Download from Wow! eBook www.WoweBook.com Debugging [ 222 ] } return $output; } // converts an array of items to output to an HTML table // expects format: // array('some text here', <- single cell on row 1 // array('label', $value), <- two cells on row 2 // (label and value) // .); public static function arrayToTable(array $contents = array(), $id = null, $caption = null, $hideByDefault = TRUE) { $html = ''; // open table tag if (count($contents) > 0) { $html .= '<table style="width: 100%;border: 2px solid $html .= ($hideByDefault) ? 'none' : ''; $html .= ';"'; $html .= ($id != null) ? " id=\"$id\"" : ''; $html .= ">\n"; } // add caption if (!empty($caption) > 0) { $html .= '<caption><h2>' . htmlentities($caption) . "</h2></caption>\n"; } $rowCount = 1; $rowColors = array('#fff', '#ccc'); // iterate over input array foreach ($contents as $row) { $html .= "<tr style=\"background: " . $rowColors[($rowCount % 2)] . ";\">\n"; // split arrays into label and field if (is_array($row) && count($row) >= 2) { $html .= '<td><strong>' . htmlentities($row[0]) . "</strong></td>\n" Download from Wow! eBook www.WoweBook.com Chapter 5 [ 223 ] . '<td>' . $row[1] . "</td>\n"; // output single strings on a row by themselves } else { $html .= '<th colspan="2" style="text-align: left;">' . $row . "</th>\n"; } $html .= "</tr>\n"; $rowCount++; } // close table tag if (count($contents) > 0) { $html .= "</table>\n"; } return $html; } // takes a multi-dimensional array and flattens it for plain text output public static function flattenArray(array $inputArray = array()) { $outputArray = array(); // iterate over input array items foreach ($inputArray as $item) { if (is_array($item)) { // use recursion to traverse the hierarchy $outputArray = array_merge($outputArray, self::flattenArray($item)); } else { array_push($outputArray, $item); } } return $outputArray; } } DebugException::init(); ?>. Download from Wow! eBook www.WoweBook.com Debugging [ 224 ] There is a lot to absorb so let's take it one step at a time. The rst thing you will notice is that the DebugException class we are dening extends PHP's built-in Exception class. That way, we can more or less treat it like any other exception in our code. We start the class by dening two class constants that serve as labels to the two types of output formatting we will be supporting, namely HTML and plain text. The property $outputFormat should be set to the value of either of those two constants and we initialize it to be HTML. The assumption is that most of the time we will be using this utility class in a web-based debug session. There are two additional static properties. First, $sourceCodeSpan is an integer that indicates the number of lines preceding and following an error line number that will be extracted from the source le and displayed to the user. Second, $errorScope determines which errors will be intercepted by our class and converted to an exception. Possible values for $errorScope correspond to the valid setting for error_reporting in the php.ini conguration le. Since we are extending the Exception class, our constructor requires the exact same arguments as the parent class and calling the parent constructor is the rst thing we are doing. However, using the func_num_args() and func_get_args() functions we are able to support additional parameters to the constructor. To be precise, we allow any number of additional arguments that will simply be stored in the $addedDebug property. The idea is that we can pass any variables or objects that we wish to display in our debug output to the constructor when we instantiate and throw our DebugException class. The init() method is crucial to the way the DebugException class operates. It sets two static methods to be the exception and error handler. What this means is that the method DebugException::errorHandler will be called for any error that occurs in our application. Analogously, execution will be handed off to the DebugException ::exceptionHandler() method whenever the interpreter encounters an uncaught exception, including a DebugException. What this means in practice is that we only have to throw an exception at the line in our code where we want to generate debug output for the DebugException class to take over. In other words, all errors and exceptions will be handled by our class and to intentionally generate debug output all we have to do is generate one such error or exception. We will see how that is actually done in an example following the walk-through of the code. The other thing that the init() method does is that it tries to make an educated guess as to whether the output is best rendered in HTML or PLAIN format. The php_sapi_name() function provides information about the SAPI (Server Application Programming Interface) with which the currently running PHP executable was built. Chances are that php_sapi_name() will return cli if we're running a script from the command line. For simplicity, we assume that all other SAPIs signal that HTML output should be generated. Download from Wow! eBook www.WoweBook.com [...]... xdebug.org website Assuming the PECL installation ran without complaining, restart Apache, and check your phpinfo() output To see your phpinfo() output, create the following simple PHP script and view it in your browser It will display many details about how PHP was compiled, installed, and configured < ?php phpinfo(); ?> [ 233 ] Download from Wow! eBook www.WoweBook.com Debugging You should see Xdebug mentioned... file: [xdebug] ; tell PHP where to find the Xdebug extension and load it zend_extension=/usr/local/apache2 /php/ lib /php/ extensions/no-debug-nonzts-20 060 613/xdebug.so ; protection xdebug.max_nesting_level=100 ; what to show when outputting variables & debug info xdebug.show_local_vars=1 xdebug.collect_params=1 xdebug.var_display_max_children=128 xdebug.var_display_max_data=1024 [ 2 35 ] Download from Wow!... Not surprisingly, the debugger, server, and PHP executable we defined in the prior steps make an appearance in this screen that you can find by going to Project | Properties | PHP Debug [ 2 46 ] Download from Wow! eBook www.WoweBook.com Chapter 5 5 Let's select the Run | Debug Configurations option to define a debug launch configuration for our project Select PHP Web Page and click on the document icon... Download from Wow! eBook www.WoweBook.com Chapter 5 1 We have to let Eclipse know about our remote server We do this via the PHP Servers screen located at Preferences | PHP | PHP Servers This list of servers is used for a number of things, but we need to add our remote server here so we can later refer to it in our debug configuration As part of defining a PHP Server, we also need to map a directory on... correspond to similarly named Xdebug configuration variables [ 2 45 ] Download from Wow! eBook www.WoweBook.com Debugging 3 We need to tell Eclipse where it can find any local PHP executables Eclipse will use the executable for parsing and syntax checking of the source code files To edit or add to the list of PHP executables, open the Preferences | PHP | PHP Executables menu option 4 Now that we have all the... installations of PHP prior to version 5. 3) Also, make sure that the path in the above line points to an existing file • Scan you web server's and PHP' s log files for any startup or initialization errors • Consult Xdebug's online installation documentation: http://xdebug.org/docs/install Configuring Xdebug I'm using Xdebug 2.0 .5 to write this chapter This version lists 44 configuration parameters in the phpinfo()... following tools that we will look at in other chapters of this book: • Code Profiling allows you to optimize the performance of your application • Code Coverage Analysis feature will be used to test applications Xdebug is an Open Source project that was started and is being maintained by the main developer, Derick Rethans This debugger has been around since about 2003 and is at version 2.0 .5, which is PHP 5. 3... no mention of Xdebug in your phpinfo() output, it means that Xdebug was not installed properly Here are a few pointers on where to start troubleshooting the installation: • Make sure that you properly restarted your web server after installing Xdebug [ 234 ] Download from Wow! eBook www.WoweBook.com Chapter 5 • Check that your active php. ini file contains a line telling PHP to load the extension: (replace... can then fine-tune the configuration in your scripts on an as-needed basis < ?php // configure Xdebug locally ini_set('xdebug.var_display_max_children', 3); ini_set('xdebug.var_display_max_data', 6) ; ini_set('xdebug.var_display_max_depth', 2); class DebugExample { … } ?> [ 2 36 ] Download from Wow! eBook www.WoweBook.com Chapter 5 At this point, I hope that you are thinking back to the beginning of the... xdebug.profiler_output_dir=/tmp xdebug.profiler_output_name=xdebug.out.%s Remember that PHP can have multiple ini configuration files Often, this depends on which SAPI (Server Application Programming Interface) you are using For example, if you are using PHP from the command line, you are likely using the CLI SAPI, which will by default look for the php- cli.ini config file If Xdebug is not responding as expected, you . Apache, and check your phpinfo() output. To see your phpinfo() output, create the following simple PHP script and view it in your browser. It will display many details about how PHP was compiled,. developer, Derick Rethans. This debugger has been around since about 2003 and is at version 2.0 .5, which is PHP 5. 3 compatible, as of this writing. It runs on Mac, Linux, and Windows and you can nd. The php_ sapi_name() function provides information about the SAPI (Server Application Programming Interface) with which the currently running PHP executable was built. Chances are that php_ sapi_name()