我一直想知道为什么我会在PHP中使用Exceptions。我们来看一个简单的例子:
class Worker
{
public function goToWork()
{
return $isInThatMood ?
// Okay, I'll do it.
true :
// In your dreams...
false;
}
}
$worker = new Worker;
if (!$worker->goToWork())
{
if (date('l',time()) == 'Sunday')
echo "Fine, you don't have to work on Sundays...";
else
echo "Get your a** back to work!";
}
else
echo "Good.";
我有理由对这种代码使用例外吗?为什么?如何构建代码?
那些可能产生错误的代码呢?
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
return false;
return file_get_contents($file);
}
}
为什么我会在上述情况下使用例外?我有一种感觉,Exceptions可以帮助你识别出现的问题的类型,是吗?
那么,我是否在此代码中正确使用了例外:
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
return throw new Exception("File not found.",123);
try
{
$contents = file_get_contents($file);
}
catch (Exception $e)
{
return $e;
}
return $contents;
}
}
还是那么穷?现在底层代码可以执行此操作:
$fo = new FileOutputter;
try
{
$fo->outputFile("File.extension");
}
catch (Exception $e)
{
// Something happened, we could either display the error/problem directly
echo $e->getMessage();
// Or use the info to make alternative execution flows
if ($e->getCode() == 123) // The one we specified earlier
// Do something else now, create "an exception"
}
还是我完全迷失在这里?
答案 0 :(得分:58)
您使用例外来表示例外条件;也就是说,某种方法阻止某个方法履行其合同,而且不应该在该级别发生。
例如,您可能有一个方法Record::save()
,它将记录的更改保存到数据库中。如果由于某种原因无法完成此操作(例如,发生数据库错误或数据约束被破坏),则可能会抛出异常以指示失败。
异常通常被命名为指示错误的性质,例如DatabaseException
。您可以将Exception
子类化为以这种方式创建自定义命名的异常,例如
class DatabaseException extends Exception {}
(当然,您可以利用继承为此异常提供一些其他诊断信息,例如连接详细信息或数据库错误代码。)
考虑另一个例子;检查文件是否存在的方法。如果文件不存在,这应该 not 抛出异常,因为该方法的目的是执行所述检查。但是,打开文件并执行某些处理的方法可以抛出异常,因为该文件应该存在,等等。
最初,并不总是清楚某些事情是否异常。像大多数事情一样,随着时间的推移,经验会告诉你何时应该而且不应该抛出异常。
关于异常的有用之处在于它们会立即跳出当前方法并抬起调用堆栈直到它们被捕获并处理,这意味着您可以将错误处理逻辑移到更高位置,尽管理想情况下,不要太高
通过使用清除机制来处理故障情况,当发生不良事件时,您会自动启动错误处理代码,这意味着您可以避免处理必须检查的各种魔术标记值,或者更糟糕的是,全局错误标志,以区分一堆不同的可能性。
答案 1 :(得分:4)
我不是PHP程序员,但这看起来与C#类似。
通常,如果它是一个不归路的点,你会想要抛出错误。然后你就可以记录一些事情,表明不可能发生了。
如果您可以告诉该文件不存在,那么您可以这么说。在我看来,没有真正需要抛出异常。
现在,如果找到了文件并且你正在处理它,并且只说上传了一半的文件,并且你无法在没有例外的情况下告诉它,那么就可以了解它。
我认为这是一个很好的设计实践,可以避免捕获所有异常“catch(Exception $ e)”并通过契约进行设计,就像您在前面的示例中所做的那样。我首先会更具体地说明抛出的异常类型,然后在需要时从那里开始工作。否则,请远离try-> catch和例外。
答案 2 :(得分:4)
您决不会因为调用file_exists()
而假设文件存在!函数file_exists
不会打开文件,因此可以随时删除或重命名或移动文件!
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
return false;
///<--- file may be deleted right here without you knowing it
return file_get_contents($file);//<-- then this will throw an error
//if you don't catch it, you're program may halt.
}
}
我相信这更好:
class FileOutputter
{
public function outputFile($file)
{
try{
if (!file_exists($file))
return false;
return file_get_contents($file);
}catch(Exception e){
check_what_went_wrong_and_go_to_plan_B();
}
}
}
(编辑:实际上尝试从头开始打开文件可能更好。如果你成功了,那么你对文件有一个“锁定”,它不会消失。如果没有,那么抓住异常,看看出了什么问题。
然后,你可能会觉得这种程度的恢复只是愚蠢的。在这种情况下,我认为您根本不需要担心try/catch
:)
答案 3 :(得分:1)
请注意,文件输出器的代码无效,因为file_get_contents($ file) 不会抛出异常。但是,如果文件不存在,或者由于某种原因无法访问,它将发出警告。另外,你应该从outputFile返回一个异常,当你应该让错误传播到调用堆栈时。
但是,您可以在php中注册错误处理程序,以便在发生错误时抛出异常:
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");
这样,任何导致错误的标准函数调用都会抛出异常。
所以,我会将FileOutputter更改为此(添加上面的代码段):
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
throw new Exception("File not found.",123);
return file_get_contents($file);
}
}
调用代码基本相同。
基本上,规则不是在发生错误时返回Exception - 如果需要可以捕获它,并抛出自定义异常,但让异常进入调用堆栈直到你想要使用它。