Как бы я использовал исключения PHP для определения перенаправления? - PullRequest
4 голосов
/ 17 августа 2011

Меня всегда учили, что использование исключений в программировании позволяет абстрагировать ошибки от объектов, которые генерируют ошибки.Глядя на руководство по 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?Плюс, как насчет сообщения?Это просто становится посторонней информацией в этот момент?

Ответы [ 2 ]

4 голосов
/ 25 мая 2014

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

Обоснование

В: Почему кто-то вообще использует исключения для перенаправления, когда вы можете сделать это так же просто, используя 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 также позволит коду реагировать в стеке, но также активирует тот же код для предотвращения перенаправления.

0 голосов
/ 17 августа 2011

Первый метод является лучшим из 3 методов. Пост, сделанный Марком Б., имеет очень правильное значение, но у вас, возможно, уже есть решение для этого, которое не упомянуто в коде.

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

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

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

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