XDebug的使用

 

XDebug是Derick Rethans写的PHP的一个扩展(extension),从使用的方式来看,主要包括二部分内容:一是其提供了一系列的在PHP文件中使用的函数;另一部分是基于DBGp协议的远程调试的功能,在此主要总结一下第二部分的内容。

 

具体的网站:http://www.xdebug.org,目前的版本为: 2.1.0,此版本支持PHP4和PHP5。

安装方法:

一、可以在网站上下载源代码后编译和安装xdebug,具体可以参加源代码包中的README文件;

二、Xdebug是PHP的一个比较成熟的扩展了,所以也可以直接网络安装编译后的文件。我所使用的是Ubuntu 10.04系统,用下面的命令可以完成安装:

sudo apt-get install php5-xdebug

 

配置方法:

如果是以第一种方法安装xdebug的,那么需要自己创建一个.ini文件,来放置相关的配置信息;当然也可以在现有的.ini中放置xdebug的配置信息。如果需要对PHP命令行以及网页中的PHP都进行调试时,则需要确保这个.ini文件在二种情况下都会被使用。

在我的机器上,目录结构为:

/etc/php5/apache2/conf.d (此conf.d为一链接,指向/etc/php5/conf.d)

/etc/php5/apache2/php.ini

/etc/php5/cli/conf.d (此conf.d为一链接,指向/etc/php5/conf.d)

/etc/php5/cli/php.ini

/etc/php5/conf.d

由于apache2及cli的conf.d都指向/etc/php5/conf.d, 所以我只要在此目录中放置xdebug的配置文件:xdebug.ini

 

配置文件中的内容:

zend_extension=/usr/lib/php5/20060613+lfs/xdebug.so

xdebug.remote_enable=1

xdebug.remote_autostart=1

说明:

第一行:根据xdebug.so的实际位置来设置zend_extension项

第二行:要进行远程调试,则必须设置xdebug.remote_enable为1

第三行:Xdebug有多种方式进入debug状态;而如果设置xdebug.remote_autostart为1,则当运行到PHP代码时,Xdebug模块会进入debug状态。

还有其它的一些选项,具体可以查看帮助文档。

 

检查配置:

1)如果xdebug是用于对PHP命令行调试的:

命令php –m会列出所有安装的模块,xdebug应该也是其中的一项;

如果命令php –m没有列出php模块,则可以通过命令php –ini来查看是否xdebug.ini包含在输出的所有的.ini列表中;

php –re xdebug会列出xdebug的具体情况,包括配置项信息及xdebug提供的所有函数,确保以下二项与你的配置是一致的:

    Entry [ xdebug.default_enable <ALL> ]

      Current = '1'

}

Entry [ xdebug.remote_autostart <ALL> ]

  Current = '1'

}

2)如果xdebug是用于对网页中的PHP内容调试的:

可以通过在网页中调用PHP函数phpinfo(), 其输出中应该包含以下信息:

additional .ini files parsed中包含xdebug.ini

xdebug.ini的配置信息等,配置信息中包含:

xdebug.remote_enable on

xdebug.remote_autostart on

 

目前已有很多的IDE中集成了XDebug的调试工具,完整的列表可以参见xdebug的网上文档。在XDebug的源代码包中,也自带了一个基于终端命令行的xdebug_client工具,用户需要手工输入基于DBGp的调试命令,具体的命令行请参见:http://www.xdebug.org/docs-dbgp.php,但是这个使用是比较繁琐的,所以我基于DBGp协议自己用PHP写了一个调试客户端。

 

具体代码如下:

#!/usr/bin/php <?php $last_command = ""; $source_begin_line = 1; $current_line = 0; $show_debug_info = 0; function strip_header_number($str) { for($i = 0; ord($str[$i]) <= 0x39 && ord($str[$i]) >= 0x30; $i++); return substr($str, $i); } function xml2assoc($xml, &$output) { $tree = null; while($xml->read()) switch ($xml->nodeType) { case XMLReader::END_ELEMENT: return $tree; case XMLReader::ELEMENT: $node = array('tag' => $xml->name, 'value' => $xml->isEmptyElement ? '' : xml2assoc($xml, &$output)); if($xml->hasAttributes) { while($xml->moveToNextAttribute()) { if(strcmp($xml->name, "encoding") == 0 && strcmp($xml->value, "base64") == 0){ $node['value'] = base64_decode($node['value']); $output = $node['value']; } $node['attributes'][$xml->name] = $xml->value; } } $tree[] = $node; break; case XMLReader::TEXT: case XMLReader::CDATA: $tree .= $xml->value; } return $tree; } function parsexml($str) { global $last_command, $source_begin_line, $current_line, $show_debug_info; $xml = new XMLReader(); $xml->XML($str); $output = ""; $assoc = xml2assoc($xml, &$output); $xml->close(); unset($xml); if($show_debug_info){ print_r($assoc); echo "/$last_command = $last_command/n"; } $result = $assoc[0]['value'][0]; if(strcmp($result['tag'], "error") == 0){ printf("Error: code => %s, value => %s/n", $result['attributes']['code'], $result['value'][0]['value']); handle_error(0 + $result['attributes']['code']); return; } if(strcmp($last_command, "step_over") == 0 || strcmp($last_command, "step_into") == 0 || strcmp($last_command, "step_out") == 0){ $result = $assoc[0]['value'][0]['attributes']; echo "Current status:/n"; printf(" [filename] => %s/n", $result['filename']); printf(" [lineno] => %s/n", $result['lineno']); $current_line = 0 + $result['lineno']; }else if(strcmp($last_command, "run") == 0){ $result = $assoc[0]['value'][0]['attributes']; $current_line = 0 + $result['lineno']; }else if(strcmp($last_command, "source_list") == 0){ if(strlen($output) > 0){ echo "Source Code:/n"; //Output source code in special format $sarr = explode("/n", $output); foreach($sarr as $i => $value){ if($i + $source_begin_line == $current_line) printf("/033[22;32m%04d: /033[01;31m%s/033[01;37m/n", $i+$source_begin_line, $value); else printf("/033[22;32m%04d: /033[01;37m%s/n", $i+$source_begin_line, $value); } } }else if(strcmp($last_command, "print_variable") == 0){ echo "Command Result:/n"; $result = $assoc[0]['value'][0]; if(is_array($result['value'])){ $result = $result['value']; for($i=0; $i<count($result); $i++){ printf('%s : "%s"' . "/n", $result[$i]['attributes']['fullname'], $result[$i]['value']); } }else{ printf("%s: %s/n", $result['attributes']['fullname'], $result['value']); } }else if(strcmp($last_command, "breakpoint_list") == 0 || strcmp($last_command, "breakpoint_get") == 0 || strcmp($last_command, "stack_get") == 0){ echo "Command Result:/n"; $result = $assoc[0]['value']; foreach($result as $i => $value){ echo $i . ":/n"; $bk_info = $value['attributes']; foreach($bk_info as $bk_key => $bk_value){ echo " $bk_key: $bk_value/n"; } } }else if(strcmp($last_command, "evaluate") == 0){ echo "Command Result:/n"; $result = $assoc[0]['value'][0]; if(is_array($result['value'])){ $result = $result['value']; for($i=0; $i<count($result); $i++){ printf('%s => "%s"' . "/n", $result[$i]['attributes']['name'], $result[$i]['value']); } }else{ printf("%s/n", $result['value']); } } } function command($client, $cmd, $first_greet = false) { if(!$first_greet){ global $show_debug_info; if($show_debug_info){ echo "/$cmd = $cmd/n"; } socket_write($client, $cmd, strlen($cmd)) or die("Could not write commands to the client/n"); socket_write($client, "/0", 1) or die("Could not write commands to the client/n"); } //Get output from the client $input = socket_read($client, 10240) or die("Could not get any text from client/n"); $len = 0 + $input; $result = strip_header_number($input); $result = implode("", explode("/0", $result)); if($show_debug_info){ echo "Info from client:/n"; echo "$result/n"; } parsexml($result); } function step_into($client, $var){ command($client, 'step_into -i 1'); } function step_over($client, $var){ command($client, 'step_over -i 1'); } function step_out($client, $var){ command($client, 'step_out -i 1'); } function print_variable($client, $var){ command($client, 'property_get -i 1 -d 0 -n ' . $var); } function status($client, $var){ command($client, 'status -i 1'); } function feature_set($client, $var){ command($client, 'feature_set -i 1' . $var); } function feature_set_1($client, $var){ command($client, 'feature_set -i 1 -n max_children -v 32' . $var); } function feature_set_2($client, $var){ command($client, 'feature_set -i 1 -n max_data -v 1024' . $var); } function feature_set_3($client, $var){ command($client, 'feature_set -i 1 -n max_depth -v 1' . $var); } function stack_get($client, $var){ command($client, 'stack_get -i 1'); } function evaluate($client, $var){ command($client, "eval -i 1 -- " . base64_encode($var)); } function breakpoint_set($client, $var){ //ok for simplest mode: bs/breakpoint_set lineno if(strcmp($var, sprintf("%d", 0 + $var)) == 0){ command($client, 'breakpoint_set -i 1 -t line -n ' . $var); return; } //ok for bs :lineno or bs filename.php:lineno $result = preg_match("/(.*?):(.*)$/", $var, $matches); if($result){ $filename = $matches[1]; $lineno = $matches[2]; if(strcmp($filename, "") == 0){ command($client, 'breakpoint_set -i 1 -t line -n ' . $lineno); }else{ command($client, "breakpoint_set -i 1 -t line -f $filename -n $lineno"); } }else{ //ok for full command command($client, 'breakpoint_set -i 1 ' . $var); } } function breakpoint_get($client, $var){ //ok for br/breakpoint_get id without -d if(strcmp($var, sprintf("%d", 0 + $var)) == 0){ command($client, 'breakpoint_get -i 1 -d ' . $var); return; } command($client, 'breakpoint_get -i 1 ' . $var); } function breakpoint_remove($client, $var){ //ok for br/breakpoint_remove id without -d if(strcmp($var, sprintf("%d", 0 + $var)) == 0){ command($client, 'breakpoint_remove -i 1 -d ' . $var); return; } //ok for full command command($client, 'breakpoint_remove -i 1 ' . $var); } function breakpoint_list($client, $var){ command($client, 'breakpoint_list -i 1'); } function breakpoint_update($client, $var){ command($client, 'breakpoint_update -i 1 ' . $var); } function source_list($client, $var = NULL){ global $current_line, $source_begin_line; if(isset($var) && strlen(trim($var)) > 0){ $result = preg_match("/.*?-b/s*(/d+)/", $var, $matches); if($result == 1){ $source_begin_line = 0 + $matches[1]; command($client, 'source -i 1 ' . $var); }else{ echo "Invalid command as [source -i 1 " . $var . "]/n"; } }else{ $source_begin_line = $current_line > 8 ? $current_line - 8 : 1; $source_end_line = $current_line + 8; command($client, "source -i 1 -b $source_begin_line -e $source_end_line"); } } function run($client, $var){ command($client, 'run -i 1'); } function stop($client, $var){ command($client, 'stop -i 1'); } function detach($client, $var){ command($client, 'detach -i 1'); } function switch_mode($client, $var){ global $show_debug_info; $show_debug_info = 1 - $show_debug_info; } function help($client, $var){ global $commands; if(isset($var) && strcmp($var, "breakpoint") == 0){ echo "Command samples:/n"; echo " bs 15, to set break point at line 15 with current file./n"; echo " bs :15, to set break point at line 15 with current file./n"; echo " bs filename.php:15, to set break point at line 15 of file filename.php./n"; echo " bl, list all break points./n"; echo " bg [-d] break_point_id, get info of break point with specified id./n"; echo " br [-d] break_point_id, remove break point of break_point_id./n"; echo " bs -t conditional -n 6 -- JHN1bSA+IDIwMA==, conditional breakpoint./n"; echo "Command options:/n"; echo " -t type/n"; echo " -n lineno/n"; echo " -f filename/n"; echo " -m function/n"; echo " -x exception/n"; echo " -h hit_value/n"; echo " -o hit_condition/n"; echo "Breakpoint type and required attributs:/n"; echo " type required attributs/n"; echo " ----------- ---------------------/n"; echo " line filename, lineno/n"; echo " call function/n"; echo " return function/n"; echo " exception exception/n"; echo " conditional expression, lineno/n"; echo " watch expression/n"; }else{ print_r($commands); } } function handle_error($error_no){ $errors = array( 0 => 'No error', 1 => 'Parse error in command', 2 => 'Duplicate arguments in command', 3 => 'Invalid options (ie, missing a required option, invalid value for a passed option)', 4 => 'Unimplemented command', 5 => 'Command not available (Is used for async commands. For instance if the engine is in state "run" then only "break" and "status" are available).', 100 => 'Can not open file (as a reply to a "source" command if the requested source file cant be opened)', 101 => 'Stream redirect failed', 200 => 'Breakpoint could not be set (for some reason the breakpoint could not be set due to problems registering it)', 201 => 'Breakpoint type not supported (for example I dont support "watch" yet and thus return this error)', 202 => 'Invalid breakpoint (the IDE tried to set a breakpoint on a line that does not exist in the file (ie "line 0" or lines past the end of the file)', 203 => 'No code on breakpoint line (the IDE tried to set a breakpoint on a line which does not have any executable code. The debugger engine is NOT required to return this type if it is impossible to determine if there is code on a given location. (For example, in the PHP debugger backend this will only be returned in some special cases where the current scope falls into the scope of the breakpoint to be set)).', 204 => 'Invalid breakpoint state (using an unsupported breakpoint state was attempted)', 205 => 'No such breakpoint (used in breakpoint_get etc. to show that there is no breakpoint with the given ID)', 206 => 'Error evaluating code (use from eval() (or perhaps property_get for a full name get))', 207 => 'Invalid expression (the expression used for a non-eval() was invalid)', 300 => 'Can not get property (when the requested property to get did not exist, this is NOT used for an existing but uninitialized property, which just gets the type "uninitialised" (See: PreferredTypeNames)).', 301 => 'Stack depth invalid (the -d stack depth parameter did not exist (ie, there were less stack elements than the number requested) or the parameter was < 0)', 302 => 'Context invalid (an non existing context was requested)', 900 => 'Encoding not supported', 998 => 'An internal exception in the debugger occurred', 999 => 'Unknown error' ); if(!isset($errors[$error_no])) { echo "Unknown error no in handle_error."; } else { echo "Detailed error info:/033[01;31m/n" . $errors[$error_no] . "/033[01;37m/n"; } } $commands = array( "s" => "step_into", "n" => "step_over", "p" => "print_variable", "stepout" => "step_out", "print" => "print_variable", "status" => "status", "set" => "feature_set", "s1" => "feature_set_1", "s2" => "feature_set_2", "s3" => "feature_set_3", "sg" => "stack_get", "stack_get" => "stack_get", "b" => "breakpoint_set", "bs" => "breakpoint_set", "breakpoint_set" => "breakpoint_set", "bu" => "breakpoint_update", "breakpoint_update" => "breakpoint_update", "br" => "breakpoint_remove", "breakpoint_remove" => "breakpoint_remove", "bg" => "breakpoint_get", "breakpoint_get" => "breakpoint_get", "bl" => "breakpoint_list", "breakpoint_list" => "breakpoint_list", "l" => "source_list", "list" => "source_list", "c" => "run", "run" => "run", "stop" => "stop", "detach" => "detach", "mode" => "switch_mode", "h" => "help", "?" => "help", "help" => "help", "help breakpoint" => "help", "eval" => "evaluate" ); $address= '127.0.0.1'; $port = 9000; $sock = socket_create(AF_INET, SOCK_STREAM, 0); socket_bind($sock, $address, $port) or die(); echo "Waiting for the conntion.../n"; socket_listen($sock); $client = socket_accept($sock); echo "Connection established: $client/n"; socket_close($sock); command($client, null, true); while(true) { $s = readline("cmd>"); readline_add_history($s); $s = trim($s); if(strcmp($s, "quit") == 0){ break; }else{ if(strlen($s) > 0){ $cmd = explode(' ', $s); if(isset($commands[$cmd[0]])){ $func = $commands[$cmd[0]]; $last_command = $func; $func($client, implode(' ', array_slice($cmd, 1))); if(strcmp($last_command, "step_into") == 0 || strcmp($last_command, "step_over") == 0 || strcmp($last_command, "step_out") == 0) { $last_command = "source_list"; $last_command($client); } else if(strcmp($last_command, "stop") == 0 || strcmp($last_command, "detach") == 0) { break; } }else{ echo "Unknown command/n"; print_r($commands); } } } } socket_close($client); echo "quit"; ?>  

 

使用方式:

1)先运行调试客户端:./xdebug_client.php, 会显示Waiting for the conntion...,处于等待状态;

2)当在命令行中运行任何其它.php脚本,或者在浏览器中加载PHP页面时,调试客户端会进入命令状态,显示为:

Connection established: Resource id #5

cmd>

3)输入s或者step_into;开始进入调试状态,会显示为:

XDebug的使用_第1张图片

 

表示当前debug的文件为verify.php

目前将要运行第二行代码,在源代码显示区域中,红颜色显示的为将要运行的代码

4)可以尝试其它命令,如

b 5 在第5行设置断点

p _POST显示变量$_POST中的内容

bl 列出所有的断点

eval $_POST[‘type’] 显示$_POST[‘type’]中的内容

所有的命令可以通过help命令来查看

 

你可能感兴趣的:(PHP,function,socket,command,attributes,output)