magento是基于zend_framework开发的电子商务系统,在很多方面于zf都相似,下面我们来看下magento 路由和动作分发的过程
1、所有的请求都需经过index.php入口文件,在该文件中通过Mage::run($mageRunCode, $mageRunType);初始化APP,那么是如何初始化的呢,
在Mage类中有这么一段
/**
* Get initialized application object.
*
* @param string $code
* @param string $type
* @param string|array $options
* @return Mage_Core_Model_App
*/
public static function app($code = '', $type = 'store', $options = array())
{
if (null === self::$_app) {
self::$_app = new Mage_Core_Model_App();//APP就是Mage_Core_Model_App类的一个对象
self::setRoot();
self::$_events = new Varien_Event_Collection();
self::_setIsInstalled($options);
self::_setConfigModel($options);
Varien_Profiler::start('self::app::init');
self::$_app->init($code, $type, $options);//初始化APP
Varien_Profiler::stop('self::app::init');
self::$_app->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS);
}
return self::$_app;
}
在APP的init过程中包括环境、配置、缓存的信息的初始化
2、调用APP类的run方法执行应用
public function run($params)
{
$options = isset($params['options']) ? $params['options'] : array();
$this->baseInit($options);
Mage::register('application_params', $params);
if ($this->_cache->processRequest()) {
$this->getResponse()->sendResponse();
} else {
$this->_initModules();
$this->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS);
if ($this->_config->isLocalConfigLoaded()) {
$scopeCode = isset($params['scope_code']) ? $params['scope_code'] : '';
$scopeType = isset($params['scope_type']) ? $params['scope_type'] : 'store';
$this->_initCurrentStore($scopeCode, $scopeType);
$this->_initRequest();
Mage_Core_Model_Resource_Setup::applyAllDataUpdates();
}
$this->getFrontController()->dispatch();
}
return $this;
}
在这步中有三个关键的地方:
a.根据请求的url判断是否存在缓存,如果存在缓存,直接输出缓存
b.初始化request对象
c.初始化前端控制器,然后分发动作
$this->getFrontController()->dispatch();
3、初始化前端控制器 $this->getFrontController()
这个方法会调用Mage_Core_Controller_Varien_Front类的init()方法来初始化前端控制器,前端控制器只能被初始化一次
public function init()
{
Mage::dispatchEvent('controller_front_init_before', array('front'=>$this));
$routersInfo = Mage::app()->getStore()->getConfig(self::XML_STORE_ROUTERS_PATH);
Varien_Profiler::start('mage::app::init_front_controller::collect_routers');
foreach ($routersInfo as $routerCode => $routerInfo) {
if (isset($routerInfo['disabled']) && $routerInfo['disabled']) {
continue;
}
if (isset($routerInfo['class'])) {
$router = new $routerInfo['class'];
if (isset($routerInfo['area'])) {
$router->collectRoutes($routerInfo['area'], $routerCode);
}
$this->addRouter($routerCode, $router);
}
}
Varien_Profiler::stop('mage::app::init_front_controller::collect_routers');
Mage::dispatchEvent('controller_front_init_routers', array('front'=>$this));
// Add default router at the last
$default = new Mage_Core_Controller_Varien_Router_Default();
$this->addRouter('default', $default);
return $this;
}
初始化前端控制器主要是初始化路由,添加路由的方法是$this->addRouter($routerCode, $router); ,这里的路由包括两部分
a、添加默认路由$this->addRouter('default', $default);
b、添加配置路由$routersInfo = Mage::app()->getStore()->getConfig(self::XML_STORE_ROUTERS_PATH);,这部分的路由是通过配置文件config.xml配置而获得的,在magento系统中默认的路由配置是在Mage/core/etc/config.xml文件中,在default配置段下有这样的配置
<default>
<web>
<routers>
<admin>
<area>admin</area>
<class>Mage_Core_Controller_Varien_Router_Admin</class>
</admin>
<standard>
<area>frontend</area>
<class>Mage_Core_Controller_Varien_Router_Standard</class>
</standard>
</routers>
</web>
</default>
当我们需要对url重写时可以通过向配置文件中添加路由的方式来达到各种需求
4、前端控制器初始化完成后就会调用dispatch() 方法来执行控制器动作的分发
public function dispatch()
{
$request = $this->getRequest();
// If pre-configured, check equality of base URL and requested URL
$this->_checkBaseUrl($request);
$request->setPathInfo()->setDispatched(false);
if (!$request->isStraight()) {
Varien_Profiler::start('mage::dispatch::db_url_rewrite');
Mage::getModel('core/url_rewrite')->rewrite();
Varien_Profiler::stop('mage::dispatch::db_url_rewrite');
}
Varien_Profiler::start('mage::dispatch::config_url_rewrite');
$this->rewrite();
Varien_Profiler::stop('mage::dispatch::config_url_rewrite');
Varien_Profiler::start('mage::dispatch::routers_match');
$i = 0;
while (!$request->isDispatched() && $i++<100) {
foreach ($this->_routers as $router) {
if ($router->match($this->getRequest())) {
break;
}
}
}
Varien_Profiler::stop('mage::dispatch::routers_match');
if ($i>100) {
Mage::throwException('Front controller reached 100 router match iterations');
}
// This event gives possibility to launch something before sending output (allow cookie setting)
Mage::dispatchEvent('controller_front_send_response_before', array('front'=>$this));
Varien_Profiler::start('mage::app::dispatch::send_response');
$this->getResponse()->sendResponse();
Varien_Profiler::stop('mage::app::dispatch::send_response');
Mage::dispatchEvent('controller_front_send_response_after', array('front'=>$this));
return $this;
}
这个方法我们关注3部分:
a、Mage::getModel('core/url_rewrite')->rewrite(); 这个的作用是根据core_url_rewrite数据库表中存储的静态url重写信息对request对象做请求重写
b、$this->rewrite(); 这部分的作用是根据config.xml文件中的rewrite配置对URL重写,也就是控制器重写的功能
public function rewrite()
{
$request = $this->getRequest();
$config = Mage::getConfig()->getNode('global/rewrite');//获取config.xml中global/rewrite配置段的信息
if (!$config) {
return;
}
foreach ($config->children() as $rewrite) {
$from = (string)$rewrite->from;
$to = (string)$rewrite->to;
if (empty($from) || empty($to)) {
continue;
}
$from = $this->_processRewriteUrl($from);
$to = $this->_processRewriteUrl($to);
$pathInfo = preg_replace($from, $to, $request->getPathInfo());
if (isset($rewrite->complete)) {
$request->setPathInfo($pathInfo);
} else {
$request->rewritePathInfo($pathInfo);
}
}
}
下面是个配置的例子
<global>
<rewrite>
<corerewrite_tag_productList>
<from><![CDATA[#^/tag/product/list#]]></from>
<to>/corerewrite/tag/productList</to>
</corerewrite_tag_productList>
<corerewrite_tag_test>
<from><![CDATA[#^/test#]]></from><!--还支持正则-->
<to>/corerewrite/tag/test</to>
</corerewrite_tag_test>
</rewrite>
</global>
c、 $router->match($this->getRequest()执行路由配置循环直到找到一个router能match,当路由得到匹配时在Match方法中会得到一个控制器对象,从而完成路由分发
$controllerInstance = Mage::getControllerInstance($controllerClassName, $request, $front->getResponse());
接着会在match方法中调用$controllerInstance的dispatch()方法执行控制器动作分发
$controllerInstance->dispatch($action);
5、控制器动作分发
因为所有控制器都继承Mage_Core_Controller_Varien_Action类,所有执行控制器动作dispatch就是调用Mage_Core_Controller_Varien_Action类的dispatch()
public function dispatch($action)
{
try {
$actionMethodName = $this->getActionMethodName($action);
if (!is_callable(array($this, $actionMethodName))) {
$actionMethodName = 'norouteAction';
}
Varien_Profiler::start(self::PROFILER_KEY.'::predispatch');
$this->preDispatch();
Varien_Profiler::stop(self::PROFILER_KEY.'::predispatch');
if ($this->getRequest()->isDispatched()) {
/**
* preDispatch() didn't change the action, so we can continue
*/
if (!$this->getFlag('', self::FLAG_NO_DISPATCH)) {
$_profilerKey = self::PROFILER_KEY.'::'.$this->getFullActionName();
Varien_Profiler::start($_profilerKey);
$this->$actionMethodName();//执行动作
Varien_Profiler::stop($_profilerKey);
Varien_Profiler::start(self::PROFILER_KEY.'::postdispatch');
$this->postDispatch();
Varien_Profiler::stop(self::PROFILER_KEY.'::postdispatch');
}
}
}
catch (Mage_Core_Controller_Varien_Exception $e) {
// set prepared flags
foreach ($e->getResultFlags() as $flagData) {
list($action, $flag, $value) = $flagData;
$this->setFlag($action, $flag, $value);
}
// call forward, redirect or an action
list($method, $parameters) = $e->getResultCallback();
switch ($method) {
case Mage_Core_Controller_Varien_Exception::RESULT_REDIRECT:
list($path, $arguments) = $parameters;
$this->_redirect($path, $arguments);
break;
case Mage_Core_Controller_Varien_Exception::RESULT_FORWARD:
list($action, $controller, $module, $params) = $parameters;
$this->_forward($action, $controller, $module, $params);
break;
default:
$actionMethodName = $this->getActionMethodName($method);
$this->getRequest()->setActionName($method);
$this->$actionMethodName($method);
break;
}
}
}
在动作分发前会调用 $this->preDispatch();,这个方法中会调用一个 $this->_rewrite()方法,这个方法的作用是根据下面这样的xml配置对动作做rewrite
<global>
<routers>
<core_module>
<rewrite>
<core_controller>
<to>new_route/new_controller</to>
<override_actions>true</override_actions>
<actions>
<core_action><to>new_module/new_controller/new_action</core_action>
</actions>
<core_controller>
</rewrite>
</core_module>
</routers>
</global>
This will override:
1. core_module/core_controller/core_action to new_module/new_controller/new_action
2. all other actions of core_module/core_controller to new_module/new_controller
而动作分发就是通过 $this->$actionMethodName();方法完成的
到这里一个完整的请求过程就完成了