Обработка ошибок в PHP при использовании MVC

12

В последнее время я много использую Codeigniter, но одна вещь, которая действует мне на нервы, - это обработка ошибок и их отображение пользователю. Я никогда не умел обрабатывать ошибки, пока они не запутались. Моя главная проблема заключается в возврате ошибок пользователю.

Это хорошая практика - использовать исключения и генерировать / перехватывать исключения, а не возвращать 0 или 1 из функций, а затем использовать if / else для обработки ошибок. Таким образом, упростить информирование пользователя о проблеме.

Я склонен уходить от исключений. Мой преподаватель Java в университете несколько лет назад сказал мне, что "исключения не должны использоваться в производственном коде, это больше для отладки". Я чувствую, что он лгал.

Но, например, у меня есть код, который добавляет пользователя в базу данных. Во время процесса может произойти сбой более чем одной вещи, такой как проблема с базой данных, дублирующаяся запись, проблема с сервером и т. Д. Когда проблема возникает во время регистрации, пользователь должен знать об этом.

Как лучше всего обрабатывать ошибки в PHP, имея в виду, что я использую инфраструктуру MVC.

Джеймс Джеффри
источник

Ответы:

14

Это хорошая практика - использовать исключения и генерировать / перехватывать исключения, а не возвращать 0 или 1 из функций, а затем использовать if / else для обработки ошибок. Таким образом, упростить информирование пользователя о проблеме.

Нет нет нет!

Не смешивайте исключения и ошибки. Исключения, ну, исключительные. Ошибок нет. Когда вы просите пользователя ввести количество товара, и пользователь вводит «привет», это ошибка. Это не исключение: нет ничего исключительного в том, что пользователь видит неверные данные. Почему вы не можете использовать исключения в неисключительных случаях, например, при проверке ввода? Другие люди уже объяснили это и показали верную альтернативу для проверки входных данных.

Это также означает, что пользователь не заботится о ваших исключениях , и показ исключений является и недружественным, и опасным . Например, исключение во время выполнения запроса SQL часто раскрывает сам запрос. Вы уверены, что хотите рискнуть показать такое сообщение всем?

более чем одна вещь может пойти не так, как проблема с базой данных, дубликат записи, проблема с сервером и т. д. Когда проблема возникает во время регистрации, пользователь должен знать об этом.

Неправильно. Как пользователь, мне не нужно знать о проблемах с вашей базой данных, повторяющихся записях и т. Д. Меня действительно не волнуют ваши проблемы. Что мне делать нужно знать, что я вошел в имя пользователя , которое уже существует. Как уже говорилось, неправильный ввод от меня должен вызвать ошибку, а не исключение.

Как вывести эти ошибки? Это зависит от контекста. Для уже используемого имени пользователя, я хотел бы видеть маленький красный флажок рядом с именем пользователя, прежде чем даже отправлять форму, говоря, что имя пользователя уже используется. Без 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 рядом с вводом с неправильным значением) и полностраничные ошибки, гораздо менее удобные, используемые для более серьезных случаев. Разве это не отвечает на ваш вопрос?
Арсений Мурзенко
2
+1 за то, что это скорее проблема пользовательского опыта, а не техническая
Чарльз Спрейберри
2
Херня, МайнМа. Просто фигня. Коды ошибок так 80-х и 90-х годов. Исключения - гораздо более чистый способ обработки особых обстоятельств, таких как неправильный ввод (например, ValidationException). Вы не обязаны отображать каждое исключение для пользователя. Я видел твои лучшие ответы.
Сокол
2
И на тот случай, если вы этого не знали: вы можете контролировать, какие исключения вы хотите представить пользователю, а какие нет. Так что это совсем не аргумент.
Сокол
13

Это хорошая практика - использовать исключения и генерировать / перехватывать исключения, а не возвращать 0 или 1 из функций, а затем использовать if / else для обработки ошибок. Таким образом, упростить информирование пользователя о проблеме.

Да, да, да!

Если вы хотите иметь чистый код, вы должны почти исключительно использовать исключения и не беспокоиться об использовании кодов ошибок. Коды ошибок не имеют смысла. Они почти всегда привязаны к некоторой числовой константе, которая не дает много информации. Это может сделать ваш код нечитаемым, и им будет сложно распространять данные вместе с ошибкой.

Исключения, однако, являются классами и могут содержать любую информацию, которая вам нравится. Таким образом, пользователь ввел неправильный ввод, например «abc» для числового поля. С кодом ошибки вы не сможете распространить эту информацию в обработчик ошибки без большого количества пузырей. То, что исключения предоставляют бесплатно. Кроме того, исключения позволяют вам иметь значимые возвращаемые значения в функциях и методах, в то же время сохраняя элегантный способ сбоя. Более того, исключения распространяются прямо туда, где вы хотите их обработать! Представьте, какой объем спагетти-кода вам понадобится для передачи кода ошибки со значимыми данными в обработчик на один или два уровня выше.

Кроме того, исключения выражают гораздо более семантически, чем коды ошибок. Коды ошибок приводят к коду спагетти, где обработка исключений приводит к чистому коду.

Кроме того, легко забыть проверить коды состояния. В таких языках, как Java, вы вынуждены обрабатывать исключения (то, что, например, отсутствует в C #).

Как лучше всего обрабатывать ошибки в PHP, имея в виду, что я использую инфраструктуру MVC.

Используйте исключения и обрабатывайте их в своих контроллерах.

сокол
источник
Я очень согласен с вами! Несмотря на то, что исключения в PHP не такие строгие, как в других языках, полезно знать, что многие их используют ...
Дэвид Конде,
6
Я согласен, что в большинстве случаев коды ошибок довольно бессмысленны. Однако бросать исключения Вилли Нилли крайне плохо! Исключения должны быть зарезервированы только для исключительных обстоятельств. Исключения вызывают непредсказуемый поток программ, могут усложнить выполнение кода (и, следовательно, поддерживать его), и в PHP они несут довольно существенное снижение производительности по сравнению с IF / THEN / ELSE. Я предпочитаю, чтобы методы возвращали истину в случае успеха, ложь в случае неудачи, и только исключение чего-то идет вопиюще неправильно.
GordonM
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. Отступать

    У вас есть другой способ получения информации? В моем простом примере, приведенном выше, это маловероятно, но рассмотрим схему базы данных master / slave. Хозяин, возможно, не смог ответить, но, может быть, просто может быть, раб все еще там (или наоборот).

  2. Это вина пользователя?

    Пользователь отправил неверный ввод? Ну, расскажи ей об этом. Вы можете или лаять сообщение об ошибке, или быть хорошим и сопровождать это сообщение об ошибке формой, чтобы она могла ввести правильный путь.

  3. Это твоя вина?

    Под вами я подразумеваю все, что не является пользователем, так что вы можете ввести неправильный путь к файлу или что-то не так на вашем сервере. Строго говоря, пришло время для ошибки HTTP 503 , так как служба недоступна. CI имеет show_404()функцию, которую вы можете легко построить show_503().

Слово совет, вы должны принять во внимание мошеннические исключения. CodeIgniter - это грязный кусок кода, и вы никогда не знаете, когда появится исключение. Точно так же вы можете забыть о своих собственных исключениях, и самый безопасный вариант - реализовать обработчик исключений catch all. В 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");
Яннис
источник
Это было действительно информативно, ура!
Джеймс