PHP 程序员的调试技术

<p>简介</p><p>有许多 PHP 调试技术可以在编码的时候节约大量时间。一个有效却很基本的调试技术就是打开错误报告。另一个略微高级一点的技术包括使用 print 语句,通过显示在屏幕上实际出现的内容,有助于精确地找出更难发现的 bug。PHPEclipse 是一个 Eclipse 插件,能够强调常见的语法错误,可以与调试器结合起来用于设置断点。</p><p>设置</p><p>要学习本文描述的概念,需要 PHP、Web 服务器和 Eclipse。调试器扩展支持的 PHP 版本是 V5.0.3。</p><p>我们需要一个 Web 服务器来解析用 PHP 创建的页面并把它们显示到浏览器。本文中使用的是 Apache2。但是,任何 Web 服务器都可以满足要求。</p><p>要利用本文中介绍的一些调试技术,需要安装 Eclipse V3.1.1 和插件 PHPEclipse V1.1.8。由于 Eclipse 要求 Java™ 技术,所以还要下载它。</p><p>还需要 PHP 的调试器扩展模块。安装它略有些麻烦。请仔细跟随安装调试器扩展的操作说明。现在,先在 php.ini 文件中注释掉那些要求装入和配置 PHP 扩展的行。在需要使用调试器的时候,再取消注释。</p><p>请参阅 参考资料 获得下载信息。现在介绍出错消息。</p><p>出错消息</p><p>出错消息是作为开发人员的第一道防线。谁都不想在一台没有配置成显示出错消息的服务器上用 PHP 开发代码。但是,请记住,当代码调试完成,准备运行的时候,应当确保关闭了错误报告,因为不希望站点的访问者看到出错消息,因为这会给他们提供足够的信息来利用站点的弱点并黑掉站点。</p><p>也可以用出错消息为自己服务,因为它们会显示抛出或生成错误的正确代码行。这样,调试就变成在浏览器上查看生成的错误所显示的行号,并在代码中检查这一行。稍后,将会看到 PHPEclipse 插件通过即时地给语法错误加下划线并在保存文件时用红色 &quot;x&quot; 标注语法错误,可在开发和调试过程中提供极大的帮助。</p><p>先来看如何在 php.ini 文件中开启错误报告并设置错误报告的级别。然后将学习如何在 Apache 的配置文件中覆盖这些设置。</p><p>PHP 的错误报告</p><p>php.ini 文件中有许多配置设置。您应当已经设置好自己的 php.ini 文件并把它放在合适的目录中,就像在 Linux 上安装 PHP 和 Apache 2 的文档说明中所示的那样(请参阅 参考资料)。在调试 PHP 应用程序时,应当知道两个配置变量。下面是这两个变量及其默认值:</p><p>display_errors = Off</p><p>error_reporting = E_ALL</p><p>通过在 php.ini 文件中搜索它们,可以发现这两个变量当前的默认值。display_errors 变量的目的很明显 —— 它告诉 PHP 是否显示错误。默认值是 Off。但是,要让开发过程更加轻松,请把这个值设为 On:</p><p>display_errors = On</p><p>error_reporting 变量的默认值是 E_ALL。这个设置会显示从不良编码实践到无害提示到出错的所有信息。E_ALL 对于开发过程来说有点太细,因为它在屏幕上为一些小事(例如变量未初始化)也显示提示,会搞糟浏览器的输出。我只想看到错误和不良编码实践,但是不想看到无害的提示。所以,请用以下值代替 error_reporting 的默认值:</p><p>error_reporting = E_ALL &amp; ~E_NOTICE</p><p>重新启动 Apache,就全部设置好了。接下来,将学习如何在 Apache 上做同样的事。</p><p>服务器上的错误报告</p><p>依赖于 Apache 正在做的工作,在 PHP 中打开错误报告可能没法工作,因为在计算机上可能有多个 PHP 版本。有时很难区分 Apache 正在使用哪个 PHP 版本,因为 Apache 只能查看一个 php.ini 文件。不知道 Apache 正在使用哪个 php.ini 文件配置自己是一个安全问题。但是,有一种方法可以在 Apache 中配置 PHP 变量,从而保证设置了正确的出错级别。</p><p>而且,最好知道如何在服务器端设置这些配置变量,以否决或抢占 php.ini 文件,从而提供更高级别的安全性。</p><p>在配置 Apache 时,应该已经接触过 &lt;apache2-install-dir&gt;/conf/httpd.conf 中 http.conf 文件中的基本配置。</p><p>要做在 php.ini 文件中已经做过的事,请把下列各行添加到 httpd.conf,覆盖任何 php.ini 文件:</p><pre class="brush:php;toolbar:false">php_flagdisplay_errorson php_valueerror_reporting2039</pre><p>这会覆盖在 php.ini 文件中为 display_errors 已经设置的标志,以及 error_reporting 的值。值 2039 代表 E_ALL &amp; ~E_NOTICE。如果愿意采用 E_ALL,请把值设为 2047。同样,还是要重启 Apache。</p><p>接下来,要在服务器上测试错误报告。</p><p>测试错误报告</p><p>如果启动了错误报告,会节约许多时间。PHP 中的错误会指向代码中的错误。请创建一个简单的 PHP 文件 test.php,并像清单 1 所示一样定义它。</p><p>清单 1. 一个生成错误的简单 PHP</p><pre class="brush:php;toolbar:false">&lt;?php print(&quot;Thenextlinegeneratesanerror.&lt;br&gt;&quot;); printaline(&quot;PLEASE?&quot;); print(&quot;Thiswillnotbedisplayedduetotheaboveerror.&quot;); ?&gt;</pre><p>第一个 print() 语句会向 Web 浏览器显示它的内容。但是第二个语句会生成错误并在 Web 页面上显示。这造成最后一个 print() 语句不起作用,如图 1 所示。</p><p>图 1. 生成错误</p><p><img src="/up_pic/201805/170236371992.jpg" title="170236371992.jpg" alt="1.jpg"/></p><p>生成错误</p><p>现在开启了错误报告!接下来,用 print 语句帮助调试应用程序。</p><p>介绍 print 语句</p><p>因为应用程序中的功能性 bug 不会产生错误,所以在所有调试策略中,关于如何正确地放置和使用 print 或 die 语句来调试 PHP 应用程序的知识是一种很好的资产。可以用 print 语句在代码中缩小对问题语句的定位,这些语句在语法上没有错误,也不是 bug,但是从代码的功能上看是 bug。这些是最难发现和调试的 bug,因为它们不会抛出错误。惟一知道的就是在浏览器上显示的内容不是想要的内容,或者想要保存在数据库中的内容根本没有保存。</p><p>假设正在处理通过 GET 请求发送过来的表单数据,想向浏览器显示信息,但是出于某种原因,数据没有正确地提交,或者不能正确地从 GET 请求中读出。要调试这类问题,重要的是用 print() 或 die() 语句知道变量的值是什么。</p><p>die() 语句会中止程序执行,并在 Web 浏览器上显示文本。如果不想注释掉代码,而且只想显示到出错之前的信息和出错信息,不想显示后面的信息,那么 die() 语句特别有用。</p><p>让我们在 PHP 中用 print 语句来测试这个概念</p><p>使用 print 语句进行调试</p><p>在我作程序员的那些时候,当我在 Linux® 上开发应用程序时,没有方便的 GUI 可以告诉我 bug 在哪,我迅速地发现我在程序中放的 print 语句越多,我在应用程序中把 bug 的范围缩小到一行的机会越大。请创建另一个 PHP 文件 test2.php,并像清单 2 所示的那样定义它。</p><p>清单 2. 显示通过 GET 提交的所有变量</p><pre class="brush:php;toolbar:false">&lt;?php $j=&quot;&quot;; print(&quot;Letsretrieveallthevariablessubmittedtothis&quot;); print(&quot;scriptviaaGETrequest:&lt;br&gt;&quot;); foreach($_GETas$key=&gt;$i){ print(&quot;$key=$j&lt;br&gt;&quot;); } if($_GET[&#39;Submit&#39;]==&quot;SendGETRequest&quot;) $j=&quot;done!&lt;br&gt;&quot;; ?&gt; &lt;formmethod=&quot;GET&quot;&gt; Name:&lt;inputname=&quot;name&quot;&gt;&lt;br&gt; Email:&lt;inputname=&quot;email&quot;size=&quot;25&quot;&gt;&lt;br&gt; &lt;inputname=&quot;Submit&quot;type=&quot;submit&quot;value=&quot;SendGETRequest&quot;&gt; &lt;/form&gt;</pre><p>您可能会非常容易地发现清单 2 中的 bug!您很棒!但请注意这是一个非常简单的脚本,只是作为使用 print 语句进行调试而展示的一个例子而已。这个脚本只是提取 GET 请求中的所有变量,如果有,就把它们显示在浏览器上。还提供了一个表单,用 GET 请求向服务器发送变量以进行测试。请看输出,如图 2 所示。</p><p>图 2. test2.php 的输出</p><p><img src="/up_pic/201805/170236513514.jpg" title="170236513514.jpg" alt="2.jpg"/></p><p>test2.php 的输出</p><p>现在单击 Send GET Request 按钮,请注意只有 $_GET 请求的键显示在浏览器上,而正确的值都没显示。可以在循环中放一个 print 语句,检验在 foreach 循环中每个元素中是否确实存在数据。请参阅清单 3。</p><p>清单 3. 用 print 语句验证代码的功能</p><pre class="brush:php;toolbar:false">... foreach($_GETas$key=&gt;$i){ print(&quot;Correctdata?&quot;.$_GET[$key].&quot;&lt;br&gt;&quot;); print(&quot;$key=$j&lt;br&gt;&quot;); } ...</pre><p>放进去的 print 语句是粗体。注意,现在已经知道在 Web 浏览器上显示的 $key 值是正确的,但是由于某些原因,值没有正确地显示。请看新的输出,如图 3 所示。</p><p>图 3. 修改后的 test2.php 的输出</p><p><img src="/up_pic/201805/170237133457.jpg" title="170237133457.jpg" alt="3.jpg"/></p><p>修改后的 test2.php 的输出</p><p>现在已经知道应用程序正确地从 GET 请求接收到了变量,那么肯定是在代码中有 bug。查看之后注意到,用来显示值的变量 $j 是错误的。在 foreach 语句中指定的是 $i,所以它肯定会有正确的值,但是无意之中输入了 $j。所以通过把 $j 替换成 $i,迅速地修正了错误,重新载入页面之后,就看到了正确的输出,如图 4 所示。</p><p>图 4. 修正后的 test2.php 的输出</p><p><img src="/up_pic/201805/170237192181.jpg" title="170237192181.jpg" alt="4.jpg"/></p><p>修正后的 test2.php 的输出</p><p>现在可以删除或注释掉刚才添加的 print 语句了,因为已经发现了代码中的 bug。注意,这只是在调试应用程序时可能遇到的许多错误中的一个很小的子集。对于使用数据库时可能遇到的问题,一个好的解决方案是输出 SQL 语句,以确保执行的 SQL 就是想要执行的。</p><p>现在要来看看如何使用 Eclipse IDE 和 PHPEclipse 插件及调试器扩展进一步在调试历程中提供帮助。</p><p>使用 PHPEclipse</p><p>您可能用过 Eclipse,但是可能不熟悉它。请参阅 参考资料 获得 Eclipse 平台的介绍。</p><p>用于 Eclipse 的 PHPEclipse 插件是用来开发 PHP 应用程序的一个流行工具。请启动 Eclipse 并把工作区目录指定为 Apache 的 www 目录(在我的机器上是 c:www)。现在单击 File &gt; New &gt; Project。会弹出 New Project 向导。双击 PHP 文件夹并选择 PHP Project。单击 Next,输入项目名称 debugArticle,并单击 Finish。</p><p>如果把 Web 服务器设置为在端口 80 上侦听,那么不需要做任何修改。否则,请转到 Navigator 窗口,在 PHP 项目 debugArticle 上右击,选择 Properties,然后单击 PHP Project Settings。单击 Configure Workspace Settings 然后修改合适的 localhost 或者添加 Web 服务器侦听的端口(例如 http://localhost:8080)。单击 Apply 完成设置。</p><p>Navigator 窗口应当显示项目和一个 .project 文件。在项目上右击,就像前面做的那样,只是这次选择 New &gt; PHP File。用想要创建的 PHP 文件的名称 test3.php 替换 *.php,然后单击 Finish。在 Eclipse IDE 中应当出现一个新文件。可能需要导航到窗口底部的 PHP 浏览器来查看 PHP 文件的当前输出(参见图 5)。</p><p>图 5. Eclipse 的 PHPEclipse 插件</p><p><img src="/up_pic/201805/170237254630.jpg" title="170237254630.jpg" alt="5.jpg"/></p><p>Eclipse 的 PHPEclipse 插件</p><p>注意,只有 Windows® 的用户可以像清单 5 所示的那样使用 PHP 浏览器。通过打开独立浏览器窗口并把浏览器指向测试脚本所在目录,也可以使用同样的功能。</p><p>现在来演示这个应用程序,证明它的强大能力。</p><p>在 &quot;使用调试器&quot; 一节中,将学习如何用 Eclipse、PHPEclipse 和前面下载的调试器 PHP 扩展来调试 PHP 应用程序。先从学习如何使用它的语法解析功能开始。</p><p>语法解析和加下划线</p><p>先从查看 PHPEclipse 如何提供帮助调试 PHP 应用程序的实时语法解析功能开始。要看这个特性的实际应用,先从在 Eclipse 中定义 test3.php 开始,如下所示。</p><pre class="brush:php;toolbar:false">&lt;?php print(,&quot;HelloWorld!&quot;); ?&gt;</pre><p>注意,在清单 4 中加下划线的两个字符在 Eclipse 中加了下划线,提示语法不正确。按 Ctrl+S 保存文件,会在 Eclipse 中显示解析错误:在代码中与解析错误对应的行上会加上红 &quot;x&quot;,如图 6 所示。</p><p>图 6. 语法错误强调</p><p><img src="/up_pic/201805/170237323813.jpg" title="170237323813.jpg" alt="6.jpg"/></p><p>语法错误强调</p><p>现在演示 PHP 浏览器。这个窗口提供了当前 PHP 脚本的预览,如图 6 所示。</p><p>从上面定义的 test3.php 中删除逗号(,)。按 Ctrl+S 保存文件,然后观察 PHP 浏览器窗口的更新,显示了 Hello World(参见图 7)。</p><p>图 7. 在 PHPEclipse 中预览 PHP 脚本</p><p><img src="/up_pic/201805/170237374984.jpg" title="170237374984.jpg" alt="7.jpg"/></p><p>在 PHPEclipse 中预览 PHP 脚本</p><p>下面是用调试器在 PHP 中设置断点。</p><p>使用调试器</p><p>使用调试器,可以设置断点并查看 PHP 代码到所设断点之前的浏览器输出。然后可以继续代码的执行并查看到下一断点之前的浏览器输出,然后再到下一个,直到 PHP 脚本完成。</p><p>现在把 &quot;设置&quot; 一节中在 php.ini 中注释掉的行取消注释,并重新启动 Apache。现在装入了调试器,Eclipse 能够和它挂上了。</p><p>现在在 Eclipse 中设计调试环境。请创建新的 test4.php 文件,先保持为空。现在单击 Run &gt; Debug。在左侧面板中选择 PHP DBG Script,并单击 New。现在转到 File 选项卡,输入当前项目 debugArticle 以及想要调试的文件 test4.php。现在转到 Environment 选项卡,然后再到 Interpreter 子选项卡。在 PHP 的安装目录中找到 php.exe 文件(我的是 c:appsphp5.0.3php.exe)。现在单击 Remote Debug 子选项卡,选择 Remote Debug,如果没有使用 Windows,请取消 &quot;Open with DBGSession URL in internal browser box&quot; 复选框。把 Remote Source 路径设置成与要测试的 PHP 脚本的绝对路径(不是 Web 路径)相同(我的设置是 c:wwwdebugArticletest4.php)。 现在单击 Debug。</p><p>现在应当装入 Debug 透视图,如图 8 所示。否则,请单击 Window &gt; Open Perspective &gt; Other,并选择 Debug。</p><p>图 8. Eclipse 中的 Debug 透视图</p><p><img src="/up_pic/201805/170237421220.jpg" title="170237421220.jpg" alt="8.jpg"/></p><p>Eclipse 中的 Debug 透视图</p><p>现在可以设置断点了。</p><p>对于本文中使用的插件和扩展的版本,断点功能是必需的,因为 PHP 在把输出发送到浏览器之前会缓冲它。除此之外,需要做的不仅仅是设置一个断点把当前显示数据刷新到 Web 浏览器,所以要像下面和图 8 所示那样定义 test4.php。</p><p>清单 4. 设置和创建断点</p><pre class="brush:php;toolbar:false">&lt;?php functionbreak-point(){ ob_flush(); flush(); sleep(.1); debugBreak(); } print(&quot;Thiswillgetshownfirst,&quot;); print(&quot;aswillthis&lt;br&gt;&quot;); breakpoint(); print(&quot;Thiswon&#39;tgetshownuntilafter&quot;); print(&quot;continuingthebreak-point&lt;br&gt;&quot;); breakpoint(); print(&quot;END!&quot;); ?</pre><p>breakpoint() 函数会把缓冲的输出和其他缓冲的数据刷新到 Web 浏览器。对 sleep(.1) 的调用是必需的,这样代码中止于 debugBreak() 之前,服务器才有足够的时间把数据刷新到 Web 浏览器,这个函数是前面下载的 PHP 调试器扩展的内部函数。这样,调用 breakpoint() 会把 HTML 块、print() 和 echo() 语句的数据刷新到浏览器,然后中止代码执行。</p><p>在像清单 4 那样编写完代码之后,可以打开浏览器并指向 test4.php,或者可以查看 PHP 浏览器窗口(我的是 http://localhost/debugArticle/test4.php)。每次输入和保存文件时,在 PHP 浏览器窗口中就已经启动了调试序列。如果不使用 Windows,请通过浏览器查看 test4.php。在保存了文件之后,用 F8 或单击 Run &gt; Resume 继续代码执行。持续这么做,直到最后一行输出是 END! 为止(参见图 9、10 和 11)。</p><p>图 9. 初始的到第一个断点的 PHP 浏览器输出</p><p><img src="/up_pic/201805/170237512169.jpg" title="170237512169.jpg" alt="9.jpg"/></p><p>初始的到第一个断点的 PHP 浏览器输出</p><p>请注意图 9 中的 Debug 窗口如何把执行显示为挂起的。</p><p>图 10. 第一个断点之后到第二个断点之前的 PHP 浏览器输出</p><p><img src="/up_pic/201805/170238002916.jpg" title="170238002916.jpg" alt="10.jpg"/></p><p>第一个断点之后到第二个断点之前的 PHP 浏览器输出</p><p>图 10 的 Debug 窗口仍然把执行显示为挂起,而第二组数据显示在 PHP 浏览器中。</p><p>图 11. 完整的 PHP 浏览器输出</p><p><img src="/up_pic/201805/170238071395.jpg" title="170238071395.jpg" alt="11.jpg"/></p><p>完整的 PHP 浏览器输出</p><p>注意,图 11 的 Debug 窗口中的代码不再挂起,整个脚本已经执行,如图 11 中的 PHP 浏览器所示。</p><p>既然已经看到了用 PHPEclipse 和调试器扩展进行开发的优势,那么很难想像没有它会怎么样。</p><p>结束语</p><p>现在已经向 PHP 的调试技术集中添加了错误报告的运用、print 语句、PHPEclipse 和调试器扩展,您可以通过减少每行代码的错误数量,成为更有效的 PHP 编码人员。请参阅 参考资料 获得一些 PHP 教程,可以在上面测试这些新技能。</p>
RangeTime:0.002948s
RangeMem:215.22 KB
返回顶部 留言