Magento代码请求流程

Magento Alan 10年前 (2014-06-23) 5720次浏览 0个评论 扫描二维码

在深入了解Magento的各组件之前,我们应当了解这里组件之间是如何交互的以及Magento是如何处理来自Web服务器的请求的。和其它PHP应用程序一样,Magento使用index.php作为入口文件,用于处理Mage.php引导类以及开启请求环节,主要有如下几步:

  1. Web服务器接到请求,magento通过调用引导文件Mage.php被实例化
  2. 初始化和实例化前端控制器,这控制器初始化过程中Magento搜索web路由并对它们进行实例化
  3. Magento在各路由器之前进行循环迭代并调用匹配方法,匹配方法用于处理URL以及生成对应的控制器和动作(action)
  4. Magento实例化匹配控制器并采取相应的动作

在这一过程中路由器(router)尤其重要,Router对象被前端控制器用来匹配请求的模块控制器URL(路由)和动作。Magento默认包含如下路由器:
Mage_Core_Controller_Varien_Router_Admin
Mage_Core_Controller_Varien_Router_Standard
Mage_Core_Controller_Varien_Router_Default
然后动作控制器会加载并渲染布局,进而加载相应的块文件、模型文件和模板文件。
下面我们来分析一下Magento是如何处理目录页的请求的,这里我们以http://localhost/catalog/category/view/id/10为例。Magento的URI由3部分组成-/FrontName/ControllerName/ActionName,也就是说我们刚刚所举的url可以分成如下部分:
FrontName: catalog
ControllerName: category
ActionName: view
如果查看Magento的路由器类,会看Mage_Core_Controller_
Varien_Router_Standard(app\code\core\Mage\Core\Controller\Varien\Router)这一匹配方法

public function match(Zend_Controller_Request_Http $request)
{
…
$path = trim($request->getPathInfo(), '/');
if ($path) {
$p = explode('/', $path);
} else {
$p = explode('/', $this->_getDefaultPath());
}
…
}

从上面的代码可以看出router首先将URI解析为一个数组,根据我们的示例URL,会得一个如下的数组

$p = Array
(
[0] => catalog
[1] => category
[2] => view
)

下面函数将会检测请求中是否包含模块名称,如果没有,请会根据数组的第一个元素来决定模块名称。如果无法获得模块名称,函数会返回false,这部分代码如下:

 // get module name

        if ($request->getModuleName()) {

            $module = $request->getModuleName();

        } else {

            if (!empty($p[0])) {

                $module = $p[0];

            } else {

                $module = $this->getFront()->getDefault('module');

                $request->setAlias(Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, '');

            }

        }

        if (!$module) {

            if (Mage::app()->getStore()->isAdmin()) {

                $module = 'admin';

            } else {

                return false;

            }

        }

下一步,匹配函数使用如下代码在现有的模块中进行循环来匹配controller和action:

foreach ($modules as $realModule) {

            $request->setRouteName($this->getRouteByFrontName($module));

            // get controller name

            if ($request->getControllerName()) {

                $controller = $request->getControllerName();

            } else {

                if (!empty($p[1])) {

                    $controller = $p[1];

                } else {

                    $controller = $front->getDefault('controller');

                    $request->setAlias(

                        Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS,

                        ltrim($request->getOriginalPathInfo(), '/')

                    );

                }

            }

            // get action name

            if (empty($action)) {

                if ($request->getActionName()) {

                    $action = $request->getActionName();

                } else {

                    $action = !empty($p[2]) ? $p[2] : $front->getDefault('action');

                }

            }

            //checking if this place should be secure

            $this->_checkShouldBeSecure($request, '/'.$module.'/'.$controller.'/'.$action);

            $controllerClassName = $this->_validateControllerClassName($realModule, $controller);

            if (!$controllerClassName) {

                continue;

            }

            // instantiate controller class

            $controllerInstance = Mage::getControllerInstance($controllerClassName, $request, $front->getResponse());

            if (!$controllerInstance->hasAction($action)) {

                continue;

            }

            $found = true;

            break;

        }

让我们来对这一长串代码进行分解,第一个循环查看请求中是否带有controller名称以及action名,Magento通过调用如下方法来匹配controller名:

$controllerClassName = $this->_validateControllerClassName($realModule, $controller);

该方法不仅会生成一个匹配类名而且会验证类别是否存在,我们例子应该会返回Mage_Catalog_CategoryController。
既然现在我们有一个有效的类名,就可以开始实例化我们的controller对象,您可能注意到至此我们还没有用action做任何事,这正是下面循环中要做的。新实例化的控制器带一个非常好用的方法hasAction(),其本质上是PHP中的is_callable()函数,用于检测我们当前的控制器是否拥有一个与action名相匹配的公共函数,本例中应为viewAction()。
能够使用这种精密的匹配进程以及foreach循环源自多个模块使用相同的FrontName:
Magento代码请求流程
http://localhost/catalog/category/view/id/10不是一个对用户友好的URL,幸好Magento拥有自己的rewrite系统,让我们可以使用http://localhost/books.html这样的链接。
我们再进一步的查看URL重写系统来看看Magento如何从URL别名中获取controller名和action名。在Varien/Front.php的控制器调度方法,Magento会调用:
Mage::getModel(‘core/url_rewrite’)->rewrite();
在查看rewrite方法的内部机制之前,我们来看看core/url_rewrite模型的结构:

Array (

	["url_rewrite_id"] => "10"

	["store_id"] => "1"

	["category_id"] => "10"

	["product_id"] => NULL

	["id_path"] => "category/10"

	["request_path"] => "books.html"

	["target_path"] => "catalog/category/view/id/10"

	["is_system"] => "1"

	["options"] => NULL

	["description"] => NULL

)

可以看到重写模块是由多个属性组成,其中request_path和target_path两个具有特别的用途,简单的说rewrite模块会通过匹配target_path的值来修改请求对象的路径信息。
下一节让我们来看看Magento版本的MVC

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址