我将如何使用PHP异常来定义重定向? [英] How would I use PHP exceptions to define a redirect?

查看:86
本文介绍了我将如何使用PHP异常来定义重定向?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直被教导,在编程中使用异常可以使错误处理从抛出错误的对象中抽象出来。查看 PHP手册,看来PHP具有Exception类,并且一个ErrorException类,指示并非所有异常都是错误。因此,我想用它们来帮助页面重定向。



我想要一个硬重定向,该重定向仅发送标头而不发送页面内容。触发此操作的最佳方法是什么?假设我有一个 Controller 类,具有一个 redirect()方法。



该方法应如下所示:

  class Controller {
公共函数redirect($ path) {
抛出new Exception($ path,301);
}
}

...

try {
$ controller-> redirect('http://domain.tld / redirected');
} catch(Exception $ e){
if($ e-> getCode()== 301){
标头('Location:'。$ e-> getMessage() );
}
}

或者像这样:

 类控制器{
公共功能重定向($ path){
标头('Location:'。$ path);
抛出新异常(页面正在重定向,301);
}
}

...

try {
$ controller-> redirect('http://domain.tld / redirected');
} catch(Exception $ e){
if($ e-> getCode()== 301){
//不输出任何内容
}
}

或者我应该创建一种新的异常类型吗?

 类RedirectException扩展了异常{
受保护的$ url;

公共功能__construct($ url){
parent :: __ construct(’重定向即将来临!’,301);
$ this-> url =(string)$ url;
}

公共函数getURL(){
return $ this-> url;
}
}

...

class Controller {
public function redirect($ path){
throw new RedirectException($ path);
}
}

...

try {
$ controller-> redirect('http://domain.tld / redirected');
} catch(RedirectException $ e){
标头(’Location:’。$ e-> getURL());
}

虽然我觉得所有这些都行得通,但没有一个人觉得我。最后一个似乎最接近,因为它清楚表明URL是必需的成员。但是,像这样的例外将仅用于一个目的。构建处理所有3XX,4XX和5XX状态代码的RequestException有意义吗?另外,该消息呢?

解决方案

我也一直在玩这个游戏。我会分享我对此事的想法。



理性



问:为什么有人会使用异​​常重定向根本,什么时候可以使用标头 die 语句轻松地做到这一点?



A:RFC2616所说的关于使用状态码301进行重定向:


除非请求方法是HEAD,响应的实体应包含简短的超文本注释,并带有指向新URI的超链接。


,实际上您需要一些代码才能正确实现重定向。最好一次实施一次,并使其易于重用。



问:但是,您可以很容易地实施重定向方法,不需要异常。



A:重定向时,如何知道杀死PHP脚本是安全的 ?也许堆栈中有一些代码正在等待您返回,因此它可以运行一些清理操作。通过抛出一个异常,堆栈中的代码可以捕获此异常并进行清理。



比较



您的示例# 1和#3实际上是相同的,不同之处在于在#1中您正在滥用泛型 Exception 类。异常的名称应说明其作用( RedirectException ),而不是属性( getCode()== 301 ),特别是因为没有定义异常中的代码应与HTTP状态代码匹配的任何地方。此外,想要在情况#1中捕获重定向的代码不能简单地执行 catch(RedirectException $ re),而是需要检查 getCode()的结果。这是不必要的开销。



#2和#3之间最重要的区别是您对接收异常的类有多少控制权。在#2中,您几乎说重定向即将到来,这正在发生,catch块没有可靠的方法来防止重定向发生。在#3中,您说我想重定向,除非您有更好的主意,但catch块将停止( catch)重定向,直到异常被进一步抛出堆栈时,它才会发生。 / p>

选择哪个



这取决于您要在堆栈中提供多少控制权。我个人认为堆栈中的代码应该能够取消重定向,这将使#3成为更好的选择。一个典型的用例是重定向到登录页面:设想一种方法,该方法将对当前用户执行某些操作,或者如果没有人登录,则重定向到登录页面。可以从未登录的页面调用该方法。要求用户已登录,但如果有的话,将提供其他功能。仅仅捕获一个异常比围绕该方法编写代码要干净得多,仅仅是为了检查用户是否实际登录。



某些程序员可能会选择#2,因为他们认为如果某些代码启动了重定向,则期望该重定向将实际发生。允许拦截重定向并执行其他操作将使框架的可预测性降低。但是,我倾向于认为这就是例外。除非某些代码具有处理异常的方法,否则将发生与异常关联的操作。此操作通常是显示错误消息,但也可以是其他内容,例如重定向。



#3的示例实现



 类RedirectException扩展了异常{
const PERMANENT = 301;
const FOUND = 302;
const SEE_OTHER = 303;
const PROXY = 305;
const TEMPORARY = 307;

私人静态$ messages = array(
301 =>'永久移动',
302 =>'已找到',
303 =>'参见其他,
305 =>使用代理,
307 =>临时重定向,
);

受保护的$ url;

公共功能__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;
}

公共函数getURL(){
return $ this-> url;
}

公共函数run(){
header(’Location:’。$ this-> url,true,$ this-> getCode());
}
}



结论



示例#1和#3几乎相同,但是#3是更好的设计。 #2和#3都是很好的解决方案,具体取决于您的要求。 Example#2将允许堆栈中的代码对重定向做出反应,但无法防止这种情况的发生。例#3还将允许堆栈中的代码作出反应,但也将启用相同的代码以防止发生重定向。


I've always been taught that using exceptions in programming allowed for error handling to be abstracted from the objects that throw the errors. Looking at the PHP manual, it seems that PHP has an Exception class and an ErrorException class, indicating that not all exceptions have to be errors. So, I'd like to use them to help with page redirects.

I want to have a hard redirect that will send only a header and no page content. What would be the best way to trigger this? Let's say I have a Controller class with a redirect() method.

Should that method look like this:

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());
    }
}

Or like this:

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
    }
}

Or should I create a new type of exception like this:

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());
}

While I feel like all of these would work, none of them feel right to me. The last seems the closest since it makes it clear that the URL is a required member. However, an exception like that would serve only one purpose. Would it make more sense to build a RequestException that handles all 3XX, 4XX, and 5XX status codes? Plus, what about the message? Does that just become extraneous information at this point?

解决方案

I've been playing around with this myself too. I'll share my thoughts on the matter.

Rationale

Q: Why would someone use exceptions to redirect at all, when you can do it just as easy using a header and die statement?

A: RFC2616 has this to say about redirecting with status code 301:

Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).

Because of this, you actually need some code to implement a redirect correctly. It is better to implement this once and make it easily accessible for reuse.

Q: But then you could just as easy implement a redirect method, you wouldn't need an Exception.

A: When you redirect, how can you know it is "safe" to kill the PHP script with a die? Maybe there is some code down the stack that is waiting for you to return, so it can run some cleanup operations. By throwing an Exception, code down the stack can catch this exception and cleanup.

Comparison

Your example #1 and #3 are actually the same, with the difference that in #1 you are abusing the generic Exception class. The name of the exception should say something about what it does (RedirectException), not an attribute (getCode() == 301), especially because nowhere is defined that the code in an exception should match the HTTP Status code. Additionally, code wanting to catch redirects in situation #1 cannot simply do catch (RedirectException $re) but will need to check the result of getCode(). This is unnecessary overhead.

The most important difference between #2 and #3 is how much control you give to the classes receiving the exception. In #2 you pretty much say "A redirect is coming, this is happening", the catch block has no reliable way to prevent the redirect from happening. While in #3 you say "I want to redirect, unless you have some better idea", a catch block will stop ("catch") the redirect, and it won't happen until the exception is thrown further down the stack.

Which to choose

It depends how much control you want to give code down the stack. I personally think that code down the stack should be able to cancel a redirect, which would make #3 a better choice. A typical use case for this is a redirect to a login page: Imagine a method that will do some operation for the current user, or redirect to the login page if nobody is logged in. This method may be called from a page that does not require that a user is logged in, but will give extra functionality if there is. Just catching an exception is a lot cleaner than writing code around the method, just to check if a user is actually logged in.

Some programmers might pick #2, because they think that if some code initiates a redirect, it expects that this redirect will actually happen. Allowing to intercept the redirect and do something else will make the framework less predictable. However, I tend to think that this is what exceptions are about; unless some code has a way to handle the exception, the operation associated with the exception happens. This operation usually is the display of an error message, but it can be something else, like a redirect.

Example implementation of #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());
    }
}

Conclusion

Example #1 and #3 are almost the same, but #3 is better design. #2 and #3 are both good solutions, depending on what your requirements are. Example #2 will allow code down the stack to react to a redirect, but will not be able to prevent this from happening. Example #3 will also allow code down the stack to react, but it will also enable the same code to prevent the redirect from happening.

这篇关于我将如何使用PHP异常来定义重定向?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆