Php 妥善处理不可避免的警告
有许多很好的旧过程函数可以在常规操作中可能发生的错误条件下发出警告,例如:Php 妥善处理不可避免的警告,php,Php,有许多很好的旧过程函数可以在常规操作中可能发生的错误条件下发出警告,例如:fopen(),mail(),oci\u connect() 据说这种方法价格昂贵,而且有隐藏一切的副作用: 自定义错误处理程序看起来有些过分: private function warningHandler($errno, $errstr, $errfile, $errline/*, array $errcontext*/){ if(error_reporting()===0){ return
fopen()
,mail()
,oci\u connect()
- 据说这种方法价格昂贵,而且有隐藏一切的副作用:
- 自定义错误处理程序看起来有些过分:
private function warningHandler($errno, $errstr, $errfile, $errline/*, array $errcontext*/){ if(error_reporting()===0){ return false; }else{ throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } } // .... try{ set_error_handler('CSVLoader::warningHandler', E_WARNING); $this->fp = fopen($filename, 'r'); restore_error_handler(); $this->readData(); }catch(ErrorException $e){ restore_error_handler(); throw new CSVLoaderException("Could not open file: {$e->getMessage()}"); }
- 即使在正确配置的服务器中,让警告通过也很烦人:
-
在发展中,它们在无处可见,可以打破事物
。
- 在生产中,他们用无用的数据淹没错误日志
很快,您将在代码库中添加适当的检查,并对问题进行排序。我对一些流行的PHP框架和流行的PackageGist软件包进行了一次快速、不科学的调查,发现:
- 错误抑制运算符相当流行(CodeIgniter、Nette、Swift Mailer、Yii…)
- 许多其他项目使用
抛出set\u error\u handler()
或其他一些异常(Zend、Symfony、Laravel、PHPUnit、Psy…)。最简单的方法是使用项目范围内的错误处理程序,一些(PHPMailer)在set/restore调用中包装特定的操作ErrorException
- 其他一些项目显然并不在意(PHPExcel)
- 如果您已经有了自定义错误处理程序,那么最好利用它
- 否则,错误控制操作符就可以了(但在开发代码时暂时删除它可能是个好主意)。所谓的性能差*,特别是如果您正在执行“缓慢”操作,如打开数据库连接、套接字或文件
- 作为辩论,它可能与制表符和空格在同一个领域
@
与isset()
进行比较,并确定@
非常慢。实际情况是,isset()
实际上非常快
最后但并非最不重要的一点是,由于性能问题,我将再次分享我的第一个自制brew基准测试,这是一个快速而肮脏的基准测试,没有适当的基准测试框架。请注意,到目前为止,我只测试了最坏的情况(所有函数调用都会生成警告):
在PHP/5.4.24上运行10000次迭代。。。
原始值:1.170秒
@:1.185s
本地设置错误处理程序():1.357s
常规设置错误处理程序():1.326s
在PHP/5.5.22上运行10000次迭代。。。
原始值:1.185s
@:1.341s
本地设置错误处理程序():4.960s
常规设置错误处理程序():4.570s
在PHP/5.6.10上运行10000次迭代。。。
原始值:1.139s
@:1.139s
本地设置错误处理程序():1.263s
常规设置错误处理程序():1.232s
在PHP/7.0.0beta1上运行10000次迭代。。。
原始值:1.232s
@:1.279s
本地设置错误处理程序():1.669s
常规设置错误处理程序():1.669s
通常在进行函数调用之前,通过检查是否设置了正确的条件,即使用文件获取内容
,进行可读
调用,这不会引发错误,但会让您了解文件获取内容
是否会出错@ᴀᴛʜ-我不是很信服。如何提前知道数据库服务器是否接受连接?即使您变得顽固,并使用fsockopen()
模拟一个,您也需要进行更多以前的检查,以防fsockopen()
也会触发警告。这种方法还意味着选择不使用诸如文件获取内容()
之类的工具:任何读取文件的代码都需要大量的样板文件。我感谢您花时间撰写答案,但我不确定您是否真正理解了我的问题。假设您维护一个库来发送电子邮件。当传出SMTP连接失败时,库会抛出一个漂亮的FooMailer\TransportException
。触发(并记录!)额外的不可捕获的E_警告有什么意义?难道最终用户不觉得在他们的新闻稿数据库中将消息标记为“未发送”更合理吗?最终用户永远不会发现错误日志或类似的消息有很大帮助-他们需要更有用的消息(“对不起,发生了非常糟糕的事”,“您没有正确填写所有表单字段”)。如何在您的应用程序中检测出错误取决于您。异常情况与检查返回值或其他情况一样有其位置。显然,在设计应用程序/系统时,您知道有些事情可能会失败(向许多地址发送新闻稿,并可在以后重试),而其他事情则是异常事件-(例如,数据库服务器已关闭)
private function warningHandler($errno, $errstr, $errfile, $errline/*, array $errcontext*/){
if(error_reporting()===0){
return false;
}else{
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
}
// ....
try{
set_error_handler('CSVLoader::warningHandler', E_WARNING);
$this->fp = fopen($filename, 'r');
restore_error_handler();
$this->readData();
}catch(ErrorException $e){
restore_error_handler();
throw new CSVLoaderException("Could not open file: {$e->getMessage()}");
}
<?php
error_reporting(-1);
ini_set('display_errors', false);
ini_set('log_errors', false);
define('ITERATIONS', 10000);
function warning_handler($errno, $errstr, $errfile, $errline/*, array $errcontext*/){
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
echo sprintf('Running %s iterations on PHP/%s...' . PHP_EOL, number_format(ITERATIONS), phpversion());
$t0 = microtime(true);
for($i=0; $i<ITERATIONS; $i++){
fopen("FooA $i", 'r');
}
echo sprintf('Raw: % 6.3fs' . PHP_EOL, microtime(true) - $t0);
$t0 = microtime(true);
for($i=0; $i<ITERATIONS; $i++){
@fopen("FooB $i", 'r');
}
echo sprintf('@: % 6.3fs' . PHP_EOL, microtime(true) - $t0);
$t0 = microtime(true);
for($i=0; $i<ITERATIONS; $i++){
set_error_handler('warning_handler', E_WARNING);
try{
fopen("FooC $i", 'r');
restore_error_handler();
}catch(ErrorException $e){
restore_error_handler();
}
}
echo sprintf('Local set_error_handler(): % 6.3fs' . PHP_EOL, microtime(true) - $t0);
$t0 = microtime(true);
set_error_handler('warning_handler', E_WARNING);
for($i=0; $i<ITERATIONS; $i++){
try{
fopen("FooD $i", 'r');
}catch(ErrorException $e){
}
}
restore_error_handler();
echo sprintf('General set_error_handler(): % 6.3fs' . PHP_EOL, microtime(true) - $t0);