We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Dispatcher::forward() behavior

Why Dispatcher::forward() does not invoke dispatcher loop or part of it? And what exactley happens when forward() invoked? Thanks.

As far as i understood how the dispatcher works, Dispatcher::forward() wont invoke the loop, it just will forward to another controller if the dispatcher is already looping.

Its Behaviour is to forward the current http request from on controller, to another controller.

Can you give a code snippet of what you are doing ?

  1. what happens with views that exists in controller/action what was forwarded to?
  2. what happens with params that was argued forward() for dispatcher?
  3. does beforeExecuteRoute and afterExecuteRoute of controllers invoked in particular forwarded controller/action object instance?
  4. does events of Manager fired? what ones?

On all of them i can give an answer by myself (after day of tests%)) not fully, but can. And just for clear and googles I wish to see some internal behavior explanations. That would be better than mine. Cause I can't answer WHY it's works exactly that way. No time to read C :(



98.9k
Accepted
answer

Everything starts in the router:

The router parses the URI according to the one passed to it, ($_GET['_url'], $_SERVER['REQUEST_URI'], others) After this handling the router pass the processed module/namespace/controller/action to the dispatcher:

$dispatcher->setControllerName($router->getControllerName());
$dispatcher->setActionName($router->getActionName());
$dispatcher->setParams($router->getParams());

When $dispatcher->dispatch() is executed, this starts a dispatch loop which basically uses a flag variable ($finished) in the dispatcher to know if there are remaining actions to dispatch:

$this->_finished = false;
while (!$this->_finished) {

   //execute actions

   $this->_finished = true;
}

The dispatch loop allows the developer to perform additional forwards, this means that $this->_finished is reset to false making the dispatcher loop again and execute the forwarded action.

$eventsManager->fire('dispatch:beforeDispatchLoop');

$this->_finished = false;
while (!$this->_finished) {

    $eventsManager->fire('dispatch:beforeDispatch');
    if ($this->_finished==false) {
        continue;
    }   

    $controllerName = $this->_controllerName;
    $actionName = $this->_actionName;
    $params = $this->_params;

    /**
     * Controllers are instantiated via the DI
     * This ensures that only one instance will be created
     * in the application;
     */
    $controller = $di->getShared($controllerName . 'Controller');

    if (!method_exists($controller, $action . 'Action')) {
        throw new Exception('404 not found');
    }

    $eventsManager->fire('dispatch:beforeExecuteAction');   
    if ($this->_finished==false) {
        continue;
    }   

    $controller->$action($params);

    $eventsManager->fire('dispatch:afterExecuteAction');
    if ($this->_finished==false) {
        continue;
    }   

    $eventsManager->fire('dispatch:afterDispatchLoop');
    if ($this->_finished==false) {
        continue;
    }   

    $this->_finished = true;
}

When you make a forward the internal $this->_finished is changed to false, this is detected and the dispatch loop iterates again:

public function forward($forward)
{
    if (isset($forward['controller']))  {
        $this->_controllerName = $forward['controller'];
    }
    if (isset($forward['action']))  {
        $this->_actionName = $forward['action'];
    }
    $this->_finished = false;
}

Very usefull informations. Thanks phalcon.

Thanks, realy usefull. One more question. Why params as arguments of action can be changed only in beforeDispatchLoop? What is real behavior of params flow and structure after Application set it from Route?

i see. in 1.2.1 already done some stuff about this %)