Я тоже играл с этим.Я поделюсь своими мыслями по этому вопросу.
Обоснование
В: Почему кто-то вообще использует исключения для перенаправления, когда вы можете сделать это так же просто, используя header
и die
оператор?
A: RFC2616 имеет это сказать о перенаправлении с кодом состояния 301:
Если метод запроса не был HEAD, объект ответа ДОЛЖЕН содержать короткий гипертекстобратите внимание с гиперссылкой на новые URI.
Из-за этого вам действительно нужен код для правильной реализации перенаправления.Лучше реализовать это один раз и сделать его легко доступным для повторного использования.
В: Но тогда вы могли бы так же легко реализовать метод 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 также позволит коду реагировать в стеке, но также активирует тот же код для предотвращения перенаправления.