magento APP、前端控制器、路由、控制器、动作分发过程

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();方法完成的


到这里一个完整的请求过程就完成了


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值