应该直接重新抛出捕获的异常,还是应该绕过新的异常?
也就是说,我应该这样做:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
} catch (Exception $e) {
throw $e;
}
Run Code Online (Sandbox Code Playgroud)
或这个:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
} catch (Exception $e) {
throw new Exception("Exception Message", 1, $e);
}
Run Code Online (Sandbox Code Playgroud)
如果您的答案是直接抛出,请建议使用异常链接,我无法理解我们使用异常链接的真实场景.
Jon*_*Jon 270
除非你打算做一些有意义的事情,否则你不应该捕获异常.
"有意义的东西"可能是其中之一:
最明显的有意义的操作是处理异常,例如通过显示错误消息并中止操作:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
Run Code Online (Sandbox Code Playgroud)
有时您不知道如何在特定上下文中正确处理异常; 也许你缺乏关于"大图"的信息,但你确实希望将故障记录在尽可能接近发生点的位置.在这种情况下,您可能想要捕获,记录和重新抛出:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
Run Code Online (Sandbox Code Playgroud)
一个相关的场景是您在正确的位置为失败的操作执行一些清理,而不是决定如何在顶层处理失败.在早期的PHP版本中,这将实现为
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}
Run Code Online (Sandbox Code Playgroud)
PHP 5.5引入了finally
关键字,因此对于清理方案,现在有另一种方法来解决这个问题.如果清理代码无论发生什么都需要运行(即错误和成功),现在可以在透明地允许任何抛出的异常传播时执行此操作:
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
Run Code Online (Sandbox Code Playgroud)
第三种情况是,您希望在更大的保护伞下对许多可能的故障进行逻辑分组.逻辑分组的示例:
class ComponentInitException extends Exception {
// public constructors etc as in Exception
}
class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您不希望用户Component
知道它是使用数据库连接实现的(可能您希望保持选项打开并在将来使用基于文件的存储).因此,您的规范Component
会说"在初始化失败的情况下,ComponentInitException
将被抛出".这允许消费者Component
捕获预期类型的异常,同时还允许调试代码访问所有(依赖于实现的)细节.
最后,在某些情况下,您可能希望为异常提供更多上下文.在这种情况下,将异常包装在另一个异常中是有意义的,该异常包含有关错误发生时您尝试执行的操作的更多信息.例如:
class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw
// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();
// this may throw if the directory cannot be created
$copier->createTargetDirectory();
// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这种情况类似于上面的情况(可能不是最好的例子),但它说明了提供更多上下文的重点:如果抛出异常,它会告诉我们文件复制失败.但为什么会失败?此信息在包装的异常中提供(如果示例更复杂,则可能有多个级别).
如果您考虑一个场景,例如创建一个UserProfile
对象导致文件被复制,因为用户配置文件存储在文件中并且它支持事务语义:您可以"撤消"更改,因为它们仅在您提交之前的个人资料副本.
在这种情况下,如果你这样做了
try {
$profile = UserProfile::getInstance();
}
Run Code Online (Sandbox Code Playgroud)
并且因此捕获了"无法创建目标目录"异常错误,您将有权被混淆.在提供上下文的其他异常层中包含此"核心"异常将使错误更容易处理("创建配置文件复制失败" - >"文件复制操作失败" - >"无法创建目标目录").
irc*_*ell 34
嗯,这都是关于维护抽象的.所以我建议使用异常链接直接抛出.至于原因,让我解释泄漏抽象的概念
假设您正在构建模型.该模型应该从应用程序的其余部分抽象出所有数据持久性和验证.那么现在当你收到数据库错误时会发生什么?如果你重新抛出DatabaseQueryException
,你就会泄露抽象.要理解为什么,请考虑一下抽象.您不关心模型如何存储数据,只关心它.同样,你并不关心模型底层系统出了什么问题,只是你知道出了什么问题,大概出了什么问题.
因此,通过重新抛出DatabaseQueryException,您将泄露抽象并要求调用代码理解模型下正在发生的事情的语义.相反,创建一个泛型ModelStorageException
,并将捕获的DatabaseQueryException
内部包装起来.这样,您的调用代码仍然可以尝试在语义上处理错误,但是模型的基础技术并不重要,因为您只是暴露了该抽象层的错误.更好的是,因为你包装了异常,如果它一直冒泡并且需要记录,你可以追踪抛出的根异常(走链),这样你仍然可以获得所需的所有调试信息!
除非您需要进行一些后处理,否则不要简单地捕获并重新抛出相同的异常.但是像一块一样} catch (Exception $e) { throw $e; }
没有意义.但是你可以重新包装异常以获得一些重要的抽象增益.
恕我直言,抓住一个例外只是重新抛出它是没用的.在这种情况下,只是不要捕获它,让之前调用的方法处理它(也就是调用堆栈中'upper'的方法).
如果你重新抛出它,将捕获的异常链接到你将抛出的新异常绝对是一个好习惯,因为它将保留捕获的异常包含的信息.但是,重新抛出它只有在添加一些信息或处理捕获的异常时才有用,可能是某些上下文,值,日志记录,释放资源等等.
添加一些信息的方式是扩展Exception
类,有像例外NullParameterException
,DatabaseException
更结束了,等等,这让developper只赶上一些例外,他可以处理.例如,一个人只能捕获DatabaseException
并尝试解决导致该问题的原因Exception
,例如重新连接到数据库.