在web开发中,PHP,Python,Ruby都是比较常见的开源语言工具,有很多开源的框架和平台可以使用,从效率来说,PHP无疑是一种最高效的web语言。但配置PHP开发调试环境并不容易。

开发调试的方法
PHP调试可分为使用PHP调试器和不用PHP调试器两种方式。前者是指用PHP debugger和IDE来进行断点调试,后者则是利用PHP的错误报告和浏览器等方式来进行调试。Xdebug和Zend Debugger是两种常用的调试器,但在使用调试器的方式,难点在于生产和测试环境的分离,以及调试器的安装配置。非调试器的方式则比较灵活,本文将探索不用PHP调试器的代码调试的方法,不论你是个人开发者还是生产环境的运维人员,都将受益于这次实战记录。
- 使用PHP错误报告
当发现代码有错时,PHP引擎能生成非常有用的调试信息,可分为三大类:Error,Warning,Notice。举例来说:当有语法错误时,这将是个Error信息,当数据库连接不上时,这会是一个Warning信息,当变量名拼写错误等,这会是一个Notice信息。不过出于安全考虑,在生产环境中,你并不希望除你之外,还有其它用户可以看到这类信息。因此可以按下面方法处理 :
- 启用错语报告 :在php.ini中搜索error_reporting,设置为error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT ,这一般也是生产环境的默认设置
- 关闭全局的HTML错误输出 设置display_errors = Off 和 log_errors = On 这样不会在浏览器中看到PHP的错误报告,而是记录到php的log
- 使HTML错误输出仅自己看到,一般要用到运行时的ini_set()函数,有几种方法:
拷贝法: 复制一份代码到安全的测试环境,然后在要调试的文件中加入:
ini_set('display_errors', '1');
IP限制法: 可直接在生产环境中备份好原始代码,然后加入调试代码:
$myIP = '1.1.1.1';
/* Check the request IP address. */
if ($_SERVER['REMOTE_ADDR'] == $myIP)
{
/* Enable HTML error reporting. */
ini_set('display_errors', '1');
}
除此之外,还可以用指定用户法(可参见后续代码示例一并使用),或使用请求参数等方法配合init()在运行时显示仅自己可见的错误信息,这里不再敖述。
- 调试代码变量
用echo打印变量
虽然错误报告能给代码语法错误的解决带来帮助,但我们更常用的是查看变量情况来修复代码,最简单的做法就是直接用echo打印变量,比如下面这个例子,当以debug用户访问页面时打印domain变量:
getName() == 'debug')
{
/* Debugging. */
echo $domain;
}
get_defined_vars()函数
echo的方式只适用于知道要检查哪个变量,有时我们需要检查脚本中的所有变量,这时可以用这个函数,它会返回所有已经定义的数组,例如
$addr = 'www.google.com'; $dotPos = mb_strpos($addr, '.'); $domainPos = $dotPos + 1; $domain = mb_substr($addr, $domainPos); /* Debug. */ echo '< pre>'; print_r(get_defined_vars()); echo '< /pre>;';
get_defined_vars函数显示用户定义的变量和系统变量,包括$_SERVER和web请求变量($_POST, $_GET…),这使得它成为非常强大的工具。上面的输出示例如下,出于可读性,我删除一些东西
Array
(
[_GET] => Array
(
)
[_POST] => Array
(
)
[_COOKIE] => Array
(
)
[_FILES] => Array
(
)
[_REQUEST] => Array
(
)
[_SERVER] => Array
(
[MIBDIRS] => C:/xampp/php/extras/mibs
[MYSQL_HOME] => \xampp\mysql\bin
[OPENSSL_CONF] => C:/xampp/apache/bin/openssl.cnf
[PHP_PEAR_SYSCONF_DIR] => \xampp\php
....
[PHP_SELF] => /test.php
[REQUEST_TIME_FLOAT] => 1595999862.718
[REQUEST_TIME] => 1595999862
)
[addr] => www.google.com
[dotPos] => 3
[domainPos] => 4
[domain] => google.com
)
如果您需要调试的是某个函数或类,可以在该函数或类中调用,如:
function myFunction($arg1, $arg2, $arg3)
{
$var1 = $arg1 + $arg2;
$var2 = $arg1 * $arg2;
$arrayVar = [$arg1, $arg2, $arg3, $var1, $var2];
/* This prints all the variables in the current scope. */
echo '< /pre>';
print_r(get_defined_vars());
echo '</pre>';
}
/* Call myFunction() */
myFunction(5, 10, 15);
- 使用debug_backtrace()调试函数
这个方法会显示函数调用的全部历史,比如:
function f1(int $arg)
{
$a = f2($arg + 1);
$b = $a + 2;
return $b;
}
function f2(int $arg)
{
$a = f3($arg);
$b = $a * 3;
return $b;
}
function f3(int $arg)
{
$a = $arg * 10;
echo '< /pre>';
print_r(debug_backtrace());
echo '< /pre>';
return $a;
}
$val = f1(5);
上面f1里调用f2,f2里调用f3,f3处插入debug_backtrace,上面f1里调用f2,f2里调用f3,f3处插入debug_backtrace,返回的是一个数组,依次由后到前的函数调用,即先显示f3函数 ,再显示f2,再显示f1,如下所示:
Array
(
[0] => Array
(
[file] => C:\xampp\htdocs\test.php
[line] => 15
[function] => f3
[args] => Array
(
[0] => 6
)
)
[1] => Array
(
[file] => C:\xampp\htdocs\test.php
[line] => 6
[function] => f2
[args] => Array
(
[0] => 6
)
)
[2] => Array
(
[file] => C:\xampp\htdocs\test.php
[line] => 33
[function] => f1
[args] => Array
(
[0] => 5
)
)
)
每个数组包括:
file:被调用函数所在的文件路径,如果函数是在include包含的文件,显示的是include文件的路径;
line:被调用函数所在行;
function:函数名称;
arg:函数参数;
您也可以使用 debug_print_backtrace()来替代debug_backtrace(),这样会更方便一些,例如:
function f3(int $arg)
{
$a = $arg * 10;
echo '< /pre>';
debug_print_backtrace();
echo '< /pre>';
return $a;
}
对应的输出结果是:
#0 f3(6) called at [C:\xampp\htdocs\test.php:15] #1 f2(6) called at [C:\xampp\htdocs\test.php:6] #2 f1(5) called at [C:\xampp\htdocs\test.php:33]
- 记录debug数据
上面讲了如何在HTML输出中显示调试信息,下面说说如何记录这些数据到log文件中,调试数据log不仅可以长久保存,你也无需使用技巧以防止其它访客可以看到,并且还能记录特定用户的活动。
把调试数据写到log文件中,最简单的办法是用fopen()函数,如:
>
/* Open the log file. */
$handle = fopen('c:\xampp\debug.log', 'ab');
function addLog($handle, string $log)
{
/* Datetime to add at the beginning of the log line. */
$date = date('d/m/Y H:i:s');
/* Complete log line. */
$line = $date . ' ' . $log . "\n";
/* Add the new line to the log file. */
fwrite($handle, $line);
}
上面fopen第一个参数是log文件路径,第二个参数是标记符,”a”代表添加到文件尾,“b”防止自动新行。
当然,需要log文件所在路径允许php写入。
上面函数用法示例:
$addr = 'www.google.com'; $domain = mb_substr($addr, mb_strpos($addr, '.') + 1); /* Log the $domain variable */ addLog($handle, 'domain is: ' . $domain);
或是记录前面说的get_defined_vars函数,打印的所有变量到log文件中,

