Исключения. Где я их использую

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


    Логика


    1. Валидация данных: иными словами ошибка входных данных, формат поля не тот, поле не заполнено, ошибка уникальности поля — всё это ошибки входных данных, в таких случаях я бросаю эксепшн с перечислением всех полей и ошибок, которые в последствии выводятся пользователю.
    2. 404 Документ не найден: Если была запрошена конкретная сущность(например строка из бд) по её индефикатору(и не только), то в случае если её нет — бросается эксепшн, который в последствии превращается в страницу 404.
    3. 403 Доступ запрещён: Если была попытка доступа пользователя туда, куда ему нельзя — то кидается эксепшн(в аргументах можно передать почему нету туда доступа), который в последствии превращается в 403.
    4. 401 Требуется авторизация: Пользователь запрашивает данные, куда нужна авторизация. Выкидываем эксепшн(в аргументах можем передать Relarm — текст запроса авторизации), а пользователь получает либо запрос на HTTP авторизацию, либо обычную формочку.
    5. 301 Редирект: Надо редиректнуть? кидаем эксепшн — в нём передаём куда надо редиректить.

    Ошибки


    1. Фатальные ошибки: Тут всё понятно, нету коннекта к бд, или конфиг нельзя прочесть. Просто пишем в лог и покзывем дефейс.
    2. Критические ошибки: Например: СМСка не отправляется, файл нельзя залить — кидаем специфичный эксепшн. а в нужных местах(например — подтверждение телефона), обрабатываем его и пишем пользователю то что не можем отправить смс.

    Отладка


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

    Пример


    Ещё рекомендую логировать все «непонятые» эксепшены:

    class Controller_Front
    {
      // на уровне приложения
      public function execute()
      {
        try {
          ...
            // где то на уровне контроллера
            try {
              ... // тут вызов основнного тела контроллера
                // Захотели мы редиректнуть на хабр.
                throw new Controller_RedirectException('http://habr.ru');
              ...
            } catch (Controller_DataException $e) {
              // Сохраняем ошибки и показывем их в шаблоне
              $this->errors = $e->getErrors();
            }
          ...
        } catch (Controller_RedirectException $e) {
          // Метод, который посылает заголовки
          $this->_processRedirectException($e);
        } catch (Controller_AuthException $e) {
          // Метод, запрашивающий HTTP авторизацию
          $this->_processAuthException($e);
        } catch (Exception $e) {
          // Записывем эксепшн в лог.
          $this->_logException($e);
          // Показывем дефейс.
          $this->_processFatalException($e);
        }
      }
    }


    * This source code was highlighted with Source Code Highlighter.


    Пока не хватает кармы для переноса в блог PHP. Спасибо за карму, перенёс.

    Поделиться публикацией

    Комментарии 148

      +4
      Переноси скорее пока кармы хватает ;-)
        0
        Спасибо! надеюсь пост принесёт пользы.
        +1
        Честно, тема исключений более обширна и описанные вами ситуации — лишь малая её часть.
          +3
          Опишите вы её более обширно и вам скажут спасибо!
            0
            тоже интересно будет
            0
            А вы кстати строите логику работы софта на основе исключений?
              0
              Если что то идёт не так — то исключение. конечно, тут речь о вебе идёт.
                0
                Ну и конечно все места из серии

                ВызовМетода();
                exit;

                Делаю как эксепшн.
                  0
                  А что это за ситуации? Я, как, .Net программист, не очень понимаю зачем после ВызоваМетода() вызывать exit.
                    0
                    Редирект:
                    header('Location: '. $url);
                    exit;

                    Или же совсем простой пример.

                    echo «Фатальная ошибка»;
                    exit;

                    А можно в замен кинуть эксепшн и код красивее и удобнее
                      0
                      лучше:

                      die(«Фатальная ошибка»);
                        0
                        лучше:

                        throw new Exception('Fatal Error');
                          +1
                          еще лучше:

                          throw new Exception(«Fatal Error: Can't open file»);
                          (указать причину, если она известна)
                            0
                            Вообще то это подразумивалось изначально…
                            PS. мы достигли идедеала!
                              +1
                              die() быстрее.
                              0
                              Если уж пошла такая «пьянка», то лучше не использовать общий класс Exception, а что либо производное от него IOFileException или что-то аналогичное.
                                0
                                Я, например, если создаю какой то класс, котрый служит отдельным сервисом создаю для него ещё его ексепшн. оч удобно.
                                  0
                                  Мне такой подход не подходит, в проекте может быть много классов и для некоторых создавать своё исключение накладно, в плане что начинают путаться.
                                    0
                                    Если они действительно нужны для отлова или передачи какой то специфичной информации, а так, лучше обычный Exception.
                  +5
                  >Надо редиректнуть? кидаем эксепшн

                  жесть
                    0
                    Лучше бросить эксепшен, и в едином месте его правильно обработать, чем везде писать header('Location: example.com/'); die();. Стройнее будет.
                      0
                      а зачем везде писать header('Location:…

                      Достаточно вызывать метод который остановит выполнение скрипта и сделает location

                      Вот например в zend frameworke я использую метод Zend_Controller_Action::_redirect($url [, $options])
                        +4
                        Не всегда нужно остановить выполнение скрипта. Если вызывать _redirect(), который завершается exit()'ом, то, логично, после него уже ничего не выполнится. А если бросить эксепшен, то на уровне выше можно его поймать, запомнить, что нужен редирект, провести уборку/записать логи/итд, и вот тогда уже завершить работу, выплюнув нужные заголовки.
                        0
                        Ну если выбирать из предложенных двух вариантов, то конечно исключения лучше. Но что мешает сделать redirect('example.com'); и дальше завершить выполнение в соответствии с логикой приложения.
                          0
                          Не просто redirect(), а redirect(); return 'Я СДЕЛАЛ РЕДИРЕКТ'. Это аналогично header(); die(), только выше ещё нужно будет анализировать либо код возврата, либо выставленные ф-ей redirect() флаги. В принципе, получается своеобразная эмуляция эксепшена.
                            0
                            это не эмуляция, а получение необходимого функционала методами, которые можно использовать для этого. ведь возможно в какой-то момент гото будет безумно удобным, но это ведь не заставит Вас его использовать?

                            Да и все намного проще. Например в симфони это может выглядеть примерно так:
                            functuion redirect($to)
                            {
                            ...->getResponse()->setHeader('Location', $to);
                            return sfView::NONE;
                            }

                            function executeSomething()
                            {
                            return redirect('somewhere');
                            }

                            Имеется ввиду что если система имеет необходимый набор функциональности, то никаких дополнительных проверок и обработок не требуется.
                        0
                        Да, гораздо проще вызвать метод который там сделает exit; =)
                          0
                          Как я понял, это эмуляция механизма catch/throw в Ruby(для классических исключений там используется синтаксис begin/rescue/ensure/raise).
                          Для иллюстрации можно привести пример кода из популярного фрэймворка Sinatra.
                          Есть блок catch(:halt), который отлавливает комманду halt в контроллере. Как только она вызывается, обработка контроллера прекращается, и результат отправляется пользователю. Таким же методом можно реализовать и редирект.
                          Отличие rescue/raise(классические эксепшены) и catch/throw(по сути «умный» goto) в том, что первый для исключительных ситуаций, когда что то пошло не так, а второй для вполне нормальных, типо редиректа(ну, конечно если причиной редиректа не послужмла ошибка).
                          В языках где нет такого разделения использоваиние исключений для не исключительных ситуаций хоть и идеалогически неверно, но все же иногда очень удобно:)
                          0
                          Небольшой но содержательный и полезный пост! Очень здорово было бы рассказать о применении эксцепшенов в «рядовых» ситуациях (ну в стиле «Избавляемся от «if(!$db) die('...');»», чтобы задать вектор мышления у людей, не пользовавшихся ими до этого.
                            0
                            Все таки надо поправить хабрапарсер, чтобы он не обрабатывал двойные кавычки внутри «елочек»
                              0
                              Ой, честно говоря, хотел в другой топик написать =)
                              0
                              Кстати, еще удобно эксепшены отправлять на специальный ящик, а особо важные — джабберботом.
                                0
                                Очень хорошая идея. Объязательно реализую в проекте. Спасибо!
                                  0
                                  Я делаю так — у меня в отдельном потоке запущен джаббер бот(на каждый эксепшн логониться долго), который отправляет описания всех критических ошибок, которые програмно не получилось обработать. На почту же отправляются полная инфа об эксепшене, т.е. весь бэктрэйс + параметры. Притом каждому сообщению присваиваю уникальный индетификатор [123] Some exception, что бы легко можно было на почте найти сообщение, которое в джаббер пришло. Очень удобная схема.
                                  Но это больше для тестового периода подходит, в релизнутом приложении эксепшн не чаще чем раз в полгода должен происходить:)
                                    0
                                    Я завёл в гугл групах приватную группу и планирую внести туда всю группу разработки… Пусть знают)
                                  0
                                  все таки эксепшены логичнее программно обрабатывать. А на почте логичнее слать логи ошибок — если где то не отлоивли эксепшн например
                                    0
                                    Смотря какие эксепшены. Все подряд логгировать конечно нет смысла. А вот если база отвалилась или еще что-нибудь такое страшное, то тут не только на почту и в джаббер, тут и смски прикрутить можно:)
                                  0
                                  дада, все читают ru_php
                                    0
                                    я не читаю, и горжусь этим! вообще в первый раз туда зашёл =)
                                      +1
                                      зря, там хорошо выбивают из головы глупые идеи и дают взамен новые — для размышления
                                        0
                                        Подписался на рсс. Если будет что то дельное — то хорошо… А вообще я каждый день на работе занимаюсь«промывательством» мозгов себе...)
                                          0
                                          Там чаще всего не посты надо читать, а комментарии к ним. В комментариях там — вся мощь и польза. :)
                                            0
                                            Вся мощь и польза тут.
                                              –3
                                              А вообще у меня если что есть «гуру» с которым я могу консультироватся и получать обратную связь…
                                                +2
                                                Рад за тебя.
                                      0
                                      локанично. — лаконично же!
                                        0
                                        Спасибо. Поправил.
                                          0
                                          «локонично» — это пять, простите за занудство.
                                            0
                                            «Локонично» — это синоним «лохмато»? :)
                                              –1
                                              =) Не даром у меня очень «хорошие» отношения с учителями русского языка…
                                        +2
                                        Я не думаю, что делать валидацию с использованием exception'ов правильно.
                                          –1
                                          На моём проекте валидация данных на урное БД! так что единтсвенный выход — это эксепшн =)
                                            +1
                                            Такое к сожалению бывает в уже написанных системах. Жаль, по рукам никого за это не бьют.
                                              0
                                              Это было решение архитектора, частично — это удобно, наверно по тому что писал с нуля сам эту всю систему..) Валидация в триггере имеет своё приемущество)
                                                +1
                                                зато тянет множество недостатков.
                                                  0
                                                  Решение адекватное, за пол года себя не провалило. Посто в Mysql — это жесть. а PostgreSQL — это гораздо удобнее.

                                                  ПС. Подумаешь дамп схемы бд около 29 тыщ строк..)
                                                  0
                                                  А какие, например?

                                                  Даже одно то, что для того чтобы свалидировать данные вам надо подключиться к бд и переслать данные, это уже терминальная стадия ФГМ.
                                                    0
                                                    А зачем вам «просто так» валидировать данные? код не задумывается о «правильности» данных. Ему надо обновить или вставить запись — он это делает, в случае если бд выплёвывает эксепшен — то тогда показывается просьба ввести правильные данные. это один.

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

                                                    Достаточно простой и мощный язык написания.

                                                    ПС. Естественно всё про PostgreSQL.
                                                      0
                                                      1. Проверка уникальности полей — это лишь один из *многих* кейзов валидации данных.

                                                      2. Из-за пункта 1, код для валидации на pl/sql будет уже не так прост. Привет веселой отладке. Плюс нужно все равно часть кода обработки ошибок валидации иметь на клинте. Совеременные СУБД, слава аллаху, не научились еще отдавать html :-) А это уже дублированием пахнет.

                                                      3. Я не говорю сейчас о быстродействии. Я говорю о здравом смысле. Нафига вообще в пустую обращаться к СУБД, если мы не уверены в том, что данные валидны, соответсвенно, не уверены, что они вообще попадут в СУБД.

                                                      4. Насчет хп и прочего на стороне СУБД. Выч. ресурс СУБД всегда дороже, чем тот же ресурс backend и frontend. Это становится очевидным, как только у вас чуть более, чем один сервер или когда у вас стоит выбор два backend или два СУБД сервера. Второе всегда сложнее, даже если у вас есть классный DBA.
                                                        0
                                                        2. PL/PgSQL Вполне позволяет всё валидировать без геммороя.
                                                        2.1. Отдавать HTML они умеют, только не по HTTP…
                                                        3. СУБД от этого плохо не будет. Такая архитектура(вся бизнес-логика в бд) живёт под нагрузками.
                                                        4. Второе неизбежно но это 10% нарузки больше, которые погоды не сделают.

                                                        Это несомненно интересный разговор, но он отошёл от тематики темы.
                                            +2
                                            Ужас.

                                            Код с кучей try/catch — адский.

                                            Ловить (и логировать/обрабатывать) исключения и ошибки проще имхо хенедлером, подвешенным к set_error_handler()/set_exception_handler()

                                            Редиректить лучше имхо вызовом метода в контроллере.
                                              +1
                                              Ересь несёте дорогой. подсчитал порядка 370 try...catch на проект 52 тысяч строк. Если конечно у вас единая точка входа.
                                                0
                                                Это не считая ОпенСорсные либы =)
                                                  0
                                                  Да, в том числе и юнит-тесты.
                                                –3
                                                А как насчет того, что try медленнее, чем if.
                                                Вы проводили сравнение скоростей?
                                                Некоторые советуют использовать вместо try if.
                                                типа:
                                                  0
                                                  if(not_autorized) throw exception('Error auth');
                                                    0
                                                    Простите, угадайте, сколько раз в «нормальном» коде происходят исключения? Сколько у вас на сайте 404 403 401 301? Может один if быстрее throw catch. но обычно это не один, а несколько так как throw может произойти на 3-4 уровня ниже… уже получаем 3 if и абсолютно плохо читаемый код?) 1-2 вызова throw и 3-4 try catch погоды не сделают. Если в коде вызывается 100 throw — то это уже говнокод=)

                                                    Итого: Да, может и медленней, но разница на столько не существенная при рекомендованных дозах, что этим можно пренебречь из-за удобства…
                                                      0
                                                      При таких условиях согласен с вами
                                                        0
                                                        умножайте количество try-catch на число хитов
                                                          0
                                                          А как число хитов влияет на кол-во if-ов?
                                                            0
                                                            этого я не знаю. но если спросите, насколько конструкция try-catch медленнее аналогичной по логике, но с использованием условного оператора — отвечу — более чем в 5 раз. про хиты — я хочу сказать что сервер не резиновый.
                                                              0
                                                              А сколько ифов заменяет один try-catch? считали? до 5-6 штук и заменяет =) более того, в относительных величинах эти цифры ничтожны.
                                                                –1
                                                                я брал кусок кода, переписывал его, с использованием if-ов, и измерял скорость выполнения. конечно, если заменить один try-catch то врядли увидишь разницу. но если заменить один try-catch, который выполняется при каждом запросе — можно обслужить в пять раз больше запросов на том же железе. согласен, не стоит заморачиваться на этом, если речь не идёт об высоконагруженных сайтах.
                                                                  0
                                                                  Хм, интересная статистика. а абсолютные цифры можете привести?
                                                                    0
                                                                    к сожалению, у меня не было опыта переписывания системы, использующей try-catch для управления логикой, так что каков будет реальный прирост производительности сказать трудно. всё зависит от того, насколько «увлечённо» это дело используется. Если хотите спорить о том, что try-catch «не медленне» — я пасс. мои исследования говорят об обратном, хоть они и были произведены «в песочнице». читателям посоветовал бы отказаться от try-catch в пользу структур и паттернов, прямо преднахначенных для управления логикой.
                                                                      0
                                                                      То есть реальных сравнительных тестов нету…
                                                                        0
                                                                        абсалюлти :) пока не было времени переписывать всю систему.
                                                        +3
                                                        О, нанооптимизаторы пришли. А вы двойных кавычек тоже избегаете, потому что медленнее?
                                                          –1
                                                          Ни в коем случае. Просто было интересно посмотреть сравнение.
                                                          Всем понятно, что с увеличением мощностей серваков, разница между if и try не такая уж и большая
                                                        0
                                                        Все-таки, столь широкое exception'ирование сродни макаронному коду, особенно при описанной Вами архитектуре — когда есть куча исключений и куча отловов их на разных уровнях логики.
                                                          0
                                                          Прошу пордона, там я считал кетчи. В контроллерах и кастомных класа порядка 111 try..catch на 52 000 строк кода это вполне позволительно, не вижу ничего страшного, одни удобства…
                                                            0
                                                            и всего лишь 400 throw(150 из них сделано шорт катом $this->redirect());
                                                              0
                                                              Ну, например, есть сложное ядро проекта, имеющее свои классы для всего, что только можно. Я пишу модуль. Мне нужно взять данные из формы, обработать и записать в базу. Если база недоступна, то отправить на почту. Так как я хочу полностью знать, что делает мой модуль (сюрпризов быть не должно), мне надо:
                                                              1. Посмотреть на всех уровнях логики, как обрабатываются исключения неправильных входных данных.
                                                              2. Посмотреть на всех уровнях логики, как обрабатываются исключения ошибки соединения с базой данных.
                                                              3. Посмотреть в классе работы с базой данных, какие исключения он может выбрасывать, какие из них мне нужно перехватывать, какие — нет.
                                                                0
                                                                да? а что бы вообще вызывать код, вам надо знать API. а откуда вы его возьмёте? из доки. так вот если код задокументирован то проблем не должно быть.

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

                                                                Теперь представтье весь этот гемморой без эксепшенов.

                                                                PS в PHPDoc есть поддержка документирование возможных эксепшенов
                                                                /**
                                                                 * @throw Exception On Error
                                                                 */

                                                          +4
                                                          1. Exception — это конструкция должна применяться только в случае исключительной ситуации.

                                                          Если вы так делаете редирект, то ваш код крайне запутан, потому что Exception не дает нам знания о том, куда передается управление. Предполагается, что только в обработчик ошибок. Плюс к этому, это нарушение интерсфейса на самом деле. Редирект — это точно такое же View с точки зрения MVC, например. Почему в одном случае контроллер возвращает его, а в другом делает goto, фактически. То есть, я делаю вывод, что ваше приложение просто плохо спроектировано, если вам это надо.

                                                          2. В ооп почти всегда можно обойтись без switch. Использование конструкций типа:

                                                          catch (x)

                                                          catch (y)



                                                          как правило оправдано только в случае, когда вы не можете управлять чужим кодом, который кидает критический exception. Во всех остальных случаях, это всегда можно разнести по разным слоям и декларировать строгий интерфейс взаимодействия между такими слоями вместо того, чтобы пользоваться goto, для third party сделать wrapper, для передачи мета-информации об ошибке, сделать более общий тип excetpion, но с доп. объектом, в котором будет инкапсулирована эта информация и т.д.

                                                          3. Можно вспомнить еще о наследовании, и о том что при большом кол-во catch и разных exception можно попасть «нетуда» :-)

                                                          Такое использование сильно похоже на процедурный стиль, а там можно обойтись и без Exception.

                                                          Вывод: Exception является мощным средством для возбуждения, передачи и обработки критических ситуаций, но как любой инструмент он должен использоваться к месту.
                                                            –8
                                                            В ПДД написано дословно: «переходить дорогу в неположенных местах запрещено.», вы часто его соблюдаете? всегда? а почему нет? да по тому что гораздо удобнее перейти улицу с маленьким трафиком, а не идти 200 метров до перехода… Тут так же и с эксепшенами. Да редирект — не совсем исключительная ситуация, но сделав исключение и реализовав это через эксепшн ты получаешь кучу удобств.
                                                            Да, и не путайте божий дар с яичницой. Скриптинг от системного программирования немного отличается…
                                                              +3
                                                              Ну да, если у нас «скриптинг», то руки можно не мыть. Бывает хороший код и плохой. Вот ваш — плохой, на мой взгляд.
                                                                –7
                                                                Мне не надо доказывать качество моего кода в ваших глазах. Его оценивают по достоинству авторитетные личности, мне этого достаточно.
                                                                +1
                                                                Если речь идет о мелком говнопроекте, который вряд ли когда-нибудь выльется в что-либо серьезное, этот подход оправдан. По быстрому слить и умыть руки.
                                                                Пренебрегая банальными правилами хорошего тона в программировании Вы 1) закладываете мину замедленного действия в код, который возможно будет ковырять кто-нибудь другой и обязательно помянет вас недобрым словом. 2) банально распускаете себя, как программиста, в следующий раз переход на красный свет для вас будет более очевидным решением, чем переход в положенном месте.
                                                                То что для вас является удобством, для других может оказаться сами знаете чем.
                                                                Из поста согласен только с обработкой реальных ошибок и security проблем через исключения, но не в таком виде, как тут написано. Походу, несмотря на название класса, о MVC вы имеете крайне смутное представление, и в частности о loose coupling, separation of concerns и encapsulation.
                                                                Удачи.
                                                                  0
                                                                  Если вам «религия не позволяет» использовать такой приём, то я вас не заставляю. Я просто рассказал как удобно и эффективно я это использую в большом проекте. и всё отлично работает.
                                                                    0
                                                                    Да, и расскажите мне как «приавльно» делать редирект и как покрыть его юнит тестом. Спасибо заранее за ваши усилия мозгом.
                                                                      0
                                                                      Например, это может быть разновидность view, как у нас.
                                                                        +1
                                                                        Ссылки почему то скипнулись.

                                                                        View: git.shadanakar.org/?p=onPHP.git;a=blob;f=main/Flow/View.class.php;h=b8bc3a9b78de4dc30d438330f73ca073b4b2e9ba;hb=HEAD

                                                                        RedirectView: git.shadanakar.org/?p=onPHP.git;a=blob;f=main/Flow/RedirectView.class.php;h=487fa3176bb15d6fe0317ebd046885a5790a6397;hb=HEAD"

                                                                          0
                                                                          О, еще кто-то кроме меня считает что генерация URL это View-логика?
                                                                            0
                                                                            Мне надо после редиректа прервать выполнения текущего контролера — чего делать будите? exit? а как же тестами это покрыть?
                                                                              0
                                                                              Если бы у вас был четкий интерфейс между контроллером и представлением, то ваш тест был бы очень прост:

                                                                              controller — имеет набор входных параметров. Например, все эти параметры инкапсулированы в каком-то HttpRequest.

                                                                              на выходе, если это не крит. ошибка, то всегда будет объект. Допустим ModelAndView.

                                                                              Тест тривиален.

                                                                              На входе HttpRequest. На выходе ModelAndView, где View — это RedirectView.

                                                                              Сам же непосредственно редирект и формирование урла — это отдельные компоненты, которые тоже могут быть покрыты тестами, причем единожды, а не для каждого контроллера.
                                                                                0
                                                                                А теперь представьте сколько надо ифов если у меня 3ёх местах вызывается редирект и не факт что на 0 уровне стека, а вложено(то есть внутри функции которая была вызвана в контроллере). Только усложнение читабельности кода. «Заставь дурака богу молиться — лоб расшибёт» тут также, отход(не нарушение!) прицепов ведёт только к удобству.
                                                                                  0
                                                                                  Причем тут if. Я вам пишу совершенно не об этом. А про то чем можно заменить if вообще можно прочесть в книжке «Рефакторинг» у Фаулера.
                                                                              0
                                                                              Почти всегда это так.

                                                                              Внутри есть scopes. Причем их может быть несколько, в зависимости от контекста. Правила сборки урла, а фактически трансформации множества scopes в строку формата http url, с применением некоторой схемы, тоже различны.

                                                                              Что-где происходит.

                                                                              Формирование scope может быть в любом слое. Application Scope — это вообще может быть статически определенный набор параметров конкретного контроллера, import происходит в контроллере.

                                                                              А скажем UserScope может формироваться и во view — динамически. Пример: прозрачная передача параметров род. scope в ssi scope.

                                                                              Поэтому, с этой точки зрения, совершенно не важно где это происходит. Важно какая концепция внутри.

                                                                              Конкретно метод трансформации scopes в url вызывается во view, конечно же.

                                                                              Теперь по реализации.

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

                                                                              1. git.shadanakar.org/?p=onPHP.git;a=blob;f=main/Application/ApplicationUrl.class.php;h=75bc8703cc7440ed37f1375ec607fd6ed440014a;hb=1.0

                                                                              2. Концепция роутеров(всего лишь как часть) git.shadanakar.org/?p=onPHP.git;a=tree;f=main/Utils/Routers;h=236a39109f1b23b9548912202bdf8ed8d93b4166;hb=1.0
                                                                    0
                                                                    > Такое использование сильно похоже на процедурный стиль, а там можно обойтись и без Exception.

                                                                    Не спорю, без исключений можно обойтись. Точно так же в процедурном стиле можно обходиться и без других языковых конструкций: switch, for, foreach, do-while,… — в пределе достаточно использовать if и goto. :) Мне кажется, что утверждение «не нужен, потому что можно обойтись», с практической точки зрения, лишено смысла.

                                                                    Замечу, что Exceptions в стиле try-throw-catch это чисто процедурный механизм. Просто он получил популярность в ООП-языках, поэтому часто ассоциируется с этим подходом. Но это не так. Как мы видим в примере из статьи, данная конструкция может прекрасно работать безо всякого ООП даже в «лапше».
                                                                      0
                                                                      Вы только не учли одну вещь. Статья позиционируется в ООП-парадигме. Об этом нам как бы говорят комменты автора и тэг «ооп».

                                                                      И я писал вовсе не о том. Я писал о том, что таким образом использовать exception не нужно ни в какой парадигме.

                                                                      А микроскопом можно и гвоздь забить, да :-)
                                                                        0
                                                                        > Вы только не учли одну вещь. Статья позиционируется в ООП-парадигме. Об этом нам как бы говорят комменты автора и тэг «ооп».

                                                                        Основной смысл механизма исключений — поддержка строго-иерархического разделения ответственности за идентификацию исключительной ситуации и принятие решения о дальнейших действиях — на мой взгляд, в примере показан изумительно. Комменты в коде — великолепны. О MVC речи не шло. Тег «ооп» пусть останется на совести автора.

                                                                          0
                                                                          Ну вообще то теги были скопи-пастены наглым образом
                                                                          ООП тут только по тому, что механизм try..throw..catch позволяет передавать объекты.
                                                                            0
                                                                            Слегка переписал :-)

                                                                            switch (ext_type)

                                                                            case vso_slomalos:
                                                                            break;
                                                                            case redirect:
                                                                            break;
                                                                            case return_from_func_my_funct:
                                                                            break;
                                                                            default:
                                                                        0
                                                                        > Если вы так делаете редирект, то ваш код крайне запутан, потому что Exception не дает нам знания о том, куда передается управление. Предполагается, что только в обработчик ошибок. Плюс к этому, это нарушение интерсфейса на самом деле. Редирект — это точно такое же View с точки зрения MVC, например

                                                                        По смыслу, редирект это действие — переход из одного состояния в другое. А действия находятся за рамками структурного паттерна MVC. Для представления действий есть собственные паттерны (например, редирект можно описать как Command). Не понимаю, зачем пытаться все описать лишь тремя словами: M V C — как будто других слов нет (или «можно обойтись» это Ваше кредо?). Впрочем слогласен, что Exception здесь тоже жесть. :)
                                                                          0
                                                                          > для передачи мета-информации об ошибке, сделать более общий тип excetpion, но с доп. объектом, в котором будет инкапсулирована эта информация и т.д.

                                                                          Думаю, не надо так делать. Это антипаттерн для исключений. Наличие мета-информации связанной с ситуацией, ни как не должно влиять на тип exception.

                                                                          В языках программирования, информация о том, «что есть» данные (isA), выражается при помощи типа. В механизме try-throw-catch, для представления информации об исключительной ситуации, служит объект-исключение. Тип такого объекта должен быть достаточен для идентификации этой ситуации на более высоких уровнях приложения, которые будут перехватывать исключение.

                                                                          Если тип исключения будет мало информативен («СлучиласьКакаяТоФигня»), то это приведет к усложнению логики перехвата (например, анализу дополнительных данных в объекте). Поскольку решение о бросании исключения принимается в какой-то отдной точке кода, а анализ будет производиться во многих (пример, одна библиотечная функция <-> много приложений, которые ее используют), то малоинформативные исключения вынуждают делать кучу сложной работы.

                                                                          Поэтому при выборе типа исключения рулит конкретизация, а излишнее обобщение как раз вредно. При необходимости, отношение обобщения между исключениями выражается при помощи наследования (в PHP все исключения это Exception).
                                                                            0
                                                                            > Думаю, не надо так делать. Это антипаттерн для исключений. Наличие мета-информации связанной с ситуацией, ни как не должно влиять на тип exception.

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

                                                                            Приведу пример.

                                                                            Допустим у вас есть какое-то старое api в сишном стиле. Это так где у нас функция возвращает результат, который говорит о том, что произошла какая-то ошибка, а подробности можно узнать через спец. функцию типа get_last_error_detailes().

                                                                            В «древности» люди были не дураки и не плодили типы и функции просто так. В данном случае нам достаточно ввести 1 Exception, типа APIException и заворачивать в него всю информацию об ошибке, нежели плодить иерархию из типов.

                                                                            Наследование — не единственный механизм декомпозиции. И кстати, наименее гибкий.

                                                                            Я, кстати, писал не про данный конкретный пример, но просто когда я критикую, я стараюсть также перечислять возможные варианты.
                                                                              0
                                                                              > Я, кстати, писал не про данный конкретный пример, но просто когда я критикую, я стараюсть также перечислять возможные варианты.

                                                                              ничего не имею против критики «на всякий случай» — пусть не расслабляются. =)

                                                                              > В «древности» люди были не дураки и не плодили типы и функции просто так. В данном случае нам достаточно ввести 1 Exception, типа APIException и заворачивать в него всю информацию об ошибке, нежели плодить иерархию из типов.

                                                                              Судя по тяге «достаточности» Вы в реале либо отшельник, либо скупердяй. :) На всякий случай, хочу отметить, что «достаточно» это не всегда «выгодно». В «древности» люди были невежественны, ибо имели очень смутное представление о типизации и инкапсуляции. Но в отличие от первобытных программистов, современные понимают, что типизация и инкапсуляция _выгодны_, поскольку помогают бороться с различными аномалиями кода. Если предполагается, что код не одноразовый, а будет переиспользоваться, то выгоднее каждую исключительную ситуацию описать class'ом, чем заставлять код на высших уровнях городить анализ кодов возврата. Анализ кодов возврата ненадежен и излишне многословен это уже многократно было проверенно в процессе эволюции человечества. :)
                                                                          –2
                                                                          Как просто опредить, стоит ли использовать исключение или нет? Очень просто: каждое выброшенное исключение должно в конечном этоге повлечь за собой отсылку email-а сапорту и показать пользователю ошибку 500. Если хорошенько задуматься над этим, то всё станет понятно.
                                                                            0
                                                                            Это не эксепшен, а kernel panic какой-то получается ;-)
                                                                              0
                                                                              не кернел, но да, паник :) исключения они это и есть. когда участок кода выбрасывает исключение, он как бы говорит «случилось что-то страшное, я не знаю что делать дальше». это как были задуманы эксепшны. если кто-то знает, что делать — пишеться «catch». но таких случаев в реальном приложении не так много — обработка транзакций, чтение из фаловой системы и т.п.
                                                                                0
                                                                                Знаете, telnet тоже был задуман как средство для удалённого управления, а все им доступность smtp-серверов проверяют :)
                                                                                  0
                                                                                  знаете, телнет был задуман как нечто гораздо большее, и тот, кто это знает, использует его не только для проверки доступности smtp серверов ;)
                                                                                  я не хочу обсуждать телнет в этом топике с вашего позволения
                                                                                    0
                                                                                    Это называется «незадокументированные возможности» =)
                                                                                      0
                                                                                      Хорошо, не будем. Я просто к тому, что средство не всегда используется так, как оно было задумано, и иногда даже довольно далеко от пресловутого «прямого назначения». Если его удобно и понятно использовать по-другому, люди будут его использовать по-другому, и ничего с этим не поделаешь. Так, собственно, холиворы и возникают =)

                                                                                      Безусловно, применяют исключения и совсем неправильно, но это нормально. Устаканится ещё, пока просто время не пришло. Ведь не наделав ошибок не осознаешь, почему так нельзя делать.

                                                                                      А исключения можно и нужно применять, оглядываясь на контекст. Если это исключение на уровне приложения, то, конечно, саппорта/разработчика стоит известить. Или если его никто не поймал. А если это исключение в валидаторе, относящееся к данным, которые ввёл пользователь, то известить об этом нужно пользователя. Это просто тот же самый эксепшен, повёрнутый другой стороной. И редирект сюда, кстати, тоже вписывается. Не согласуется с классическими лекциями по обработке исключений? Что ж, посмотрим лет через 5, что приживётся.
                                                                                        +1
                                                                                        Когда я работал тимлидом, новички меня часто спрашивали а как «сделать правильно»? Я всегда пытался отвечать на этот вопрос так: нету такого понятия правильно или нет. Есть такое понятие «хорошая инженерная практика». Оно применимо и к программированию. И это не холивар, потому что практика становится хорошей или плохой, опираясь не на спор, но на опыт.
                                                                                0
                                                                                Только нужно заметить, что не обязательно каждое. Если у нас, например, приложение с нагрузкой 200-300 хитов/сек, это запросто вешает почтовик :-)

                                                                                Поэтому тут можно применить более интеллектуальные техники доставки и оповещения об ошибке.
                                                                                –4
                                                                                других http кодов ты не знаешь? ;-)
                                                                                  0
                                                                                  баба яга против эксэпшона на валидацию ввода.
                                                                                    0
                                                                                    я вообще считаю, что надо вначале контроллера проверять уровни доступов и авторизации, а не после тела.
                                                                                    имхо, когда работаешь в команде, так проще, ибо люди пока ещё читаю сверху вниз, а не снизу вверх
                                                                                      0
                                                                                      А никто об обратном и не говорит.
                                                                                      0
                                                                                      вопрос автору:
                                                                                      предположим, существует две связанных таблицы. допустим — users(user_id, name) и user_friends(user_id, user_id) со списком друзей (ссылки на ту же таблицу users) был у юзера друг а потом удалился. но в таблице user_firends ссылка осталась (по какой-то ужасной причине). и вот теперь на странице со списком друзей пользователя, когда мы подгружаем каждого друга (или их картинки, по ID), мы не находим друга№34232 (который удалился). Насколько я понял, согласно Логике#2 юзер получит 404?
                                                                                        0
                                                                                        Нет. Речь идёт о выборках где выбирается конкретная сущность, то есть в данном случае это не конкретная сущность, а список. если бы мы зашли на страницу друга 34232(который удалился), то тогда мы бы запросили конкретного друга по номеру(или что у вас. айдишник?) 34232 — но не нашли — тогда бы получили 404.
                                                                                          0
                                                                                          то есть юзер получит что? (какое исключение будет выброшено?)
                                                                                            0
                                                                                            Я изначально получаю список друзей пользователя и рассчитываю получить не строго одну запись, а набор/массив/список из записей. Если я получаю список из записей, то если он пустой — это ещё не значит то что это повод для 404. никаких эксепшенов в таком случае я не бросаю.

                                                                                            Если мне надо получить строго одну запись, а её в бд нету — то только тогда я бросаю эксепшн.
                                                                                              0
                                                                                              тоесть в данном случае юзерпик (или чтото ещё) просто не отобразится и ситуация проигнорируется?
                                                                                                0
                                                                                                Да, не стоит делать из мухи — слона.
                                                                                                  0
                                                                                                  ок. а если вместо юзеров и юзерпиков — транзакции и ссылки на лог колбэка от платёжной системы а страница — отчёт в админской?
                                                                                                    0
                                                                                                    Гм, уход от темы. Целостность бд и проверка её целостности не совсем задача эксепшенов. Если вам надо построить систему которая проверяет целостность данных, то надо исходить из этого. я предпочитаю доверять бд.
                                                                                                      0
                                                                                                      одна из главных задач исключений — обработка ошибок и ситуаций, связанных с целосностью данных. классический пример из учебника выглядит примерно так:

                                                                                                      try {
                                                                                                      doCriticalAction()
                                                                                                      } catch (Exception e) {
                                                                                                      recoverData();
                                                                                                      throw new Exception('Critical action failed');
                                                                                                      }

                                                                                                      то, что делаете вы (Логика#1-#5 и Ошибки #1-2) — это попытки реализовать систему, основанную на сигналах\сокетах, как например это сделано в десктоп-приложениях. («намёки» на это есть и в .NET, так что возможно это заблуждения идёт «оттудава» )

                                                                                                      я пока не склонен «разбивать в пух и прах» такой подход, так как хочу присмотреться поближе. Однако критические изъяны налицо: деградация производительности и уменьшение стабильности системы засчёт неправильной обработки ошибок.
                                                                                                        0
                                                                                                        Путаете что то.

                                                                                                        Деградация производительности ещё не доказана. Такая же деградация по вашим меркам получается если использовать двойные кавычки для строковых констант.

                                                                                                        То что вы привели в примере тут, и в примере чуть выше немного разные действия.

                                                                                                        С начала вы говорили про Чтение данных, и про то что в случае ошибки в этих данных пользователь получит не сообщение об ошибки, а эти битые данные. При этом вы успели обвинить в этом не БД, а скрипт, который эти данные читает.(Накой вообще тут упрёки в нарушение целостности)

                                                                                                        А теперь вы говорите про запись. Так вот дорогой мой, представьте что Вы забыли/не знали/етц о том что в случае если какой то метод вернёт код ошибки надо откатить транзакцию, ну не знали и всё тут(или этот метод изменили потом). И чего получается? скрипт который работает криво, при этом нарушается целостность данных.

                                                                                                        А теперь представьте что вы используете не коды ошибок, а эксепшены. если вы добавляете в код новый фроу эксепшена вы точно знаете то что любая открытая транзакция откатится назад, если это надо. — что на лицо? только повышение стабильности.

                                                                                                        Пример:

                                                                                                        try {
                                                                                                        $db->begin();

                                                                                                        //… do somthing critical.

                                                                                                        $db->commit();
                                                                                                        } catch (Exception $e) {
                                                                                                        $db->rollBack();
                                                                                                        throw $e;
                                                                                                        }

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

                                                                                                          0
                                                                                                          что-то как-то сумбурно… вы повторили всё что я сказал и сказали что я был неправ. или я не прав? :)
                                                                                                            0
                                                                                                            Вы не правы в том что вы сказали что я не прав, хотя в большинстве мы говорили об одном и том же.

                                                                                                            Вас пугает название Логика. смотри на него как Логические Ошибки(редирект стоит отдельным пунктом) и тогда вы со мной согласитесь, что такой способ правильный и эффективный.
                                                                                                              0
                                                                                                              в том-то и дело, что я счетаю что «логичские ошибки» не слудует смешивать с системными ошибками. тогда будет толк от обработки последних, вернее тогда будет понятно, что есть ошибка и что нужно с ней делать. Если везде используются исключения, смысл которых зависит от контекста использования — это повышает сложность программы, затрудняет отладку и чтение кода, ухудшает стабильность, и в совокупности ухудшает качество ПО.
                                                                                                                0
                                                                                                                Стоп. Вы наверно думаете что я везде и всегда использую класс Exception.
                                                                                                                  0
                                                                                                                  Упс. Случайно нажалось не та кнопка. Я имею в виду то, что различить исключения можно по их классу. и явно видно что за исключение, системная ошибка или логическая…
                                                                                                          +1
                                                                                                          А вы не там производительность ищете.

                                                                                                          Сейчас веб-проекты обладают достаточно сложной логикой и постоянно меняются, а разрабатывать их нужно быстро. Код должен быть элементарным для чтения и писаться просто, иначе не успеете никуда. А это значит, что в ход идут большиииие такие ООП библиотеки для работы с БД, примитивная модель MVC обрастает большой обвязкой, и всё для того, чтобы выдачу страницу пользователю можно было описать в несколько строчек. Если здесь не использовать эксепшены, то код будет просто длиннее в ненужном месте.

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

                                                                                                          И если в таких проектах использовать дедовские методы тюнинга производительности вроде хаков, безумных оптимизаций трёх алгоритмов в один и экономии битов и скорости выполнения скрипта, то следующий за вами программист просто не сможет в указанный срок изменить функционал — он должен будет в вашем оптимизированном коде разбираться.
                                                                                                            0
                                                                                                            если исключения используются как хак, а не по прямому назначению, программисту (который «не в теме») понадобиться много времени, чтобы понять, что вообще происходит. ну и отладка такого кода, извините, гемор в чистом виде. так что производительность программистов страдает ещё больше чем проивзодительность кода.
                                                                                                              0
                                                                                                              Как «хак» они используются только для редиректа. Всё остальное в порядке общепринятых понятий.
                                                                                                                +1
                                                                                                                $this->redirect('http:/'. '/example.com');

                                                                                                                Программисту, который описывает обработку запросов, даже не нужно знать, что там дальше происходит.
                                                                                            0
                                                                                            symfony core team одобряет этот пост.
                                                                                              0
                                                                                              а если я предложу по каждому пункту код, не исопльзующий исключения и не уступающий по всем параметрам и даже превосходящий? Откажетесь ли от старой схемы в пользу «безисключенческой»?
                                                                                                0
                                                                                                Это будет долгая дискуссия. Давайте предложите. Но скорее всего нет, так как плюсы от его внедрения не покроют тот гемморой рефакторинга, и тот гемморой дальнейшего использования. Но вообще тема интересная.
                                                                                                0
                                                                                                Exception = исключительная ситуация. И использоваться он должен для сообщения об исключительніх ситуациях, а не для быстрого свертывания стека (хотя такая идея и заманчива — это нарушает суть исключений). В связи с этим, сомнительным кажется пункт 1 и совсем нехорошим — пункт 5.
                                                                                                • НЛО прилетело и опубликовало эту надпись здесь

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

                                                                                                  Самое читаемое