从 Division by zero 到 set_error_handler

最近做了个简单的表格应用,其中有个功能是支持公式运算,如下图,毛利率这列是其他两列相除算出来的:

Division by zero

做完,客户还没用一天,就碰到问题了,因为有时候公式中的被除数是 0,这时候 log 中一堆 「Division by zero」的 Warning,影响查看其他的 log 的查看。

刚开始我想到的办法是在填充公式变量的时候,判断一下被除数是零就抛出「除零错误」,不去真正计算就好了,确实可以解决问题。嘻嘻,我还是挺聪明的。😎

但是很快又碰到新的问题了,因为有这样的公式:$a / ($b + $c) ,然后 ($b + $c) 的结果为 0。😂

如果继续按照原来的做法,我要根据运算的优先级,来判断被除数是 0,如果公式一复杂,那么什么时候是个头啊。🤦🤦‍♂️🤦‍♀️🤕

Warning 还是异常

先搜索了一圈 Division by zero,然后在 PHP 文档,看到 PHP 就已经有一个预定义的异常 DivisionByZeroError

但是为啥我的代码能够执行成功,只是在 log 中出现一堆 「Division by zero」的 Warning,那就继续看文档:

在 PHP 7 中使用算术运算符 / 不会抛出异常,而在 PHP 8 中会抛出异常,可惜客户的系统还在使用 PHP 7.4。

直接问 DeepSeek 我该怎么办?它给了我四个方法:

1. 手动检查并抛出异常

我这个又不是简单的被除数,真的是。🙄

2. 使用 set_error_handler 捕获警告

貌似这个可以。😘

3. 封装除法操作类

我是算术公式,这个不合适。🤪

4. 升级 PHP 8

我能升级就不会问你了。🤬

看来使用方案二了!

使用 set_error_handler 捕获警告

就是通过 set_error_handler 函数将警告转换成异常抛出。

set_error_handler(function($no, $str){
	if(str_contains($str , 'Division by zero')){
		throw new DivisionByZeroError($str); 
	}

	throw new ErrorException($str , $no);

	return true;
});

如果警告信息的字符串里面有 Division by zero,就抛出 DivisionByZeroError 异常,其他情况抛出 ErrorException

然后原来的执行表达式运算的代码改成 try - catch 结构:

try{
	// 原来执行运算表达式代码
}catch(DivisionByZeroError $e){
	return $if_error ?? '!除零错误';
}catch(throwable $e){
	return $if_error ?? '!计算错误';
}

完美解决!🍾🥂🎆🎇🎊

还没开心 5 分钟,很快就来了但是。😞😮‍💨

如果这样自定义错误处理程序,那么之后的代码的警告都被他接管了,然后直接抛出异常。这 🙄🫤

继续查 PHP 文档,问 DeepSeek,原来 set_error_handler 有返回值的,如果之前有定义的错误处理程序,则返回之前定义的错误处理程序,如果没有定义或者是内置的错误处理程序,则返回 null。

这样的话,那就好处理,给 try - catch 结构加上 finally,最终的代码如下:

$handler	= set_error_handler(function($no, $str){
	if(str_contains($str , 'Division by zero')){
		throw new DivisionByZeroError($str); 
	}

	throw new ErrorException($str , $no);

	return true;
});

try{
	// 原来执行运算表达式代码
}catch(DivisionByZeroError $e){
	return $if_error ?? '!除零错误';
}catch(throwable $e){
	return $if_error ?? '!计算错误';
}finally{
	if($handler){ // 之前有定义的错误处理程序
		set_error_handler($handler);
	}else{ // 恢复到内置的错误处理程序
		restore_error_handler();
	}
}

搞定,收工,记录一下!📝 🐂🍺


©我爱水煮鱼,本站推荐使用的主机:阿里云,国外主机建议使用BlueHost

本站长期承接 WordPress 优化建站业务,请联系微信:「chenduopapa」。