Почему и как бы вы использовали исключения в этом примере PHP-кода? - PullRequest
23 голосов
/ 01 июня 2009

Мне было интересно, зачем мне использовать исключения в моем PHP. Давайте рассмотрим простой пример:

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);
 }
}

Зачем мне использовать исключения в вышеуказанном случае? У меня такое ощущение, что исключения помогают вам распознать тип возникшей проблемы, правда?

Итак, правильно ли я использую исключения в этом коде:

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"
}

Или я здесь полностью потерялся?

Ответы [ 4 ]

58 голосов
/ 01 июня 2009

Когда я должен использовать исключение?

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

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

Как создать пользовательские исключения?

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

class DatabaseException extends Exception {}

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

Когда не должен использовать исключение?

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

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

Зачем использовать исключения вместо возврата специальных кодов ошибок и т. Д.?

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

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

4 голосов
/ 01 июня 2009

Вы никоим образом не можете предположить, что файл существует только потому, что вы позвонили 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 вообще:)

4 голосов
/ 01 июня 2009

Я не программист PHP, но это похоже на C #.

Обычно вы хотите выбросить ошибки, если это точка возврата. Тогда вы сможете что-то зарегистрировать, чтобы показать, что случилось невозможное.

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

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

Я бы сказал, что это хорошая практика проектирования, позволяющая избегать подхвата всех исключений "catch (Exception $ e)" и проектирования по контракту, как вы это делаете в предыдущем примере. Сначала я хотел бы более конкретно указать тип создаваемого исключения, а затем работать при необходимости. В противном случае держитесь подальше от try-> catch и исключения.

1 голос
/ 01 июня 2009

Просто примечание, ваш код для файлового выходного файла неверен, так как 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);  
 }
}

Тогда вызывающий код в основном такой же.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...