Phalcon's Mvc structure and startup process (partial source code analysis)

Phalcon itself supports the creation of various forms of web application projects to deal with different scenarios, including mini-applications , single-module standard applications , and more complex multi-module applications

Create project

After the Phalcon environment configuration is installed, a standard Phalcon multi-module application can be generated through the command line

 phalcon project eva --type modules

The entry file is public/index.php, after simplification, a total of 5 lines, including the entire Phalcon startup process, the following will be explained in order

require __DIR__ . '/../config/services.php';
$application = new Phalcon\Mvc\Application(); $application->setDI($di); require __DIR__ . '/../config/modules.php'; echo $application->handle()->getContent();

DI registration stage

          All of Phalcon's component services are organized through DI (dependency injection) , which is the method used by most mainstream frameworks today. Through DI, you can flexibly control the services in the framework: which ones need to be enabled, which ones are not enabled, the internal details of components, etc., so Phalcon is a loosely coupled and replaceable framework, which can completely replace any component in MVC through DI.

require __DIR__ . '/../config/services.php';

Phalcon\Mvc\RouterThe three most basic components (Route), Phalcon\Mvc\Url(Url), and Phalcon\Session\Adapter\Files(Session) are registered by default in this file . At the same time, when MVC is started, there are still many services registered by default in DI, and all currently registered services can be obtained through DI:

$services = $application->getDI()->getServices();
foreach($services as $key => $service) { var_dump($key); var_dump(get_class($application->getDI()->get($key))); }

Printing sees that Phalcon has also registered the following services:

  • dispatcher :  Phalcon\Mvc\Dispatcher Distribute service, distribute the result of route hit to the corresponding Controller
  • modelsManager :  Phalcon\Mvc\Model\Manager Model management
  • modelsMetadata :  Phalcon\Mvc\Model\MetaData\Memory ORM table structure
  • response :  Phalcon\Http\Response response
  • cookies : Phalcon\Http\Response\Cookies Cookies
  • request :  Phalcon\Http\Request request
  • filter :  Phalcon\Filter Filter the data submitted by the user
  • escaper :  Phalcon\Escaper escape tool
  • security :  Phalcon\Security Password Hash, CSRF prevention, etc.
  • crypt :  Phalcon\Crypt encryption algorithm
  • annotations :  Phalcon\Annotations\Adapter\Memory Annotation Analysis
  • flash :  Phalcon\Flash\Direct prompt information output
  • flashSession :  Phalcon\Flash\Session The prompt information is delayed output through the Session
  • tag :  Phalcon\Tag Common Helper for View

   And each service can be replaced by DI. Next, instantiate a standard MVC application, and then inject our defined DI into it

   $application = new Phalcon\Mvc\Application();

   $application->setDI($di);

Module registration phase

  Like DI, Phalcon recommends registering all required modules by introducing a separate file:

  require __DIR__ . '/../config/modules.php';

 The content of this file is as follows

$application->registerModules(array(
'base' => array(
'className' => 'Cn\Liuxue\Site\Base\Module',
'path' => __DIR__ . '/../apps/base/Module.php'
),
//前台
'front' => array(
'className' => 'Cn\Liuxue\Site\Front\Module',
'path' => __DIR__ . '/../apps/www/Module.php'
),
//后台
'backend' => array(
'className' => 'Cn\Liuxue\Site\Backend\Module',
'path' => __DIR__ . '/../apps/admin/Module.php'
),
//CMS
'cms' => array(
'className' => 'Cn\Liuxue\Site\Cms\Module',
'path' => __DIR__ . '/../apps/cms/Module.php'
),
));

You can see that Phalcon's so-called module registration actually just tells the framework where the bootstrap file of the MVC module Module.phpis located and what the class name is.

MVC stage

$application->handle()是整个MVC的核心,这个函数中处理了路由、模块、分发等MVC的全部流程,处理过程中在关键位置会通过事件驱动触发一系列application:事件,方便外部注入逻辑,最终返回一个Phalcon\Http\Response。整个handle方法的过程并不复杂,下面按顺序介绍:
 

Basic inspection

Check DI first, if there is no DI injected, an error will be thrown

 

A dependency injection object is required to access internal services

 

Then start EventsManager from DI and trigger events through EventsManagerapplication:boot

routing phase

Next, enter the routing phase, get the routing service from DI router, pass the uri into the route and call the route's handle()method .

The handle method of the route is responsible for converting a uri into the corresponding Module, Controller, Action, etc. according to the route configuration. In this stage, it will check whether the route hits a certain module, and obtain the module name by Router->getModuleName()obtaining the module name.

If the module exists, enter the module startup phase, otherwise directly enter the distribution phase.

Did you notice that in Phalcon, the module startup is after the routing , which means that Phalcon's module function is relatively weak, we cannot register a global service in a module that is not started, or even simply call another module in the current module. An inactive module. This may be the biggest problem in the functional design of Phalcon modules. The solution is not within the scope of this article for the time being, and will be introduced in another article in the future.

module start

application:beforeStartModuleThe event is first fired when the module starts . After the event is triggered, check the correctness of the module, load the module bootstrap file according modules.phpto the definitions in className, pathetc., and call the methods that must exist in the module bootstrap file

  • Phalcon\Mvc\ModuleDefinitionInterface->registerAutoloaders ()
  • Phalcon\Mvc\ModuleDefinitionInterface->registerServices (Phalcon\DiInterface $dependencyInjector)

registerAutoloaders()Used to register namespaces within modules for autoloading. registerServices ()It is used to register the service in the module. In the official example, the service and template path are registerServices ()registered and defined , and the database connection service is registered and the connection information of the database is set.viewdb

After the module is started, the event is triggered  application:afterStartModuleand the distribution stage is entered.

Dispatch

The distribution process is Phalcon\Mvc\Dispatchercompleted by the (distributor), the so-called distribution, in Phalcon essentially, the distributor calls the corresponding Controller/Action according to the result of the route hit, and finally obtains the result returned by the Action.

Before the distribution starts, the View will be prepared first. Although the View is theoretically located in the last link of MVC, if there is any problem during the distribution process, it is usually necessary to display the problem, so the View must be started in advance in this link. Phalcon does not prepare the default View service, which needs to be injected from the outside. In the multi-module demo, the official recommendation for the injection of View is done in the module startup phase. If it is a single module application, it can be injected in the very first DI stage.

If there is always no View injection, an error will be thrown

Service 'view' was not found in the dependency injection container

Causes a direct interruption of the distribution process.

Distribution requires Dispatcher, and Dispatcher is also obtained from DI. Then copy all the parameters (NamespaceName / ModuleName / ControllerName / ActionName / Params) obtained in the router to the Dispatcher.

start()Before the distribution starts, the View's method is called . For details, please refer to the related documents of View. In fact, it Phalcon\Mvc\View->start()is a simple encapsulation of PHP's output buffer function ob_start. All output during the distribution process will be temporarily stored in the buffer.

Events are also fired before the distribution starts application:beforeHandleRequest.

The official start of the distribution will be called Phalcon\Mvc\Dispatcher->dispatch().

Distribution processing within the Dispatcher

After entering the Dispatcher, you will find that the Dispatcher further subdivides the entire distribution process, and will trigger a lot of distribution events in sequence during the distribution process, and you can use these distribution events for more detailed process control. Some events provide an interruptible mechanism to falseskip the dispatching process of the Dispatcher as long as it returns.

Since the distribution can be used Phalcon\Mvc\Dispatcher->forward()to achieve the reuse of Actions, the distribution will be implemented internally through a loop, and a global finishedflag will be detected to determine whether to continue the distribution. Distribution ends when:

  • Controller throws exception
  • forwardThe number of layers reaches the maximum (256 times)
  • All Actions are called

Rendering stage View Render

It will be triggered after the distribution is completed application:afterHandleRequest, and then Phalcon\Mvc\Dispatcher->getReturnedValue()the results returned by the distribution process are obtained and processed.

Since the logic of the Action is outside the framework, the return value of the Action is unpredictable, so here we Phalcon\Http\ResponseInterfacedifferentiate the processing according to whether the return value implements the interface.

When Action returns a non- Phalcon\Http\ResponseInterfacetype

At this time, the return value is considered invalid, and the View itself reschedules the Render process, which will trigger application:viewRenderthe event and obtain the ControllerName / ActionName / Params from the Dispatcher as Phalcon\Mvc\View->render()the entry parameters.

After Render is completed, call to Phalcon\Mvc\View->finish()end the receiving of the buffer.

Next, get the response service from DI, and put the Phalcon\Mvc\View->getContent()obtained content into the response.

When Action returns a Phalcon\Http\ResponseInterfacetype

At this time, the Response returned by the Action will be used as the final response, and a new Response will not be rebuilt.

return response

Through the previous process, no matter how many branches are experienced in the middle, it will eventually be aggregated into a unique response. This will trigger application:beforeSendResponseand call

  • Phalcon\Http\Response->sendHeaders()
  • Phalcon\Http\Response->sendCookies()

Send the http header information first. At this point, Application->handle()the processing of the request is all over, and a Phalcon\Http\Responseresponse is returned to the outside world.

send response

After the HTTP header is sent, the content of the response is generally sent:

echo $application->handle()->getContent();

This is the complete MVC flow of Phalcon Framework.

Process control

To analyze the startup process of MVC, we undoubtedly hope to have a better grasp and control of the process. There are two methods:

custom start

According to the above process, we can actually implement $application->handle()->getContent()this process ourselves. The following is a simple alternative. The triggering of events is not considered in the code for the time being.

//Roter
$router = $di['router'];
$router->handle(); //Module handle $modules = $application->getModules(); $routeModule = $router->getModuleName(); if (isset($modules[$routeModule])) { $moduleClass = new $modules[$routeModule]['className'](); $moduleClass->registerAutoloaders(); $moduleClass->registerServices($di); } //dispatch $dispatcher = $di['dispatcher']; $dispatcher->setModuleName($router->getModuleName()); $dispatcher->setControllerName($router->getControllerName()); $dispatcher->setActionName($router->getActionName()); $dispatcher->setParams($router->getParams()); //view $view = $di['view']; $view->start(); $controller = $dispatcher->dispatch(); //Not able to call render in controller or else will repeat output $view->render( $dispatcher->getControllerName(), $dispatcher->getActionName(), $dispatcher->getParams() ); $view->finish(); $response = $di['response']; $response->setContent($view->getContent()); $response->sendHeaders(); echo $response->getContent(); 

Process checklist

In order to facilitate the search, the entire process is organized into a tree list as follows:

  • Initialize DI (config/services.php) $di = new FactoryDefault();
    • set route $di['router'] = function () {}
    • set URL $di['url'] = function () {}
    • Set Session $di['session'] = function () {}
  • Initialize Application (public/index.php)
    • Instantiate App $application = new Application();
    • inject DI $application->setDI($di);
    • Registering modules (config/modules.php) $application->registerModules()
  • Start Application (ext/mvc/application.c) $application->handle()
    • Check DI
    • E trigger event application:boot
    • route start $di['router']->handle()
    • Get the module name  $moduleName = $di['router']->getModuleName(), if not  $application->getDefaultModuleget it from
    • Module starts (if route hits)
      • E trigger event application:beforeStartModule
      • Call the module initialization method (Module.php)  registerAutoloaders() and registerServices()
      • E trigger event application:afterStartModule
    • distribution
      • Initialize View
      • Initialize the Dispatcher and copy the parameters from the Router to the Dispatcher
      • Call View to  View->start()open the buffer
      • E trigger event application:beforeHandleRequest
      • Start distribution (etc/dispatcher.c) Dispatcher->dispatch()
        • E trigger event dispatch:beforeDispatchLoop
        • The loop starts a single distribution
          • E trigger event dispatch:beforeDispatch
          • Obtain the complete class and method name according to the Module, Namespace, Controller, and Action carried by the Dispatcher, and trigger event E if it is not found dispatch:beforeException
          • E trigger event dispatch:beforeExecuteRoute
          • transferController->beforeExecuteRoute()
          • transferController->initialize()
          • E trigger event dispatch:afterInitialize
          • Call the Action method
          • E trigger event dispatch:afterExecuteRoute
          • E trigger event dispatch:afterDispatch
        • If there is forward() in Action, start the next distribution
      • E All distribution ends, triggering an event dispatch:afterDispatchLoop
      • Application gets the output after distribution $dispatcher->getReturnedValue()
      • E trigger event  application:afterHandleRequest distribution ends
    • Rendering, if Application gets the Phalcon\Http\ResponseInterfacetype of return from the distribution, the rendering ends directly
      • E trigger event  application:viewRender distribution ends
      • Call  Phalcon\Mvc\View->render(), the entry parameter is ControllerName / ActionName / Params of Dispatcher
      • Call to  Phalcon\Mvc\View->finish()end the receive of the buffer
    • ready to respond
      • will be Phalcon\Mvc\View->getContent()passed Phalcon\Http\Response->setContent()into the Response
      • E trigger event application:beforeSendResponse
      • call Phalcon\Http\Response->sendHeaders()send header
      • call Phalcon\Http\Response->sendCookies()to send cookies
      • returns the prepared response as $application->handle()the return value of
  • send response
    • echo $application->handle()->getContent();

MVC events

Phalcon, as a C extension framework, has the advantage of high performance. Although we can implement the entire startup by ourselves through the previous method, a better way is to avoid replacing the content of the framework itself and use event-driven.

The following is a summary of the events that can be monitored in the entire MVC process. You can choose the corresponding event as the entry point according to different needs:

  • DI injection
    • application:boot app launch
  • routing phase
    • module start
    • application:beforeStartModule Before the module starts
    • application:afterStartModule After the module starts
  • distribution phase
    • application:beforeHandleRequestbefore entering the dispenser
    • start distribution
      • dispatch:beforeDispatchLoop Before the distribution cycle begins
      • dispatch:beforeDispatch Before a single distribution begins
      • dispatch:beforeExecuteRoute Before the Action is executed
      • dispatch:afterExecuteRoute After the Action is executed
      • dispatch:beforeNotFoundAction Action not found
      • dispatch:beforeException before throwing exception
      • dispatch:afterDispatch End of single distribution
      • dispatch:afterDispatchLoop Distribution cycle ends
    • application:afterHandleRequest distribution ends
  • render stage
    • application:viewRender Before rendering starts
  • send response
    • application:beforeSendResponse before the final response is sent

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325977377&siteId=291194637