根据一个广为流传的传说,第一个计算机 bug 是一只真正的虫子,它出现在 Harvard University 测试的 Mark II Aiken Relay Calculator 中的一个继电器上。根据操作员在 1947 年 9 月 9 日所做的日志记录,这只虫子是 “(在计算机中)发现的第一个 bug”。在图 1 中可以看到这份手写的日志记录和这只声名狼籍的虫子。 图 1. 声名狼籍的 Mark II 虫子实际上,bug 这个词的起源要早得多,可能比前面这次事件早了大约七十年。在 1848 年,Thomas Edison 在描述机械故障时写道,“首先是觉得有点儿不对劲儿,然后是一声爆响,接着麻烦就来了 —— 机器不正常了,出现了小故障和各种麻烦等 ‘bug’...” 显然,与 Edison 同时代的人已经把 bug 这个词当作行话了。 了解到即使是 Edison 也必须对其发明进行 “调试(debug)”,软件开发人员可能会觉得有所慰藉并受到鼓舞(Edison 没有用过 debug 这个词。这个词比较新,它是在二战时期从航空工业开始成为工程师的行话的)。或者,您可能希望所有的 bug 都是真正的虫子,那倒好办了,我们只需安装一个捕虫器就行了。“计算机 bug 进得来,但是出不去。”(有人能在 Subversion 的下一版中增加这个特性吗?) 但是,正如 Edison 所指出的,bug 是每个工程项目的固有部分。在讨论发明时,Edison 写道,“必须经过数月的艰苦观察、研究和劳动,才能让产品经受市场的考验。” 幸运的是,软件开发人员可以利用工具简化 “观察” 的过程,将花费的时间从几个月减少到几分钟,至少减少到几小时或几天。以前的一篇文章 “Squash bugs in PHP applications with Xdebug” 介绍了收集分析故障原因所需的信息的各种技术。但是,这种故障后分析往往很困难,而且很耗费时间,因为必须做出推测,然后进行测试。如果缺少关键的信息,那么必须反复研究、调整和测试代码,这个过程可能要重复许多次。 本文讨论一种更高效的调试技术:交互式调试(interactive debug)。有一种称为调试器(debugger) 的特殊应用程序,它们可以探测正在运行的代码,允许在任意的断点暂停执行,检查对象、检查调用堆栈和环境,甚至允许在运行时修改变量的值。 在本文中,将使用 Zend Debugger;它是 Zend 引擎的一个扩展,可以探察正在运行的 PHP 应用程序。可以免费下载并使用 Zend Debugger。但是,为了控制 Zend Debugger 和查看它的诊断信息,需要同时使用一个客户机应用程序。客户机可以是从命令行运行的简单程序,也可以是成熟的集成开发环境(IDE),提供编辑器、代码补全、图形化类浏览器等特性。 有好几种开放源码客户机可以与 Zend Debugger 进行交互,包括开放源码的 PHP Eclipse 插件。但是,我喜欢的 PHP IDE 是由 Zend Technologies 提供的 Zend Studio,这家公司同时提供 PHP 运行时引擎的开放源码版本和商业版本。与 PHP 本身不同,Zend Studio 是一个商业产品。可以下载这个软件并免费使用 30 天,这段时间足够您体会它的众多特性了;但是在此之后,如果希望继续使用它,就必须购买许可证。对于我来说,许可证的费用绝对物有所值。 您应该试试各种客户机,寻找适合自己的软件。例如,许多 IDE 包含您喜欢的编辑器,这样就不需要重新学习一套键盘快捷键。无论选择哪种软件,当您开始使用调试器客户机之后,很可能会发现再也离不开它了。您可以和 安装 Zend Studio 和 Zend Debugger首先,下载并安装 Zend Studio 和 Zend Debugger 软件。这里的步骤说明针对 Mac OS X,但是在 Linux? 和 Microsoft? Windows? 操作系统上安装步骤是相似的。(Zend Studio Web 页面为每种平台提供了安装说明。)实际上,可以在本地系统上安装并运行 Zend Studio,将调试器部署在服务器上,这样就可以进行远程代码调试。 无论使用哪种平台,都要确保系统中运行着 PHP V4 或 PHP V5:Zend 软件可以与这两个版本的 PHP 配合工作。因为 Mac OS X 当前附带 PHP V4(更精确地说,是 V4.4.7),所以本文基于这个比较老的 PHP 版本。 安装 Zend 工具的步骤如下:
为了检验 PHP 和 Web 服务器的运行是否正常,进入 ~/Sites/ 目录并创建包含以下内容的 info.php 文件: <?php phpinfo(); ?> 在浏览器中访问 http://localhost/~username/info.php,其中的 username 是您的 Mac OS X 登录名。如果配置正确,应该会在 图 4. 检验调试器是否正在工作请注意 顺便说一句,如果您觉得这个安装过程比较复杂,或者希望节省时间,那么可以下载并安装 Zend Core 或 Zend Platform。这两个软件是商业产品,但是都预先配置了 Zend Debugger。本文余下的说明对于 Zend Core 或 Zend Platform 同样是有效的。 连接 Zend DebuggerZend Studio 和 Zend Debugger 的组合允许远程控制 Zend 引擎。Zend Studio 作为控制面板,显示关于引擎内部机制的信息。可以看到正在运行的源代码、变量的状态、Web 环境的状态以及其他数据。 Zend Debugger 像是一个代理:它将来自 Zend 引擎的信息转发给 Zend Studio,将来自 Zend Studio 的命令转发给 Zend 引擎。命令包括各种调试概念,比如 因此,为了调试 PHP 应用程序,要让 Zend Studio 连接到调试器,然后在浏览器中访问应用程序。当应用程序开始运行时,调试器作为代理运行并将控制传递给 Zend Studio。这样开发人员就可以接管控制权了。 配置 Zend Studio下面是一种连接 Zend Debugger 的简单方法(以后我会提供一个比较复杂的脚本并讨论更多调试技巧):
现在,可以编写并调试 PHP 代码了。 调试一些简单的代码Zend Studio 提供了编写 PHP 代码所需的所有工具:一个用来编辑文件的编辑器;一个文件管理器,用来将许多文件集中在一个项目中;一个作为参考的 PHP 类和模块浏览器;一个用来查看中间结果的输出视图;一个环境浏览器,可以直接查看变量、调用堆栈和输出缓冲区。更好的是,编辑器是上下文敏感的,可以自动补全 PHP 控制结构、关键字和字符串分隔符。 首先,在 Zend Studio 顶部中间的 Editor 窗口中单击鼠标。输入 清单 1 所示的代码片段,体会一下编辑器的代码补全特性。这段代码的意图是输出数字 1-10。 清单 1. 输出数字 1-10<?php $counter = 1; while ( $counter < 10; ) { printf( "%d\n", $counter ); increment( $counter ); } function increment( $i ) { $i++; return; } ?> 输入完代码之后,单击工具栏中绿色的 Play 按钮运行代码。但是,代码并没有输出 1-10,而是不停地输出数字 1。单击红色的 Stop 按钮停止程序。 这里显然有 bug。首先要调查的是: 图 6. 在代码示例中设置的断点接下来,单击 Play 重新启动应用程序。程序在第 7 行停止执行。Debug Output 面板(在最右边)包含一些输出,Debug 窗口中的 Variables 选项卡显示变量的列表。数组标有方括号( 现在单击工具栏中蓝色的向下箭头,执行函数中的下一个语句(蓝色的向下箭头和短线的作用是调用函数,并前进到调用者中的下一个语句)。游标现在应该出现在第 11 行,Variables 选项卡应该只显示一个变量:形式参数 再次单击向下箭头,执行第 11 行。 图 7. 堆栈跟踪再次单击向下箭头,返回第 5 行。让人吃惊的是, 单击 Stop,然后再次单击 Play。分步执行这个程序,应该会看到 1, 2, 3, . . . 8, 9。怎么没有 10 呢?答案很明显:这是一个边界错误。只需将 尽管这个示例是人为制造的,但是它演示了几种基本的交互式调试技术:
要想进一步了解 Zend Studio,可以使用 IDE 编写 PHP 代码并单击各个选项卡,从而研究它的众多特性。即使选择在另一个程序中编辑代码,也可以利用 Zend Studio 调试类、方法和单元测试。在 IDE 中编写单元测试尤其有帮助。 调试 PHP 应用程序当然,PHP 调试器的真正价值在于实时探察 Web 应用程序。下面提供一个这样的场景。 比萨饼应用程序清单 2 和 清单 3 是一个简单的 PHP 应用程序,其中包含一个类、方法和一些包装器代码。清单 2(index.php)所示的应用程序使用 清单 2. 比萨饼订单应用程序,index.php<?php include_once( 'pizza.class.php' ); $large = new Pizza( 'L', null ); echo $large->price() . '<br />'; $toppings = array(); $toppings[] = 'pepperoni'; $toppings[] = 'sausage'; $xl = new Pizza( 'XL', $toppings ); echo $xl->price() . '<br />'; $special = new Pizza( 'XL' ); $special->add( null ); $special->add( 'pepperoni' ); $special->add( 'pepperoni' ); $special->add( 'anchovies' ); $special->add( 'anchovies' ); $special->add( 'olives' ); echo $special->price() . '<br />'; ?> 清单 3. Pizza 类<?php class Pizza { var $size; var $toppings = array(); var $price; function Pizza( $size = "R", $toppings = null ) { $this->size = $size; $this->toppings = $toppings; } function size() { return( $this->size ); } function price() { $this->price = 10; $multiplier = 1; switch ( $this->size ) { case 'L': $multiplier = 1.25; break; case 'XL': $multiplier = 2; break; } $this->price = $this->price + ( sizeof( $this->toppings() ) ) * $multipler; return( $this->price ); } function toppings() { return( $this->toppings ); } function add( $topping ) { $this->toppings[] = $topping; } } ?> 按照以下步骤创建项目:
我的代码出了什么问题?现在可以调试这个应用程序了。单击 Play:应用程序运行,它的输出出现在 Debug Output 面板中。输出如下: Content-type: text/html 10<br />10<br />10<br /> 为什么这三个比萨饼的价格都是 $10?如果看一下 Debug Messages 面板,答案就清楚了:变量 如果这个 bug 的原因不这么明显的话(常常是这种情况),还可以用另一种方法捕捉这个错误。双击 Pizza.class.php,将游标放在第 29 行上并选择 Debug > Add/Remove Breakpoint。这一行会变成红色的。双击 index.php 并单击 Play。程序在第 29 行停止执行。看一下 Debug 窗口, 作为一个练习,使用 IDE 分步探察另一个 bug,这是一个导致重复计价的逻辑错误。取消所有断点,然后在 index.php 中的第 14 行设置一个断点。在 图 13. 错误的 Pizza 对象为了纠正这个 bug,不允许 null 作为有效的配料(这实际上是 index.php 的错误,但是可以在 function toppings() { if ( is_array( $this->toppings ) ) { return( array_unique( $this->toppings ) ); } return( 0 ); } function add( $topping ) { if ( ! is_null( $topping ) ) { $this->toppings[] = $topping; } } 连接浏览器最后一个练习是在浏览器中启动应用程序并在 IDE 中调用它。典型的情况是:观察浏览器中的输出,与 Web 页面进行交互,然后分步执行应用程序,观察它如何做出响应:
如果应用程序包含表单,那么可以在表单处理函数上设置断点并查看输入的参数。在 Debug 窗口中可以查看所有全局环境变量、PHP 全局变量和每个 Web 请求的参数。 使用 Debug URL 窗口控制应用程序何时暂停。PHP Web 应用程序通常集中在一个目录中并包含一个主页(一般名为 index.php)。主页上的链接指向其他 PHP 页面,那些页面分别提供不同的特性。例如,URL .../store/index.php 可能是一个在线商店的主页;URL .../store/cart/index.php 可能实现购物车。 可以用 Zend Studio 调试某个应用程序根目录中的一个、几个或所有 PHP 页面。例如,如果购物车出现了错误,那么可以在打开 cart/ 中的任何页面时启动调试会话。也可以在浏览主页 index.php 时启动调试会话,调试整个应用程序。 更好的是,可以组合使用 Zend Studio、Internet Explorer? 或 Mozilla Firefox 以及 Zend Debugger Browser Toolbar,从而在浏览器中直接启动调试会话。可以调试当前页面、站点上的所有页面或某个表单的操作。 一种新的工作方式交互式调试器就像是矿工头上的矿灯。诊断问题所需的艰苦劳动突然变得容易了,我们不必在黑暗中苦苦摸索了 —— 只需设置断点,分步执行代码,观察每个语句发生的情况。用一个笔记本记下每次暂停时的情况,观察并推测原因。 Zend Studio 不是调试器的惟一选择。试试其他调试器,寻找最适合您的工作习惯的调试器。无论如何,务必选用一种调试器。您会发现编写代码时离不开它。 参考资料学习
获得产品和技术
|
|