Лучше всего попробовать - поймать весь мой PHP-код или быть как можно более конкретным?

В моем проекте не так много исключений.
Прямо сейчас (мы используем MVC) у меня есть try catch, охватывающий весь мой код:

try{
   fronController::dispatch($somthing...);
}catch(Exception $E){
  //handle errors
}

Интересно, есть ли веская причина использовать блок try-catch как можно более конкретным способом, чем я могу или просто сохранить его вообще, как сейчас?

Ответы

Ответ 1

обычно локально локально, ловить глобально, если обработчик исключений не является специфичным для функции, в этом случае дескриптор локально.

 class fooException extends Exception{}

 // DB CLASS

 public function Open(){
    // open DB connection
    ...
    if ($this->Conn->connect_errno) 
      throw new fooException("Could not connect: " . $this->Conn->connect_error);
  }

 // MAIN CLASS

 public final function Main(){
    try{
      // do stuff
    }
    catch(fooException $ex){
       //handle fooExceptions
    }
 }

Ответ 2

Идея исключения заключается в том, что функция может сообщать об ошибке, не возвращая специальные значения. В старом PHP, единственный способ, которым функция могла сказать, что проблема была связана с возвратом некоторого специального значения, такого как false или -1. Это не нравится. Например, предположим, что я пишу вариант file_get_contents().

Типичное возвращаемое значение - это дескриптор, представляемый положительным целым числом. Однако есть две основные проблемы, с которыми я могу столкнуться: указанный вами файл не найден, или указанный вами файл не читается. Чтобы указать на ошибку, я мог бы вернуть отрицательное число - потому что дескрипторы положительны - это связано с конкретной причиной ошибки. Скажем, что -1 означает, что файла там нет, а -2 означает, что файл не читается.

Теперь у нас есть проблема, что -1 и -2 не имеют в виду ничего для кого-то, кто читает код. Чтобы исправить это, мы вводим глобальные константы FILE_NOT_FOUND и FILE_NOT_READABLE. Посмотрим на какой-то результирующий код.

<?php

define('FILE_NOT_FOUND', -1);
define('FILE_NOT_READABLE', -2);

function my_file_get_contents($file) {
    // blah blah blah
}

$friendListFile = getDefaultFriendListFile();

$result = my_file_get_contents($friendListFile);

if ($result == FILE_NOT_FOUND) {
    deleteFriendListFromMenu();
} elseif ($result == FILE_NOT_READABLE) {
    alertUserAboutPermissionProblem();
} else {
    useFriendList($result);
}

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

$result - это ужасное имя переменной. Имена переменных должны быть описательными и очевидными, например $friendListFile. Реальное имя для $result - это $fileContentsOrErrorCode, которое не только слишком длинное, но и показывает, как мы перегружаем одну переменную двумя значениями. Вы никогда, никогда, не хотите, чтобы одни и те же данные означали две вещи. Нам нужны отдельные $errorCode и $fileContents!

Итак, как нам обойти эту проблему? Одно не-действительно-решение, используемое некоторыми библиотеками PHP, состоит в том, чтобы вернуть их my_file_get_contents() -подобные функции false, если они столкнулись с проблемой. Чтобы устранить проблему, на самом деле, мы называем my_file_get_contents_getError(). Это почти работает.

define('FILE_OKAY', 0);
define('FILE_NOT_FOUND', -1);
define('FILE_NOT_READABLE', -2);

$my_file_get_contents_error = FILE_OKAY;

function my_file_get_contents_getError() {
    // blah blah blah
}

function my_file_get_contents($file) {
    global $my_file_get_contents_error;
    // blah blah blah
    // whoa, an error? return false and store the error code in
    // $my_file_get_contents_error
    // no error? set $my_file_get_contents_error to FILE_OKAY
}

$friendListFile = getDefaultFriendListFile();

$result = my_file_get_contents($friendListFile);

if (my_file_get_contents_getError() == FILE_NOT_FOUND) {
    deleteFriendListFromMenu();
} elseif (my_file_get_contents_getError() == FILE_NOT_READABLE) {
    alertUserAboutPermissionProblem();
} elseif (my_file_get_contents_getError() == FILE_OKAY) {
    useFriendList($result);
} else {
    die('I have no idea what happened. my_file_get_contents_getError() returns '
        . my_file_get_contents_getError()
    );
}

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

Мы все еще не можем назвать $result чем-нибудь лучше, чем $fileContentsOrFalseIfError. Эта проблема не была исправлена.

Теперь я исправил одну проблему, которую вы, возможно, заметили в предыдущем примере. Что делать, если мы не покрываем все коды ошибок? Если программист решает, что должен быть код -3, который мы изначально не обнаружили! Мы могли бы проверить, была ли $result строка, чтобы убедиться, что это не код ошибки, но мы не должны действительно заботиться о типах в PHP, правильно? Теперь, когда мы можем использовать второе возвращаемое значение из my_file_get_contents_getError(), нет никакого смысла включать код успеха.

Появилась новая проблема, которая возникла. Исправить один и найти еще три? Новая проблема заключается в том, что можно сохранить только самый последний код ошибки. Это ужасно хрупко! Если что-то еще вызывает my_file_get_contents(), прежде чем вы обратитесь к вашему коду ошибки, их код перезапишет ваш!

Gah, теперь нам нужно сохранить список функций, которые небезопасно для вызова, прежде чем вы обратитесь к возвращаемому значению из my_file_get_contents_getError(). Если вы этого не сделаете, вы должны придерживаться условного соглашения о кодировании, которое вы всегда вызываете my_file_get_contents_getError() сразу после my_file_get_contents(), чтобы сохранить код ошибки, который принадлежит вам, прежде чем он будет таинственным образом перезаписан.

Подождите! Почему бы нам просто не раздавать идентификаторы нашим абонентам? Чтобы использовать my_file_get_contents(), вам нужно задать create_my_file_get_contents_handle() для некоторого числа, которое будет рассогласовывать вас со всеми другими вызывающими. Теперь вы можете вызвать my_file_get_contents($myHandle, $myFile), а код ошибки можно сохранить в специальном месте только для вас. Теперь, когда вы вызываете my_file_get_contents_getError($myHandle), вы можете получить доступ к этому специальному месту, получить код ошибки, и никто не наступил на ваши пальцы.

Er, но если есть много звонящих, мы не хотим, чтобы вокруг нас были блокированы бесполезные коды ошибок. Мы попросили пользователей позвонить destroy_my_file_get_contents_handle($myHandle), когда они будут сделаны, чтобы мы могли освободить некоторую память.

Надеюсь, это все очень хорошо знакомы вам в старых PHP-мантрах.

Это все так безумно, просто сделайте это простым, пожалуйста!

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

Введите исключения!

<?php

class FileNotFoundException extends Exception {}
class FileNotReadableException extends Exception {}

function my_file_get_contents($file) {
    if (!is_file($file)) {
        throw new FileNotFoundException($file);
    } elseif (!is_readable($file)) {
        throw new FileNotReadableException($file);
    } else {
        // blah blah blah
    }
}

$friendListFile = getDefaultFriendListFile();

try {
    $fileContents = my_file_get_contents($friendListFile);
    useFriendList($fileContents);
} catch (FileNotFoundException $e) {
    deleteFriendListFromMenu();
} catch (FileNotReadableException $e) {
    alertUserAboutPermissionProblem();
}

Внезапно наши старые головные боли с особыми возвращаемыми значениями, ручками и условными обозначениями были излечены!

Теперь мы действительно можем переименовать $result в $fileContents. Если my_file_get_contents() имеет проблему, назначение прерывается полностью, и мы переходим к соответствующему блоку catch. Только если нет ошибки, мы даже думаем о предоставлении $fileContents значения или вызова useFriendList().

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

Нет проблем с памятью! Сборщик мусора с удовольствием очистит ненужные объекты исключений, не думая об этом. Используя устаревшую систему ручек, мы должны были помнить о том, чтобы вручную уничтожить ручку, чтобы она не скрывалась навсегда в памяти.

Есть много других преимуществ и черт для исключений. Я настоятельно рекомендую посмотреть на другие источники, чтобы узнать об этом. Особенно интересны то, как они пузыряют стек исполнения, пока какой-то вызывающий может их поймать. Также интересно, как вы можете поймать исключение, попытаться исправить эту проблему, а затем перестроить исключение, если не можете. Не забывайте, что исключения - это объекты! Этому можно добиться большой гибкости. Для исключений, которые никто не может уловить, посмотрите на обработчик исключений.

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

Ответ 3

Вы должны быть как можно более конкретными с ошибками ловушки в вашем коде. Устранение определенных ошибок должным образом повышает удобство обслуживания кода, делает ваш код структурированным и организованным.

Это также хорошая практика в качестве конвенции, особенно если вы позже работаете над проектами на основе команд, и вы не единственный, кто смотрит на код.

Лично бросая все в один блок try catch, кажется, запах кода.

Ответ 4

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

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

Поэтому, отвечая на ваш вопрос, лучше быть как можно более конкретным, поскольку каждое исключение должно обрабатываться только в том случае, если вы знаете, что с ним делать (молчание - это плохая идея). Если не просто позволить исключению уведомить пользователя о том, что что-то пошло не так. Или, если вы хотите, поймайте исключение, чтобы зарегистрировать ошибку и восстановить ее.

Хорошее обсуждение здесь для С++, но применяются общие понятия. Я нашел java учебники по исключениям тоже очень хорошо.

Ответ 5

Если вы используете блок try для всего своего кода, вы также можете определить обработчик исключений по умолчанию (см. документы).

Кроме того, размер блока try зависит от вас, это зависит от того, насколько вы хотите, чтобы ваша обработка ошибок была. Если вы не можете восстановить какое-либо из исключений, на самом деле нет причин быть конкретным, если вы не хотите регистрировать сообщения об ошибках, которые являются конкретными (но сообщение об исключении и трассировке стека, вероятно, будет достаточно).

Ответ 6

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

Быть конкретным и охватывать все ваши базы, где необходима обратная связь, и возможно восстановление.

Ответ 7

Для разных ошибок могут потребоваться разные ответы.

Вы не выпрыгнете из самолета в ответ на все возможные проблемы, которые могут возникнуть. Не могли бы вы?

Хорошо, это то, что делает ваше приложение.

Бывают ситуации, когда исключение можно поймать, и приложение может продолжать работать. Скорее всего, приложение, возможно, придется реагировать на один и тот же класс исключений по-разному в разных ситуациях. Возможно, в одной функции исключение ввода-вывода не вредно, а в другом.

Ответ 8

Будьте конкретны и соответствующим образом обрабатывайте определенные ошибки.