Phalcon启动之后,会通过router路由器将URL解析,然后传递给dispatcher分发器,分发器会找到对应的Module/Controller/Action并执行,执行中会输出到view视图发送给客户端。
简单情况下分发器不需要特殊的配置,router的解析已经足够,一般来讲,只有在判断用户权限的时候才需要特殊处理
$di->set('dispatcher', function () {
$dispatcher = new \Phalcon\Mvc\Dispatcher();
//Attach a event listener to the dispatcher
$eventManager = new \Phalcon\Events\Manager();
$eventManager->attach('dispatch:beforeDispatch', new \SecurityPlugin(__CLASS__));
$dispatcher->setEventsManager($eventManager);
$dispatcher->setDefaultNamespace('Multiple\Entrance\Controllers\\');
return $dispatcher;
});
\Phalcon\Events\Manager类通过attach函数的第一个参数定义流程控制类的某个特殊事件,通过第二个参数定义对应的行为,然后在流程控制对象中使用setEventsManager函数挂上这个事件控制器类,就可以在特定的事件发生时,调用事件控制器定义的行为,如果行为执行正常会返回程序主线,如果行为执行之后跳出了流程,就不会返回了。
常见的被视为跳出流程的标志有:1)返回false;2)重定向;3)强行中止;4)抛出异常
如果返回null或者true则会返回主线继续执行。
也就是说attach函数的第2个参数其实才是主要问题。实际上笔者的习惯是把用户权限判定放在那里。
对于分发器而言,内建有一些事件定义。对于其他的类,也有事件定义,可以参考各个类的官方手册。
Event Name | Triggered | Can stop operation? |
---|---|---|
beforeDispatchLoop | Triggered before enter in the dispatch loop. At this point the dispatcher don’t know if the controller or the actions to be executed exist. The Dispatcher only knows the information passed by the Router. | Yes |
beforeDispatch | Triggered after enter in the dispatch loop. At this point the dispatcher don’t know if the controller or the actions to be executed exist. The Dispatcher only knows the information passed by the Router. | Yes |
beforeExecuteRoute | Triggered before execute the controller/action method. At this point the dispatcher has been initialized the controller and know if the action exist. | Yes |
afterExecuteRoute | Triggered after execute the controller/action method. As operation cannot be stopped, only use this event to make clean up after execute the action | No |
beforeNotFoundAction | Triggered when the action was not found in the controller | Yes |
beforeException | Triggered before the dispatcher throws any exception | Yes |
afterDispatch | Triggered after execute the controller/action method. As operation cannot be stopped, only use this event to make clean up after execute the action | Yes |
afterDispatchLoop | Triggered after exit the dispatch loop | No |
来看一个稍微复杂点的事件管理器,它可以在找不到对应的Controller/Action时,做出合适的处理,不让用户看到难看的404错误
$di->setShared('dispatcher', function() {
//Create/Get an EventManager
$eventsManager = new Phalcon\Events\Manager();
//Attach a listener
$eventsManager->attach("dispatch", function($event, $dispatcher, $exception) {
//The controller exists but the action not
if ($event->getType() == 'beforeNotFoundAction') {
$dispatcher->forward(array(
'controller' => 'index',
'action' => 'show404'
));
return false;
}
//Alternative way, controller or action doesn't exist
if ($event->getType() == 'beforeException') {
switch ($exception->getCode()) {
case Phalcon\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
case Phalcon\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
$dispatcher->forward(array(
'controller' => 'index',
'action' => 'show404'
));
return false;
}
}
});
$dispatcher = new Phalcon\Mvc\Dispatcher();
//Bind the EventsManager to the dispatcher
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
});
分发器的另外一个用途是让我们从一个Controller/Action跳转到另一个Controller/Action
dispatcher->forward(array(
"controller" => "post",
"action" => "index"
));
}
}
另外在多Module程序中,forward不能跳到另一个Module,而HTTP重定向可以。这个时候使用下面的代码实现跳转
$this->response->redirect('entrance/index/login');
forward还有一些用法
// Forward flow to another action in the current controller
$this->dispatcher->forward(array(
"action" => "search"
));
// Forward flow to another action in the current controller
// passing parameters
$this->dispatcher->forward(array(
"action" => "search",
"params" => array(1, 2, 3)
));
// Forward flow to another action in the current controller
// passing parameters
$this->dispatcher->forward(array(
"action" => "search",
"params" => array(1, 2, 3)
));
// Get the post's title passed in the URL as parameter
$title = $this->dispatcher->getParam("title");
// Get the post's year passed in the URL as parameter
// also filtering it
$year = $this->dispatcher->getParam("year", "int");