try… catch VS if…else. Что, когда и почему?

    Данная статья расчитана для двух типов разработчиков:
    — кто еще не знаком с исключениями
    — кто пытается найти более разумное использование исключениям

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

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

    Те, кто начинал знакомиться с PHP (тут и далее я буду подразумевать пятую версию), после другого процедурного языка программирования, так и не поняли что же такого в переходе от 4ки к 5ке и продолжают оформлять код привычными функциями, которые находятся на одном уровне по отношению друг к другу, а так же каждое действие проверяют на удачный код возврата. Но те кто знали об исключениях и классах…


    Теория


    Исключение (Exception) — сигнал отправляемый программой в интерпретатор, о возникновении нештатной (исключительной) ситуации во время исполнения кода.

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

    throw new Exception(\\\"This is exception message\\\", $exception_code);


    Для того, что бы отловить исключение, используется конструкция try...catch. В блоке try выполняются операции, которые могут привести к исключительной ситуации, а блок catch позволяет принять решение что делать, если исключение было брошено.
    try {
        throw new Exception(\\\"Exception message\\\");
        echo \\\"That code will never been executed\\\";
    } catch (Exception $e) {
        echo $e->getMessage(); //выведет \\\"Exception message\\\"
    }


    Как видно из примера, при выбрасывании исключения, остальной код в блоке try выполнен не будет, а управление будет передано в оператор catch, в котором мы указываем, как будет называться объект, в который будет передано выброшенное исключение (в нашем случае — $e). Внутри блока оператора catch, на основании данных из исключения мы можем применять какое-либо действие в зависимости от ситуации. Сейчас мы просто вывели сообщение, которое было передано исключением.

    Объект Exception (который кстати можно унаследовать) имеет следующий набор финальных (final) методов:
    final function getMessage(); // сообщение исключения
    final function getCode(); // код исключения
    final function getFile(); // файл из которого брошено исключение
    final function getLine(); // строка бросания
    final function getTrace(); // массив стека вызовов
    final function getTraceAsString(); // массив стека вызовов отформатированый в строку


    Практика


    Когда же удобно использовать исключения? Да всегда, когда функция или метод может прийти к ошибочной ситуации! На примере встроенной функции mysql_connect() и mysql_select_db(), если бы они бросала исключения при ошибке соединения и выборе базы данных соответственно, код выглядел бы так:
    try {
        mysql_connect($hostname, $username, $password);
        mysql_select_db($dbname);
    } catch (Exception $e) {
        echo $e->getMessage(); //выведет либо сообщение об ошибке подключения, либо об ошибке выбора
    }


    И в зависимости от результата мы бы:

    * удачно присоединились бы к СУБД и выбрали БД (код в сatch блоке не выполнился бы)
    * при неудаче соединения с СУБД, вывели бы соответствующую ошибку и прикатили выполнение сеанса
    * при ошибке выбора БД, оповестили бы пользователя об этой неприятности

    Теперь самое время спросить: \\\«А зачем нам такая сложная штука, если можно использовать оператор if?\\\».

    Первый же ответ: \\\«Потому что так код более читабельный, а обработка ошибок выведена в отдельный блок\\\». Если бы мы использовали оператор if, то код выглядел бы следующим образом:
    $connId = mysql_connect($hostname, $username, $password);
    if (false == $connId) {
        echo \\\"Ошибка подключения к СУБД\\\";
    }

    $flag = mysql_select_db($dbname);
    if (false == $flag) {
        echo \\\"Невозможно выбрать базу данных.\\\";
    }


    Согласитесь, что в предыдущем варианте код выглядит более понятным. Но это еще не все. Теперь обработкой этих ошибок заниматься непосредственно разработчику не обязательно — достаточно выбросить исключение, а его обработкой можно заняться на более высоком уровне. Так же исключения можно передавать цепочкой (chain) наверх:
    class MyException extends Exception {}

    try {
        try {
            //...
            throw new Exception(\\\"inner\\\");
            //...
        } catch (Exception $e) {
            throw new MyException(\\\"outer\\\");
        }
    } catch (MyException $e) {
        echo $e->getMessage(); //выведет \\\"outer\\\"
    }


    Производительность


    Пытаясь достичь истины, провел несколько экспериментов с различными типами функций.
    Первый тип возвращал статус true и проверялся операторами if...else
    Второй тип возвращал статус false и проверялся операторами if...else
    Третий тип просто выполнял действия и ничего не возвращал. Проверялся блоком try...catch
    Четвертый тип всегда бросал исключение и проверялся в try...catch

    Результаты:
    True: 0.72382092475891
    False: 0.85190796852112
    No exception: 0.72565317153931
    Exception: 14.176206827164


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

    Выводы


    Когда же использовать исключения? Всегда, когда подразумевается ошибка или нестандартное поведение программы, а так же когда принятие решения об обработке результата необходимо переложить на более высокий уровень.
    А что же делать с оператором if и булевыми статусами отработки функций? Оставить их. Но толко там где они действительно необходимы. Тоесть там, где логический оператор подразумевает использование результата в вычислениях, а не контроле потока выполнения. Тоесть все удачные завершения функций теперь не нужно оповещать оператором return true, если это логическое значение не пригодиться для дальнейших вычислений. И в то же время все статусы завершения функций с ошибками изменить из формата return false в формат throw new Exception()
    Что нужно помнить? Использование исключений предполагает что весь код выполняется со статусом true (без ошибок), но если ошибка произошла, то для ее обработки всегда найдется место в блоке catch.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 65

      +1
      Про исключения есть простое правило: нормальное поведение программы при нормальном поведении пользователя - 0 брошенных, 0 пойманных исключений. То есть если вы бросаете и отлавливаете исключения при том, что пользователь ничего криминального не сделал - вы переборщили с отказом от кодов возврата...

      С другой стороны преимущество исключений в том, что их невозможно проигнорировать - так что если ваша программа не должна нормально продолжать работу после какого-то действия - время бросать исключение... __attribute__ ((warn_unused_result)) в PHP нету...
        0
        Ну это как-то слишком строго, надо просто помнить, что они небесплатны (хотя к динамическим языкам это в меньшей степени относится).
          0
          это неправильное пониание исключений как парадигмы. Брошеных может быть хоть миллиард - это нормально. Хоть одно не пойманное именно и означает фатальную ошибку.
            0
            Это вполне правильное понимание. Парадигма как раз подразумевает, что исключения не должны использоваться для того, что называется "normal workflow".
              0
              1. исключение - это "some condition that changes the normal flow of execution.". Следовательно, любая ошибка в процессе реализации задачи (ну скажем хотели файлик прочитать с винта, а прав нет) является исключением.
              2. workflow != flow.
              3. "Status returns are inferior to exceptions. All modern programming systems provide tools for exception handling. Use them." В PHP везде уйма варнингов и нотисов лишь по той простой причине, что слишком дофига std библиотеке переписывать надо.

              Я причем не говорю что вообще нотисов и варнингов не должно быть. Должны быть, но те такие которые хочется ловить. Т.е. deprecation warning конечно никто швырять не будет.
                0
                в последнем абзаце - "которые НЕ хочется ловить". Ауч.
            0
            Вышеописанное подключение к базе может генерировать ошибку, когда сервер лежит. При этом и код верный и пользователь ничего плохого не делал.
            В Python, например, кругом и рядом исключения. При открытии файла - исключение если нет.
              0
              Если ваш сервер отвечает "не шмогла я, не шмогла" - тут исключениям самое место, но если вы пытаетесь открыть 100 файлов хотя из них 99 не откруются и бросят exception - вы чего-то перемудрили со структурой программы.
              0
              Не понимание парадигмы, согласен с mocksoul.
              Вот пример: пытаемся соединиться с сервером базы данных и в течении некоторого времени не получаем отклика (отваливаемся по таймауту) — кто тут виноват? Пользователь? Программа? — никто! Это нормальная работа приложения.
            • UFO just landed and posted this here
                +3
                давайте не будем говорить о Java как о родители ООП концепции :) потому как насколько знаю я, первым ООП языком была Simula ;)
                • UFO just landed and posted this here
                    0
                    Обоснуйте, почему именно джавовский, а не сиплюсплюсный например?
                    • UFO just landed and posted this here
                        0
                        Вообще механизм исключений с ОО никак не связан. Исключения работают на уровне методов и представляют собой конструкции позволяющие управлять потоком выполнения. К объектам и классам прямого отношения не имеют.
                        • UFO just landed and posted this here
                            0
                            Это разумеется так, но лишь потому что в ОО любая сущность представляется в виде объекта. А возможность создания исключений разных типов — просто способ их классификации.

                            Возьмите любую классическую книжку по ОО (того же Гради Буча, например). Там нет ни слова про ислючения. Потому что они не имеют отношения к этой парадигме. Так же как и другие управляющие конструкции (if, for, switch и т.п.), а исключения как-раз из их числа.
                            • UFO just landed and posted this here
                              0
                              нет, никак не завязано. Исключения - это просто способ отматывать стеки обратно в поиске кода, который исключение поймает. В некоторых языках можно даже продолжить выполнение программы в том месте, где остановились, и это все при том что как правило исключения ловятся совсем в другом месте.

                              Вон, тот же питон позволяет выкидывать даже циферки и строки =).
                            0
                            Как интересно, а что такое "полностью ОО-язык"? Я вот могу на C++ написать программу, где не будет ни одного класса, и чего?
                              0
                              а на яве не сможете (:

                              полностью ОО-язык это как раз тот и есть который не позволяет процедурного программирования. Хотя необходимость наличия классов опять же этого не показывает.
                                0
                                Java это не чисто ОО язык программирования. Иначе что бы там делали примитивы? int, float, char, byte — все это не объекты в Java.
                                0

                                Наверное

                      0
                      По теме: заставить PHP выбрасывать исключения вместо вывода предупреждений можно, используя код из этой статьи: http://forum.vingrad.ru/faq/topic-164992…
                        0
                        к сожалению не все типы ошибок возможно будет отловить таким образом :(
                        0
                        Обе парадигмы нужны. Плохо получается когда разработчик увлекся и соблюдает "чистый" стиль.
                          0
                          от знания меры никто и не отказывался. я сам противник впадения в крайности, но если статья навела на мысль чт оя полностью отказываюсь от возвращаемых значений, то нет - это не так…
                          0
                          Исключения позволяют избежать написания сложного кода. Они должны использоваться для локализации исключительного поведения, связанного с неожиданной ошибкой. Они не должны служить заменой проверкам выполнения условий.

                          Вот это исключение
                          try {
                          db.execute('UPDATE ...');
                          db.execute('INSERT ...')
                          }
                          catch {
                          print 'что-то не то с SQL';
                          }

                          А это проверка условия
                          user = User.find(login, password);
                          if (user == null) { // вместо try {...} catch UserNotFound
                          print 'Пользователь с такой комбинацией логина и пароля не найден';
                          }
                            0
                            да, неплохая статья, полезная)
                            0
                            Сам недавно переписал некоторые свои библиотеки на работу с исключениями. Вместо того чтобы возвращать false кидается исключение определенного типа. Например, есть функция добавления в БД какой-то записи, есть несколько причин, почему она может не смочь добавиться (дублирование уникального ключа, слишком линные данные, ошибка SQL). Раньше в таких случаях приходилось говорить, мол произошла ошибка, теперь есть возможность сказать какая именно (и прореагировать соотвествующе).

                            Что касается библиотек доступа к MySQL в PHP, то есть такая замечательная штука как PDO, который может выбрасывать исключения при ошибках SQL (с mysqli никогда не работал, поэтому не знаю, возможно, она тоже это умеет).

                            Инициализируется примерно так:

                            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);


                            Далее, при желании, можно переопределить глобальную функцию обработки исключений:

                            set_exception_handler('exceptionHandler');


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

                            И еще. Поддержку исключений гораздо проще встроить в тело какой-то функции, чем поддержку выхода по ошибке, особенно, если функция уже возвращает какой-то результат, а нам вдруг понадобилось сделать выход из функции по ошибке, реакцию на ошибку, причем типов ошибок может быть несколько. Единственное, более менее простое решение без исключений, которое прямо сейчас приходит в голову - это возврат $errorCode в параметрах функции. А если у нас параметров много, причем есть со значениями по умолчанию (придется переписывать вызовы функции)? А если их переменное количество?
                              0
                              имейте ввиду, исключения в php - не родное. Когда я последний раз плотно занимался пхп (сомневаюсь что многое изменилось) было ещё огромная куча варнингов, нотисов и фатальных ошибок, которые на самом деле являются теми же исключениями но без возожности "поимки" выйдя из стека.
                                0
                                Спасибо, очень хорошая статья, у самого никак руки не доходили потестить скорость ексепшенов.
                                Исключения стоит использовать только если дальнейшие _корректное_ выполнение скрипта, при возникновение в блоке исключений ошибки, невозможно или приведет к непредсказуемым ошибкам. В остальных случаях достаточно простой проверки по if.
                                Жалко что исключения в РНР не перехватывают некоторые критические ошибки, на uncatched exception тратятся дополнительные ресурсы, да и пользователю может пролезть сообщение об ошибке... :(
                                  +1
                                  Как страшно жить, зная что вокруг ходят разработчики софта, не знающие когда применять try catch (:
                                    –2
                                    осталось только ещё понять что вы в своих примерах вообще используете исключения как простой status return.

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

                                    Думать надо головой, в любой случае =).
                                      +1
                                        0
                                        Вообще не понятно зачем сравнивать операторы исключений и операторы условного перехода. Их в принципе сравнивать нельзя.
                                        Если используется объектная модель с высоким уровнем абстракции, то само собой для отлавливания ошибок (точнее будет сказать событий, т.к. исключение - это не всегда ошибка) будут использоваться exceptions. Если используется структурное программирование - разумеется никакой речи о перехвате исключений и быть не может.
                                        Наглядное (и очевидное) тому подтверждение - введение исключений начиная с PHP 5-й версии, т.к. от нее и начинается путь ООП в пхп-кодинге.
                                        • UFO just landed and posted this here
                                            0
                                            «ООП не требует и даже не подразумевает использование исключений.»

                                            Можно поподробней?
                                            • UFO just landed and posted this here
                                              0
                                              Exceptions ввели, чтобы можно было отслеживать события от классов (объектов), а переменные испокон веков проверяют конструкцией if..else.
                                              • UFO just landed and posted this here
                                                  0
                                                  В PHP неприлично дорогое???
                                                  Это как раз в компилируемых языках для него нужно создавать множество лишнего тормозящего кода.
                                                  В подобных же PHP языках, где стек вызовов и т.п. определяется на этапе выполнения, всё намного лучше.
                                                  • UFO just landed and posted this here
                                                      0
                                                      Понятно, что Ц будет быстрее ПХП по всякому.
                                                      Другое дело, что разница использования исключений в Си по сравнению с if в том же Си, будет гораздо больше, чем та же разница в ПХП. С учетом того, что и область применения Си гораздо чаще требует оптимизации, то тут гораздо серьезнее стоит вопрос о выборе между удобством и эффективностью.
                                                      • UFO just landed and posted this here
                                                          0
                                                          Какой топик? Я реагирую на ваше сообщение "обработка исключений - дорогое удовольствие. как выясняется, в php - просто неприлично дорогое" и тесты скорости, которые говорят о том, что под дороговизной вы понимаете время выполнения. Покажите мне тест, который бы указывал на то, что исключения в ПХП неприлично медленно работают по сравнению с другими PHP-конструкциями.
                                                          • UFO just landed and posted this here
                                                              0
                                                              Не нервничайте, пожалуйста, и не язвите.
                                                              Можно и аргументы в функции передавать путем запись в файл и чтения его в самой функции. Можно и исключения непойми для чего использовать.
                                                              Если же использовать их для того, чего нужно то никакой "неприличной дороговизны" нет.
                                                              Число "16" само по себе не говорит абсолютно ничего.
                                                              • UFO just landed and posted this here
                                                                  +1
                                                                  Да, давайте определимся. Дорого использовать исключения там, где не надо или вообще дорого?
                                                                  Я продолжаю настаивать на том, что при правильном использовании совсем не дорого.
                                                                  Относительное число 16 в абсолютных величинах ничтожно (это из темы использовать двойный кавычки или нет), а в более сложных вариантах при большом количестве уровней, туева куча ифов скорее всего будет еще и тормознее.
                                                                  • UFO just landed and posted this here
                                                                      0
                                                                      if по сравнению с хотя бы запросом к базе, это 0 или около того.
                                                                      Хорошо, видимо, вы не слишком чётко высказали свою мысль, а я не слишком чётко её понял :)
                                                                    0
                                                                    по-моему это и так понятно, что при использовании исключений - необходимо больше ресурсов, чем при использовании if...else. А вы думали будет по другому? А использование ООП по сравнению с обычным структурным программированием также требует намного больше ресурсов. А использование своего cgi-приложения оптимизированного под определенные нужды будет еще быстрее работать, чем php. И кодирование на ассемблере еще быстрее, чем любой другой язык...
                                                                    Чем выше уровень языка, тем больше ресурсов требуется для реализации кода.
                                                      0
                                                      причем здесь дорогое или не дорогое??? Если используете ООП, то единственным методом обработки ОБЪЕКТНЫХ событий (событий которые происходят в классах) является исключения.
                                                      поэтому исключения - это просто другой способ обработки ошибок

                                                      И ошибок тоже! Я к примеру могу генерировать исключения для отладки кода.
                                                      • UFO just landed and posted this here
                                                          0
                                                          События - это и так исключительное явление :) Положительный результат - это тоже исключительное событие. Так что тут вопрос философский. :)
                                                          факт состоит в том, что без них всегда можно обойтись и ООП не завязано на генерирование "объектных событий".

                                                          обойтись можно, но исключения именно для ООП и предназначены и ниче тут не попишешь. Если нужна гибкая функциональность и высокая абстракция, то без эксепшинов никак.
                                                          • UFO just landed and posted this here
                                                              0
                                                              пасиб. и вам также.
                                                              я не хочу устроить холивар. просто сталкивался с программистами, которые читали именно такие мануалы и смело использовали то, что абсолютно не нужно в данной ситуации.

                                                              оффтоп (хотя, может и не оффтоп :) )
                                                              Кстати, оч рекомендую хорошую книгу к прочтению :
                                                              http://rsdn.ru/res/book/web/php_5_prof.x…
                                                                0
                                                                зря рекомендуете эту книгу

                                                                невнятная
                                                                все галопом по европам
                                                                mvc вообще с какого-то непонятного ракурса приведен - пришлось "погуглить"

                                                                итого через 2месяца продал ее. т.к. нет ничего в ней, чтобы возможно пришлось еще раз прочитать
                                                                  0
                                                                  а с ней и Design Patterns за одно лучше сразу читать - тогда все понятно будет, т.к. именно на шаблоны проектирования идет уклон. Разумеется перед прочтением такой книги надо знать что такое ООП в действительности :) и pattern'ы проектирования неразрывно с ним связаны. вообщем гуглите.
                                                                  а книга действительно хорошая, но не для новичков разумеется.
                                                                    0
                                                                    в этой книге рассмотрены всего несколько паттернов проектирования.
                                                                    для изучения паттернов проектирования лучше приобрести книгу Э.Гамма, Р.Хелм, Р.Джонсон, Д.Влисседес "Приемы объектно-ориентированного проектирования. Паттерны проектирования"
                                                                    согласен что книга не для новичков.
                                                                    я ее покупал, надеясь увидеть подробное описание/применение MVC.
                                                                      0
                                                                      Вообще-то подробно про MVC можно почитать в книгах по Си. А переписывать такие же книги для пхп думаю будет глупо ибо пхп - это Си-подобный язык.
                                                                0
                                                                хабрахабр зашлючил. вот ответ на ваше сообщение :
                                                                http://habrahabr.ru/blog/php/37247.html#…
                                                  0
                                                  при неудаче соединения с СУБД, вывели бы соответствующую ошибку и прикатили выполнение сеанса
                                                  -----------------------

                                                  Исправьте, пожалуйста, в статье слово "прикатили" на "прекратили".

                                                  Спасибо.
                                                    0
                                                    <offtopeg&imho>
                                                    Смотрю вот на примеры кода и вижу яву, один в один. Сдается мне, что пхп 6.0 будет называть jPHP6 и странички JSPhp
                                                    не к добру это, ой не к добру
                                                    потеря самобытности и натяганые отовсюды фичи, смотаные изолентой, хорош язык, нечего сказать...
                                                    </offtopeg&imho>

                                                    Only users with full accounts can post comments. Log in, please.