Magento 2 的请求处理流程

Magento 2 的入口是 index.php,有两个:

  • <your Magento install dir>/index.php
  • <your Magento install dir>/pub/index.php

简化后的入口像这样:

require __DIR__ . '/app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$app = $bootstrap->createApplication('Magento\Framework\App\Http');
$bootstrap->run($app);

第一句,就是将<your Magento install dir>/app/bootstrap.php 包含进来。这个bootstrap.php文件主要做了一件事情,就是将<your Magento install dir>/app/autoload.php<your Magento install dir>/app/functions.php包含进来。autoload.php负责了Magneto 系统中所有类的自动加载。functions.php负责翻译用的。

第二句,调用静态方法,返回实例给$bootstrap

第三句,调用$bootstrapcreateApplication方法,该方法调用 Object Manager 创建了Magento\Framework\App\Http 实例。

第四句,将上一步骤的实例传递给$bootstrap->run()

下面进入\Magento\Framework\App\Bootstrap -> run 看一看,简化后的方法如下:

public function run(\Magento\Framework\AppInterface $application)
{
    //PART-2-1
    $this->initErrorHandler();
    $this->initObjectManager();
    $this->assertMaintenance();
    $this->assertInstalled();
    //PART-2-2
    $response = $application->launch();
    //PART-2-3
    $response->sendResponse();
}

PART-2-1 handles the sort of housekeeping bits. It initializes the
custom error handler, initializes the object manager, checks if our application is in maintenance mode, and checks that it is installed.

PART-2-2 部分调用Magento\Framework\App\Http -> launch() 暂时把$response看作是\Magento\Framework\App\Response\Http

PART-2-3 调用\Magento\Framework\App\Response\Http 实例的sendResponse方法,该方法在该类的父类\Magento\Framework\HTTP\PhpEnvironment\Response中。这个父类继承自\Zend\Http\PhpEnvironment\Response 不继续深入了。总之,到这个类为止,真正开始输出数据了。

总结一下,到目前为止的流程是:

  • index.php
  • \Magento\Framework\App\Bootstrap -> run
  • \Magento\Framework\App\Http -> launch
  • \Magento\Framework\App\Response\Http -> sendResponse

来看\Magento\Framework\App\Http -> launch
简化后是这样的:

public function launch()
{
    // PART-3-1
    $frontController = $this->_objectManager->get('Magento\Framework\App\FrontControllerInterface');

    // PART-3-2
    $result = $frontController->dispatch($this->_request);
    if ($result instanceof \Magento\Framework\Controller\ResultInterface) {
        // PART-3-3
        $result->renderResult($this->_response);
    } elseif ($result instanceof \Magento\Framework\App\Response\HttpInterface {
        $this->_response = $result;
    } else {
        throw new \InvalidArgumentException('Invalid return type');
    }

    // PART-3-4
    return $this->_response;
}

PART-3-1 创建一个实现\Magento\Framework\App\FrontControllerInterface接口的类的实例,具体是哪个接口,需要查看di.xml 文件。这个类一般是Magento\Framework\App\FrontController

PART-3-2 Magento\Framework\App\FrontController -> dispatch 具体的稍后再看。他的返回值一般$result\Magento\Framework\Controller\ResultInterface,一般是\Magento\Framework\View\Result\Page

PART-3-3 执行$result->renderResult($this->_response)这一步没有输出,只是对$this->_response的修改,之后 PART-3-4 把$this->response返回出去。

再总结一下,流程现在是:

  • index.php
  • \Magento\Framework\App\Bootstrap -> run
  • \Magento\Framework\App\Http -> launch
  • \Magento\Framework\App\FrontController -> dispatch
  • \Magento\Framework\View\Result\Page -> renderResult
  • \Magento\Framework\App\Response\Http -> sendResponse

\Magento\Framework\App\FrontController -> dispatch 需要更深入一下。简化后的方法是这样的:

public function dispatch(\Magento\Framework\App\RequestInterface $request)
{
    // PART-4-1
    while (!$request->isDispatched() && $routingCycleCounter++ < 100) {
        //PART-4-2
        foreach ($this->_routerList as $router) {
            try {
                //PART-4-3
                $actionInstance = $router->match($request);
                if ($actionInstance) {
                    $request->setDispatched(true);
                    //PART-4-4
                    $result = $actionInstance->dispatch($request);
                    break;
                }
            } catch (\Magento\Framework\Exception \NotFoundException $e) {}
        }
    }
    // PART-4-5
    return $result;
}

PART-4-1 和 PART-4-2 给每个$router 100次机会找出匹配项,避免死循环。
routers 有以下类型:

  • Magento\Framework\App\Router\Base
  • Magento\UrlRewrite\Controller\Router
  • Magento\Cms\Controller\Router
  • Magento\Framework\App\Router\DefaultRouter

他们都实现了\Magento\Framework\App\RouterInterface 以确保他们都实现了match方法。match返回的$actionInstance是一个实现了\Magento\Framework\App\ActionInterface接口的类的实例。

PART-4-4 $actionInstance(比如 controller)继承自\Magento\Framework\App\Action\Action 类,返回\Magento\Framework\App\ResponseInterface。在dispatch方法中会执行controllerexecute 方法。

流程现在变成:

  • index.php
  • \Magento\Framework\App\Bootstrap -> run
  • \Magento\Framework\App\Http -> launch
  • \Magento\Framework\App\FrontController -> dispatch
  • \Magento\Framework\App\Router\Base -> match
  • \Magento\Framework\App\Action\Action -> dispatch
  • \Magento\Framework\View\Result\Page -> renderResult
  • \Magento\Framework\App\Response\Http -> sendResponse

总结,对前端开发者来说,controller 返回 Page 类型的对象后,会自动调用该page的 renderResult 方法。
Page and Layout is where all the theme translations, layout, and template loading are triggering

参考文档

Magento 2 Developer’s Guide by Branko Ajzele
Routing in Magento 2
Request Flow in Magento 2

发表评论

电子邮件地址不会被公开。 必填项已用*标注