php教程:php设计模式介绍之观测模式(2)
为了实现以观测模式为基础的错误记录句柄,首先我们注意到作为观测者的FileErrorLogger类和EmailErrorLogger类什么也不能做。那么,FileErrorLogger类是如何向一个文件写出错信息,EmailErrorLogger类又如何发送电子邮件的? 接下来,让我来看看用来实现观测模式的技术细节,然后,再集中精力来看看该模式的主体――ErrorHandler的细节。最后,再写一些错误处理函数来调用这个ErrorHandler类。
最后用下面的这一段代码来表示:
// PHP4
$eh =& getErrorHandlerInstance();
$eh->attach(new EmailErrorLogger(‘jsweat_php@yahoo.com’));
$eh->attach(new FileErrorLogger(fopen(‘error.log’,’w’)));
set_error_handler(‘observer_error_handler’);
// ... later
trigger_error(‘this is an error’);
ErrorHandler类是一种单件模式(参考第4章:The Singleton Pattern)。它可以通过函数Attach()来注册各种错误信息观测者,而set_error_handler()函数就是一个指向ErrorHandler类的函数。最后,当一个错误信息被触发后,所有的观测者都会得到通知。
为了使这次观测的操作生效,你的测试必须能证明所有的这些操作(将错误信息写入日志,利用电子邮件发送错误信息)都能得到执行,并且能正常工作。简而言之,让我们来看看一系列简单的测试。(和这个实例有关的其他更多实例,可以在本书附带的源代码中找到)
这里有FileErrorLogger类联合测试的一部分代码:它用来测试当FileErrorlogger类被某个对象实例化时,是否具有向一个文件写日志的能力。
class FileErrorLoggerTestCase extends UnitTestCase {
var $_fh;
var $_test_file = ‘test.log’;
function setup() {
@unlink($this->_test_file);
$this->_fh = fopen($this->_test_file, ‘w’);
}
function TestRequiresFileHandleToInstantiate() { /* ... */ }
function TestWrite() {
$content = ‘test’.rand(10,100);
$log =& new FileErrorLogger($this->_fh);
$log->write($content);
$file_contents = file_get_contents($this->_test_file);
$this->assertWantedPattern(‘/’.$content.’$/’, $file_contents);
}
function TestWriteIsTimeStamped() { /* ... */ }
}
在这个测试中, setup()函数创建了一个文件指针,指向一个名为“test.log”的新文件。并且,将该指针保存在变量$_fh中,这个可写的文件指针将作为一个变量传递给FileErrorlogger对象的实例,进行测试。变量$content的值将传递给函数write(),并且,在存储结束后,还将用来被检查$content的值是否确实被正确写入test.log文件中。
(这个测试要求PHP必须具有向那个新建的test.log中写数据的权限。)
下面的一些代码也许可以帮助FileErrorLogger类通过测试。
class FileErrorLogger {
var $_fh;
function FileErrorLogger($file_handle) {
$this->_fh = $file_handle;
}
function write($msg) {
fwrite($this->_fh, date(‘Y-m-d H:i:s: ‘).$msg);
}
}
一个类似的测试代码可以使EmailErrorLogger类生效。
class EmailErrorLoggerTestCase extends UnitTestCase {
function TestEmailAddressFirstConstructorParameter() {
$log =& new EmailErrorLogger;
$this->assertErrorPattern(‘/missing.*1/i’);
}
function TestMail() {
$log =& new EmailErrorLogger(‘jsweat_php@yahoo.com’);
$log->mail(‘test message’);
}
}
接下来,通过这个测试的EmailErrorLogger类的代码如下:
class EmailErrorLogger {
var $_addr;
var $_subject;
function EmailErrorLogger($addr,
$subject=’Application Error Message’) {
$this->_addr = $addr;
$this->_subject = $subject;
}
function mail($msg) {
mail($this->_addr
,$this->_subject
,date(‘Y-m-d H:i:s: ‘).$msg);
}
}
你是怎样确定EmailErrorLogger类能真正发送电子邮件的呢?是的,你可以打开你的收件箱,看看其中是否有新邮件,就知道了。但是,那就不是一个全自动的测试了。 或者说,这个测试就只是伪模式的一个不错的替代方案。(至于如何创建一个控制邮件的类,将作为一个练习留给读者的。详细信息,请参考第6章The MockObject Pattern 或参考FakeMail项目http://sf.net/projects/fakemail/.)