Intereting Posts
Функции для округления и округления фракций с точностью в PHP, любой лучший способ сделать это? Как правильно шифровать данные с надлежащей аутентификацией с использованием AES-256-CBC в php? ЛАМПА: Как создать .Zip больших файлов для пользователя «на лету», без разбиения диска / процессора Как изменить кодировку символов PDO / SQLite-соединения в PHP? Каков наилучший способ получить общее количество записей в таблице mysql с php? логика для разбивки на страницы, как Google Доступ запрещен для пользователя '@' localhost '(с использованием пароля: НЕТ) Сделайте акцию после входа в систему PHP: Самый безопасный (дешифруемый) метод шифрования? Как продемонстрировать SQL-инъекцию второго порядка? Как написать подробную информацию в файл? FatalErrorException: Ошибка: вызов функции-члена имеет () для не-объекта Отправить запрос на отправку AJAX, используя только PHP Как мне повторить попытку PHP flock () в течение определенного периода времени? Отключение строгих стандартов в PHP 5.4

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

Меня всегда учили, что использование исключений в программировании допускает абстрагирование ошибок от объектов, которые вызывают ошибки. Глядя на руководство PHP , кажется, что PHP имеет класс Exception и класс ErrorException, указывая, что не все исключения должны быть ошибками. Поэтому я хотел бы использовать их, чтобы помочь с переадресацией страниц.

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

Должен ли этот метод выглядеть так:

 class Controller { public function redirect($path) { throw new Exception($path, 301); } } ... try { $controller->redirect('http://domain.tld/redirected'); } catch (Exception $e) { if ($e->getCode() == 301) { header('Location: ' . $e->getMessage()); } } 

Или вот так:

 class Controller { public function redirect($path) { header('Location: ' . $path); throw new Exception('The page is being redirected', 301); } } ... try { $controller->redirect('http://domain.tld/redirected'); } catch (Exception $e) { if ($e->getCode() == 301) { // Output nothing } } 

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

 class RedirectException extends Exception { protected $url; public function __construct($url) { parent::__construct('The redirects are coming!', 301); $this->url = (string)$url; } public function getURL() { return $this->url; } } ... class Controller { public function redirect($path) { throw new RedirectException($path); } } ... try { $controller->redirect('http://domain.tld/redirected'); } catch (RedirectException $e) { header('Location: ' . $e->getURL()); } 

Хотя я чувствую, что все это будет работать, никто из них не чувствует себя хорошо. Последнее кажется самым близким, поскольку он дает понять, что URL-адрес является обязательным элементом. Однако исключение, подобное этому, будет служить только одной цели. Было бы разумнее создать RequestException, который обрабатывает все коды состояния 3XX, 4XX и 5XX? Плюс, как насчет сообщения? Это просто становится посторонней информацией на данный момент?

Я тоже с этим занимаюсь. Я поделюсь своими мыслями по этому вопросу.

обоснование

В: Почему кто-то использует исключения для перенаправления вообще, когда вы можете сделать это так же просто, используя инструкцию header и die ?

A: RFC2616 имеет это сказать о перенаправлении с кодом статуса 301:

Если метод запроса не был HEAD, сущность ответа СЛЕДУЕТ содержать короткую гипертекстовую ноту с гиперссылкой на новый URI (ы).

Из-за этого вам действительно нужен код для правильной реализации перенаправления. Лучше реализовать это один раз и сделать его доступным для повторного использования.

Q: Но тогда вы могли бы просто реализовать метод redirect , вам не понадобилось бы исключение.

A: Когда вы перенаправляете, как вы можете знать, что «безопасно» убить скрипт PHP с помощью die ? Возможно, есть какой-то код в стек, который ждет вас, чтобы он мог вернуться, поэтому он может запускать некоторые операции очистки. Выбрасывая исключение, код вниз по стеку может поймать это исключение и очистку.

сравнение

Ваш пример # 1 и # 3 на самом деле тот же, с той разницей, что в # 1 вы злоупотребляете общим классом Exception . Имя исключения должно сказать что-то о том, что он делает ( RedirectException ), а не атрибут ( getCode() == 301 ), тем более, что нигде не определено, что код в исключении должен соответствовать коду состояния HTTP. Кроме того, код, который хочет перехватить переадресацию в ситуации # 1, не может просто сделать catch (RedirectException $re) но ему нужно будет проверить результат getCode() . Это лишние накладные расходы.

Наиболее важным отличием между # 2 и # 3 является то, какой контроль вы предоставляете классам, получающим исключение. В # 2 вы в значительной степени говорите: «Приходит перенаправление, это происходит», блок catch не имеет надежного способа предотвратить перенаправление. В то время как в # 3 вы говорите: «Я хочу перенаправить, если у вас нет лучшей идеи», блок catch остановит («поймать») перенаправление, и это произойдет не до тех пор, пока исключение не будет сброшено дальше по стеку.

Что выбрать

Это зависит от того, сколько элементов управления вы хотите передать в стек. Я лично считаю, что код в стеке должен иметь возможность отменить перенаправление, что сделало бы # 3 лучшим выбором. Типичным примером использования этого является перенаправление на страницу входа в систему: Представьте метод, который будет выполнять некоторую операцию для текущего пользователя, или перенаправить на страницу входа в систему, если никто не войдет в систему. Этот метод можно вызвать со страницы, которая не требуют, чтобы пользователь вошел в систему, но предоставит дополнительную функциональность, если есть. Просто поймать исключение намного проще, чем писать код вокруг метода, просто чтобы проверить, действительно ли пользователь вошел в систему.

Некоторые программисты могут выбрать # 2, потому что они думают, что если какой-то код инициирует перенаправление, он ожидает, что это перенаправление действительно произойдет. Позволяя перехватывать перенаправление и делать что-то еще, система будет менее предсказуемой. Однако я склонен думать, что это то, о чем говорят исключения; если у некоторого кода есть способ справиться с этим исключением, происходит операция, связанная с исключением. Эта операция обычно является отображением сообщения об ошибке, но это может быть что-то другое, например, перенаправление.

Пример реализации # 3

 class RedirectException extends Exception { const PERMANENT = 301; const FOUND = 302; const SEE_OTHER = 303; const PROXY = 305; const TEMPORARY = 307; private static $messages = array( 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 305 => 'Use Proxy', 307 => 'Temporary Redirect', ); protected $url; public function __construct($url, $code = 301, $message = NULL) { parent::__construct($message ? (string)$message : static::$messages[$code], (int)$code ); if (strpos($url, '/') === 0) { $this->url = static::getBaseURL() . $this->url; } $this->url = (string)$url; } public function getURL() { return $this->url; } public function run() { header('Location: ' . $this->url, true, $this->getCode()); } } 

Вывод

Пример №1 и №3 практически одинаковы, но # 3 – лучший дизайн. # 2 и # 3 – оба хороших решения, в зависимости от ваших требований. Пример №2 позволит коду стека реагировать на перенаправление, но не сможет предотвратить это. Пример №3 также позволяет коду стека реагировать, но он также позволит тому же коду предотвратить переадресацию.

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

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

Примечание. Поскольку вы должны умереть после перенаправления, вы можете увидеть, почему примеры 2 и 3 странны.

Объяснение: Если вы запрашиваете страницу, первое, что сервер отправляет вам, это заголовок. Заголовок содержит информацию о веб-странице, которую вы собираетесь получить. Первый оператор в вашем php-коде, который генерирует вывод или первый HTML-код на вашем сайте, запускает заголовок, который нужно отправить. Если вы помещаете оператор определения заголовка в свой код, заголовок изменяется, чтобы сообщить браузеру немедленно перенаправить указанный URL. Так что почти все выполнение после инструкции заголовка бесполезно и пропущено.