异常是面向对象编程的工具,它提供了一种优雅的方式来抛出和处理(对待)应用程序错误。
异常首先被抛出(thrown
),被处理(try
),被捕捉(catch
)。只有投掷是强制性的。
在异常出现之前,编程中的错误处理是非常复杂的,因为我们必须依赖函数的返回值,并以自己的方式捕捉它们,并做出相应的行为。
事实上,函数本身并不执行错误处理,这可能会导致致命的问题,但David在Programmers don't ignore errors中已经写到了这一点。
一个被遗忘的错误处理的例子。
// 从磁盘到磁盘的移动copy('c:/oldfile', 'd:/newfile');unlink('c:/oldfile');// 如果第一次操作失败,该文件将被不可挽回地删除。
这是因为处理copy()
函数输出的正确方法是不继续,并抛出一个错误。在良好的旧功能的情况下,它可能看起来像这样。
function backup(): bool{if (copy('c:/oldfile', 'd:/newfile')) {return unlink('c:/oldfile');}return false;}
我们的 "backup() "函数只有在 "copy() "函数没有失败且 "unlink() "函数返回 "true "时才会返回 "true"。否则,它将返回 "false"。
但现在这样做对应用来说是否安全?它不是!因为现在我们必须在调用backup()
函数的时候对待它的输出,如果它失败了,我们甚至不知道原因。简而言之,它将返回 "false",我们必须以某种方式自己检测错误。我想在这种情况下,看到程序员经常放弃错误处理,或者干脆忘记处理一些东西,而应用程序却因此抛出难以检测的错误,这是一件好事。
解决这个问题的方法是使用强制处理的异常,如果不处理,应用程序就会完全崩溃,我们总能找到原因。
在PHP中,异常是一种特殊的接口,由我们将要使用的本地Exception
类实现。
如果程序的某些部分处理失败,我们只需抛出一个描述该问题的异常。
if (copy('c:/oldfile', 'd:/newfile') === false) {throw new \Exception('不能复制文件 "oldfile"。');}
抛出一个异常是通过throw
关键字完成的,然后创建一个带有异常的类的实例。我们也可以通过其他方式获得一个实例(例如,从一个变量中传递),而仅仅创建一个异常实例并不会导致它被抛出。
`Exception'类构造函数的第一个参数接受异常的文本,它应该简明扼要地解释刚刚发生了什么。良好的做法是,也包括正在进行的操作的信息和对数据的参考。例如,如果文件复制失败,传递文件名是很好的做法。如果SQL查询执行失败,我们再次传递正在执行的查询。这对我们以后处理错误时有很大帮助,因为我们可以准确地看到问题所在。
例如,让我们有一个函数backup()
来备份数据,并可能抛出一对错误。
function backup(): void{if (copy('c:/oldfile', 'd:/newfile')) {if (unlink('c:/oldfile') === false) {throw new \Exception('无法删除旧文件。');}}throw new \Exception('不能复制备份文件。');}
注意,该函数不返回任何输出,我们在定义中指定了 "void "类型。该函数不需要返回任何东西,因为成功被认为是一个没有抛出错误的状态,我们不需要对待一个积极的情况。
如果我们在一个没有处理的应用中使用该函数,例如,如下所示。
echo '备份文件...';backup();echo '备份完成。';
这是它的正常工作方式。然而,如果发生错误,脚本将自动终止,并在输出端显示异常文本。重要的是,它不会继续执行代码,我们知道不会发生数据损坏。
如果我们想继续执行,我们需要**清除错误,我们通过使用 "try "和 "catch "结构来做到这一点。
echo '备份文件...';try {backup();} catch (\Exception $e) {echo '备份失败。' . $e->getMessage();}echo '备份完成。';
如果一个异常被抛出,catch()
区域中的代码(如果符合其数据类型,则接受该异常)被调用,内部代码被执行。
我们总是得到一个异常类的实例,例如,它可以用来显示错误信息,这由getMessage()
方法处理。了解getFile()
方法也很有用,它返回包含错误的文件的磁盘路径,getCode()
返回错误状态代码,getLine()
返回抛出异常的行号。
除了基本的 Exception
异常之外,PHP还包括其他预定义的异常类型,适合于不同的使用情况。
数据类型 | 解释 |
---|---|
LogicException |
逻辑错误,在程序设计时可以预测的 |
BadFunctionCallException |
函数调用错误;未找到函数;不允许调用。 |
BadMethodCallException |
方法调用错误 |
InvalidArgumentException |
传递给函数或方法的错误(无效)参数 |
OutOfRangeException |
数组或集合的值超出了范围 |
LengthException |
值超过了允许的长度 |
DomainException |
值不在所需的域或范围内 |
RuntimeException |
只能在运行时检测到的错误(例如,未能写入文件) |
缓冲区或算术运算溢出,通常是由于处理的数据比预期的多而造成的。 | |
UnderflowException |
缓冲区或算术运算的下溢,传递的数据比预期的少 |
OutOfBoundsException |
索引超出了数组或集合的范围 |
RangeException |
数值不在要求的范围内 |
UnexpectedValueException |
意外的值(如函数的返回值) |
应通过适当的程序设计来防止 "LogicException "和 "RuntimeException "异常。就我个人而言,我只在特殊情况下使用它们,例如无法写入文件和与外部服务通信。
我建议完全不捕捉RuntimeException
,让应用程序失败。这通常是一个严重的问题,应尽快报告。
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | zh