使用MVC时处理PHP中的错误


12

我最近一直在使用Codeigniter,但令我不安的是处理错误并将错误显示给用户。我从来没有擅长处理错误,而不会变得凌乱。我主要关心的是何时将错误返回给用户。

使用异常和抛出/捕获异常,而不是从函数返回0或1,然后使用if / else处理错误,是一种好习惯吗?因此,使得更容易通知用户该问题。

我倾向于避开例外。几年前,我在大学的Java导师告诉我:“不应在生产代码中使用异常,它应该更多地用于调试”。我感到他在撒谎。

但是,举例来说,我有将用户添加到数据库中的代码。在此过程中,可能有不止一件事情出错,例如数据库问题,重复条目,服务器问题等。注册期间发生问题时,用户需要了解它。

记住我正在使用MVC框架,是处理PHP错误的最佳方法是什么。

Answers:


14

使用异常和抛出/捕获异常,而不是从函数返回0或1,然后使用if / else处理错误,是一种好习惯吗?因此,使得更容易通知用户该问题。

不不不!

不要混淆异常和错误。例外是例外。错误不是。当您要求用户输入某种产品的数量,而用户输入“ hello”时,这是一个错误。也不例外:看到用户的无效输入并没有什么例外。为什么在非异常情况下不能使用异常,例如在验证输入时?其他人已经对此进行了解释,并显示了输入验证的有效替代方法。

这也意味着用户不在乎您的例外,并且显示例外既不友好,也是危险的。例如,在执行SQL查询期间的异常通常会揭示查询本身。您确定要冒险向所有人显示这样的消息吗?

超过1件事可能会出错,例如数据库问题,重复条目,服务器问题等。在注册过程中发生问题时,用户需要了解它。

错误。作为用户,我不需要知道您的数据库问题,重复的条目等。我真的不在乎您的问题。需要知道的是,我进入已经存在的用户名。如前所述,我的错误输入必须触发错误,而不是异常。

如何输出这些错误?这取决于上下文。对于一个已经使用过的用户名,我想看到一个小的红色标记出现在用户名附近,甚至在提交表单之前,说用户名已被使用。如果没有JavaScript,则提交后必须出现相同的标志。

启用AJAX的错误的示例

对于其他错误,您将显示整页并显示错误,或者选择另一种方式通知用户出现问题(例如,将出现一条消息,然后在页面顶部消失)。然后,这个问题更多地与用户体验有关,而不是与编程有关。

从程序员的角度来看,根据错误的类型,您将以不同的方式传播它。例如,在已经使用用户名的情况下,AJAX请求http://example.com/?ajax=1&user-exists=John将返回一个JSON对象,指示:

  • 用户已经存在,
  • 向用户显示的错误消息。

第二点很重要:要确保在禁用JavaScript的情况下提交表单以及在启用JavaScript的情况下键入重复的用户名,都必须确保出现相同的消息。您不想在服务器端源代码和JavaScript中复制错误消息的文本!

这实际上是Stack Exhange网站使用的技术。例如,如果我尝试提高自己的答案,则AJAX响应包含要显示的错误:

{"Success":false,"Warning":false,"NewScore":0,"Message":"You can't vote for your own post.",
"Refresh":false}

您还可以选择其他方法,并在填写表单之前在HTML页面中预设错误。优点:您不必在AJAX响应中发送错误消息。缺点:可访问性如何?尝试在没有CSS的情况下浏览页面,您会看到所有可能的错误出现。


感谢您的回应。这正是我正在努力的目标。您是否有任何资源可以报告错误,尤其是在用户体验方面?
詹姆斯·杰弗里

嗯,就像我说的那样,这实际上取决于错误,并且向用户报告错误与用户界面紧密相关。我还重点介绍了报告错误的两种主要方法:紧密集成(在输入附近带有AJAX的红色标记,错误值)和整页错误(友好程度较低),用于更严重的情况。这不是回答您的问题吗?
Arseni Mourzenko

2
+1是更多的用户体验问题,而不是技术问题
Charles Sprayberry 2012年

2
废话,MainMa。废话。错误代码是80年代和90年代。异常是处理特殊情况(例如输入错误(例如,ValidationException))的一种更为干净的方法。您没有被迫向用户显示所有异常。我看到了你的更好的答案。
猎鹰

2
并且以防万一您不知道:您可以控制要向用户呈现的异常以及不希望呈现的异常。因此,这根本不是一个论点。
猎鹰2012年

13

使用异常和抛出/捕获异常,而不是从函数返回0或1,然后使用if / else处理错误,是一种好习惯吗?因此,使得更容易通知用户该问题。

对对对!

如果您想拥有干净的代码,则几乎应该只使用异常,而不必理会错误代码。错误代码是没有意义的。它们几乎总是与一些不会透露太多信息的数值常数联系在一起。它会使您的代码无法读取,并且使与错误一起传播数据变得困难。

但是,异常是类,可以包含您喜欢的任何信息。因此,用户在数字字段中输入了错误的输入,例如“ abc”。有了错误代码,如果没有很多冒泡,您将无法将该信息传播到错误的处理程序。某些例外情况是免费提供的。此外,异常使您可以在函数和方法中获得有意义的返回值,同时仍然可以优雅地进行失败。更好的是,异常会直接传播到您要处理它们的地方!想象一下,将具有有意义的数据的错误代码传播到上一层或两层的处理程序时,将需要多少意大利面条代码。

而且,异常在语义上的表达远比错误代码所表达的要多。错误代码导致意大利面条代码,而异常处理导致干净代码。

而且,很容易忘记检查状态码。在Java之类的语言中,您不得不处理异常(例如C#遗漏的异常)。

记住我正在使用MVC框架,是处理PHP错误的最佳方法是什么。

使用异常并在控制器中处理它们。


我非常同意你的看法!在PHP中,异常的强制执行程度不如其他语言强,因此很高兴知道很多人正在将它们纳入其中……
David Conde

6
我同意大多数情况下错误代码是毫无意义的。但是,抛出Willy Nilly异常非常糟糕!例外情况仅在特殊情况下保留。异常导致程序流无法预测,可能使代码难以遵循(并因此而难以维护),并且在PHP中,与IF / THEN / ELSE相比,它们带来了相当可观的性能损失。我倾向于选择一些方法,如果成功则返回true,失败则返回false,并且只抛出异常错误的异常。
GordonM '02

6

考虑这个方便的小类:

class FunkyFile {               

    private $path;
    private $contents = null;

    public function __construct($path) { 
        $this->setPath($path); 
    }

    private function setPath($path) {
        if( !is_file($path) || !is_readable($path) ) 
            throw new \InvalidArgumentException("Hm, that's not a valid file!");

        $this->path = realpath($path);
        return $this; 
    }

    public function getContents() {
        if( is_null($this->contents) ) {
            $this->contents = @file_get_contents( $this->path );
            if($this->contents === false) 
                throw new \Exception("Hm, I can't read the file, for some reason!");                                 
        }

        return $this->contents;            
    }

}

这是对异常的完美使用。从FunkyFile's角度来看,如果路径无效或file_get_contents失败,则绝对无法采取任何措施来纠正这种情况。真正的例外情况;)

但是,让用户知道您在代码中的某个地方偶然发现了错误的文件路径,是否有任何价值?例如:

class Welcome extends Controller {

    public function index() {

        /**
         * Ah, let's show user this file she asked for
         */                 
        try {
            $file = new File("HelloWorld.txt");
            $contents = $file->getContents();   
            echo $contents;
        } catch(\Exception $e) {
            log($e->getMessage());

            echo "Sorry, I'm having a bad day!"; 
        }                           
    }        
}

除了告诉别人您今天过得很糟糕之外,您还可以选择:

  1. 倒退

    您还有另一种获取信息的方式吗?在上面的简单示例中,这似乎不太可能,但是请考虑主/从数据库模式。主机可能没有响应,但也许,也许,从机仍然在那里(反之亦然)。

  2. 是用户的错吗?

    用户是否提交了错误的输入?好吧,告诉她。您可以吠叫错误消息,也可以友好地将错误消息与表单一起使用,以便她可以键入正确的路径。

  3. 是你的错吗

    对您而言,我的意思是不是用户的任何东西,范围从您输入错误的文件路径到服务器中出现问题。严格来说,是时候出现503 HTTP错误了,因为该服务不可用。CI具有show_404()功能,可以轻松构建show_503()

忠告,您应该考虑流氓异常。CodeIgniter是一段凌乱的代码,您永远不知道何时会弹出异常。同样,您可能会忘记自己的异常,最安全的选择是实现一个捕获所有异常处理程序。在PHP中,您可以使用set_exception_handler来做到这一点:

function FunkyExceptionHandler($exception) {
    if(ENVIRONMENT == "production") {
        log($e->getMessage());
        show_503();
    } else {
        echo "Uncaught exception: " , $exception->getMessage(), "\n";
    }   
}

set_exception_handler("FunkyExceptionHandler");

您还可以通过set_error_handler处理流氓错误。您可以编写与异常相同的处理程序,或者将所有错误都转换为ErrorException并让您的异常处理程序处理它们:

function FunkyErrorHandler($errno, $errstr, $errfile, $errline) {
    // will be caught by FunkyExceptionHandler if not handled
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler("FunkyErrorHandler");

这真是内容丰富,欢呼!
詹姆斯,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.