Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Stopping the forward flow and the redirection problem

I check permissions in onConstruct method and want to stop any action execution if access is denied, but to render standard layout as usual. If possible not redirecting to another page. Why, i write below. How to do the stop.

There is $this->response->redirect('authentication/accessdenied'); which i want to use to redirect to a page with user friendly information. If i must use response->redirect i've noticed a problem. The problem is after the execution of the redirect method i had been able to do some php instructions before redirect actually worked. In my case php die() function worked faster. Not sure whether this can cause some insesure stuff/instructions to be executed in this time gap.

I know there is _finish property in the dispatcher object. I understand i cannot change it to stop the controller flow

There is as i remember an beforeExecuteRoute what i couldn't use returning true or false to stop the flow or not, yet the method/event generally works.



4.6k
Accepted
answer
edited Feb '15

Ok, i've possibly found a good way to resolve the problem avoiding using the redirection. Yet i might not know about some things, but it looks like it works at the moment, and allows avoiding the use of redirect what was mentioned earlier .Somewhere int the onConstruct area or initialize someone can set up $this->accessDenied property to true if accesss is denied for a certain user. Oh, one thing yet: it all is in one central class all controllers extend.

public function beforeExecuteRoute($dispatcher)
{
    if ($this->accessDenied===true&&($this->dispatcher->getControllerName()!="auth"||$this->dispatcher->getActionName()!="accessdenied")) {
        $this->dispatcher->forward(array(
                    'controller' => 'authentication',
                    'action' => 'accessdenied'
        ));
        return false;
    }    
}


40.7k
$this->response->redirect('authentication/accessdenied');
$this->response->send();
return false;

Nothing should be able to execute beyond that point. I recommend letting the application exit through the entire flow because later you might want to do cleanup and various things. I don't think that it should be insecure unless you put something weird in the Dispatcher or a Plugin.



4.6k

Thank you, dschissler, for your reply. I would like to admit, that in my circumstances my way, i've posted nearly at the same time with your post works for me very well (my application works in a certain way and it allows me using that beforeExecuteRoute example) and i don't have to redirect nothing - i stay on the same URL. Furthermore i see that no other action is invoked. At the moment solved.



40.7k

Ok you are using a forward. I use forwards and redirects both. Sometimes I want to forward but the destination is in a different module. In my own project I'm currently working on a way to maintain additional state variables on a redirect.

    /**
     * Forwards if the current module is the same as the destination module and redirects
     * if they are different.  In the case of a redirect it cleans up the path for the
     * default module and for index/index and :controller/index
     *
     * @param string $moduleDestination
     * @param string $controller
     * @param string $action
     * @param \Phalcon\Mvc\Dispatcher $dispatcher
     * @return boolean
     */
    private function forwardModuleSafe($dispatcher, $moduleDestination, $controller, $action)
    {
        $moduleCurrent = $this->router->getModuleName();
        $moduleDefault = $this->router->getDefaultModule();

        if ($moduleCurrent == $moduleDestination) {
            $dispatcher->forward([
                'controller' => $controller,
                'action' => $action
            ]);
        } else {
            $pathArr = [];
            if ($moduleDestination != $moduleDefault) {
                $pathArr[] = $moduleDestination;
            }

            if ($controller == 'index' && $action == 'index') {
                // Do nothing to prevent ugly index/index
            } else if ($action == 'index') {
                $pathArr[] = $controller;
            } else {
                $pathArr[] = $controller;
                $pathArr[] = $action;
            }
            $path = implode('/', $pathArr);

            // Fixed in Phalcon 2.0
            // Phalcon Issue: https://github.com/phalcon/cphalcon/issues/3073
            // Phalcon Issue: https://github.com/phalcon/cphalcon/issues/3108
            if ($this->getDI()->has('view')) {
                $this->getDI()->getShared('view')->disable();
            }

            $this->response->redirect($path);
            $this->response->send();
        }

        return false;
    }

Also on redirecting and forwarding http://stackoverflow.com/questions/27900017/whats-the-difference-between-redirect-and-dispatch-in-phalcon



4.6k

I'll have to rethink it all. I am sure all the information here is very useful.

I had similar issue as well. I banged my head on this one couple of times.

The solution is pretty simple though.

The best approach is to use dispatcher forwards. If you really need to update URL bar in user's browser, well then you need HTTP header redirect.

Instead of just doing:

                $this->response->redirect(['for' => 'myNamedRoute']);

This works much faster:

                $this->response->redirect(['for' => 'myNamedRoute']);
                //Change HTTP response code as per standards
                $this->response->setStatusCode(303); //This is really optional
                //Make sure that code below does not get executed when we redirect, the other way we get SLOW redirects
                exit($this->response->send());


40.7k

exit($this->response->send());

I think that it is better to return false so that normal cleanup can occur.

$this->response->send();
return false;
edited Jan '16

Well, in my case that takes ~1000 ms to complete, while forcelly stopping flow with exit(); after output headers are flushed takes only ~20 ms. It is clear that returing false; or simple:

return;

will not stop the execution. After you send Location HTTP header field, client is not suppose to do any request to that resource anymore, but it is being sent to another resource. PHP's gabage collector will trash the memory contents anyway...