Magento代码请求流程

Magento Alan 11年前 (2014-06-23) 6400次浏览 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)这一匹配方法

1
2
3
4
5
6
7
8
9
10
11
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,会得一个如下的数组

1
2
3
4
5
6
$p = Array
(
[0] => catalog
[1] => category
[2] => view
)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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模型的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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,您需要填写昵称和邮箱!

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