Application initialization is done in 9 steps, each one is described below. We are also going to talk about 3 Magento 2 modes and help you learn their advantages and disadvantages.
Application initialization
Designing a customization
How to use Magento modes
Developer mode
Production mode
Default mode
Enabling/disabling maintenance mode
Front controller responsibilities
Application initialization
Step 1
Request to index.php (entry point) in the root of the site or in pub/index.php. When in developer mode, you should use index.php as the entry point in the root of the site, while for production mode, it is recommended to use pub/index.php.
Step 2
Connecting the file to bootstrap.php:
1 2 3 |
try { require __DIR__ . '/app/bootstrap.php'; } catch (\Exception $e) { |
in which autoload.php is loaded and connected:
1 |
require_once __DIR__ . '/autoload.php'; |
and the translation mechanism is implemented:
1 |
require_once BP . '/app/functions.php'; |
Step 3
The autoloader can call the create () method of the Bootstrap class:
1 |
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER); |
which returns a Bootstrap object, which in its turn contains the ObjectManagerFactory object:
1 2 3 4 5 6 7 8 |
public static function create($rootDir, array $initParams, ObjectManagerFactory $factory = null) { self::populateAutoloader($rootDir, $initParams); if ($factory === null) { $factory = self::createObjectManagerFactory($rootDir, $initParams); } return new self($factory, $rootDir, $initParams); } |
Step 4
Next, the createApplication() method of the Bootstrap object is called:
$app = $bootstrap->createApplication(\Magento\Framework\App\Http::class);
This method creates an instance of the Magento\Framework\App\Http class using the objectManager and returns to index.php.
1 2 3 4 5 6 7 8 9 10 11 12 |
public function createApplication($type, $arguments = []) { try { $application = $this->objectManager->create($type, $arguments); if (!($application instanceof AppInterface)) { throw new \InvalidArgumentException("The provided class doesn't implement AppInterface: {$type}"); } return $application; } catch (\Exception $e) { $this->terminate($e); } } |
Step 5
Next, the run () method of the Bootstrap object is called to start the application, which in its turn calls the launch () method of the application object.
$bootstrap->run($app);
1 2 3 4 5 6 7 8 9 10 11 12 |
public function createApplication($type, $arguments = []) { try { $application = $this->objectManager->create($type, $arguments); if (!($application instanceof AppInterface)) { throw new \InvalidArgumentException("The provided class doesn't implement AppInterface: {$type}"); } return $application; } catch (\Exception $e) { $this->terminate($e); } } |
Step 6
The http instance of the object performs initial routing — the scope from the URL is determined and set to $this->_ state->setAreaCode($areaCode). After setting the scope, the required configuration for the given area is loaded.
Next, an object of the \Magento\Framework\App\FrontController class is created and its dispatch($this->_request) method is called in which the request is passed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public function launch() { $areaCode = $this->_areaList->getCodeByFrontName($this->_request->getFrontName()); $this->_state->setAreaCode($areaCode); $this->_objectManager->configure($this->_configLoader->load($areaCode)); /** @var \Magento\Framework\App\FrontControllerInterface $frontController */ $frontController = $this->_objectManager->get(\Magento\Framework\App\FrontControllerInterface::class); $result = $frontController->dispatch($this->_request); // TODO: Temporary solution until all controllers return ResultInterface (MAGETWO-28359) if ($result instanceof ResultInterface) { $this->registry->register('use_page_cache_plugin', true, true); $result->renderResult($this->_response); } elseif ($result instanceof HttpInterface) { $this->_response = $result; } else { throw new \InvalidArgumentException('Invalid return type'); } // This event gives possibility to launch something before sending output (allow cookie setting) $eventParams = ['request' => $this->_request, 'response' => $this->_response]; $this->_eventManager->dispatch('controller_front_send_response_before', $eventParams); return $this->_response; } |
Step 7
The dispatch() method of the FrontController class defines the current router and determines the current action controller. Then, the dispatch() method is called from the action controller.
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 |
public function dispatch(RequestInterface $request) { \Magento\Framework\Profiler::start('routers_match'); $routingCycleCounter = 0; $result = null; while (!$request->isDispatched() && $routingCycleCounter++ < 100) { /** @var \Magento\Framework\App\RouterInterface $router */ foreach ($this->_routerList as $router) { try { $actionInstance = $router->match($request); if ($actionInstance) { $request->setDispatched(true); $this->response->setNoCacheHeaders(); if ($actionInstance instanceof \Magento\Framework\App\Action\AbstractAction) { $result = $actionInstance->dispatch($request); } else { $result = $actionInstance->execute(); } break; } } catch (\Magento\Framework\Exception\NotFoundException $e) { $request->initForward(); $request->setActionName('noroute'); $request->setDispatched(false); break; } } } \Magento\Framework\Profiler::stop('routers_match'); if ($routingCycleCounter > 100) { throw new \LogicException('Front controller reached 100 router match iterations'); } return $result; } |
Step 8
The dispatch() method is implemented in Magento\Framework\App\Action\Action.php.
We are inherited from this class when creating our own controllers. Action controller method returns an object that implements ResultInterface through execution of the execute() method.
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 |
public function dispatch(RequestInterface $request) { $this->_request = $request; $profilerKey = 'CONTROLLER_ACTION:' . $request->getFullActionName(); $eventParameters = ['controller_action' => $this, 'request' => $request]; $this->_eventManager->dispatch('controller_action_predispatch', $eventParameters); $this->_eventManager->dispatch('controller_action_predispatch_' . $request->getRouteName(), $eventParameters); $this->_eventManager->dispatch( 'controller_action_predispatch_' . $request->getFullActionName(), $eventParameters ); \Magento\Framework\Profiler::start($profilerKey); $result = null; if ($request->isDispatched() && !$this->_actionFlag->get('', self::FLAG_NO_DISPATCH)) { \Magento\Framework\Profiler::start('action_body'); $result = $this->execute(); \Magento\Framework\Profiler::start('postdispatch'); if (!$this->_actionFlag->get('', self::FLAG_NO_POST_DISPATCH)) { $this->_eventManager->dispatch( 'controller_action_postdispatch_' . $request->getFullActionName(), $eventParameters ); $this->_eventManager->dispatch( 'controller_action_postdispatch_' . $request->getRouteName(), $eventParameters ); $this->_eventManager->dispatch('controller_action_postdispatch', $eventParameters); } \Magento\Framework\Profiler::stop('postdispatch'); \Magento\Framework\Profiler::stop('action_body'); } \Magento\Framework\Profiler::stop($profilerKey); return $result ?: $this->_response; } |
Step 9
FrontController returns ResultInterface into Application Instance, which displays response.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public function launch() { $areaCode = $this->_areaList->getCodeByFrontName($this->_request->getFrontName()); $this->_state->setAreaCode($areaCode); $this->_objectManager->configure($this->_configLoader->load($areaCode)); /** @var \Magento\Framework\App\FrontControllerInterface $frontController */ $frontController = $this->_objectManager->get(\Magento\Framework\App\FrontControllerInterface::class); $result = $frontController->dispatch($this->_request); // TODO: Temporary solution until all controllers return ResultInterface (MAGETWO-28359) if ($result instanceof ResultInterface) { $this->registry->register('use_page_cache_plugin', true, true); $result->renderResult($this->_response); } elseif ($result instanceof HttpInterface) { |
Designing a customization that acts on every request
To obtain data from each request, you can create an observer for controller_front_send_response_before event (Magento\Framework\App\Http::launch()).
There is an implementation in Magento\Customer\Observer\Visitor\SaveByRequestObserver.
How to use Magento modes
Magento 2 can be launched in one of the three modes — developer, production and default. The main difference between the modes is in how Magento will get access to static files. Static files include css, javascript files and pictures.
There is also a maintenance mode, but it serves to prevent access to the system.
To view the current mode, use the CLI command bin/magento deploy:mode:show.
To switch between the modes use bin/magento deploy:mode:set.
The pros and cons of using developer mode/production mode
Developer mode
In developer mode, static files are generated in each request. In pub/static symlinks are installed on real files from modules and libraries.
The advantage of this mechanism is that it immediately displays all the changes that you make. The disadvantage — decrease in performance due to the lack of static content caching.
Uncaught exceptions are displayed. Additionally, you need to configure app/bootstrap.php.
1 2 |
error_reporting(E_ALL); ini_set('display_errors', 1); |
Error logging in var/log/exception.log.
Developer mode is recommended for development and customization of modules or themes. Its main advantage is the display of errors in the browser and fast editing of static files, while the disadvantage is low performance.
In Developer mode, static view files are generated each time they are requested. They are written to the pub/static directory, but the cache is not used. This has a big performance impact, but any changes a developer makes to view files are immediately visible.
Uncaught exceptions are displayed in the browser, instead of being logged. An exception is thrown whenever an event subscriber cannot be invoked.
You should use the Developer mode while you are developing customizations or extensions.
Production mode
Production mode is recommended for using on a production server. This is the most productive mode available.
When switching to this mode, errors will not be displayed to a user but will be logged into files. (check out the article Developer Mode and Debugging in Magento 2 for more information about debugging)
In this module, static files will not be generated on the fly but will be deployed to the pub/static directory using the command <your Magento install dir>/bin/magento setup:static-content:deploy. This mechanism allows you to significantly increase the performance, but it is required to redeploy static content after each change.
Default mode
Default mode is how the Magento software operates if no other mode is specified.
This mode is set by default during the initial Magento installation.
In this module, the errors are not displayed to users but are logged to var/log. Files in pub/static are generated on the fly, and then cached.
Unlike the developer mode, the file changes are not displayed until the generated static view files (var/view_preprocessed) are cleared.
Default mode is not optimized to work in production as well as the production mode is, because the creation of files on the fly and their further caching is less productive than the preliminary creation using the command line tool.
Enabling/disabling maintenance mode
bin/magento maintenance:enable
bin/magento maintenance:disable
Front controller responsibilities
- Gathering all routers (injected into the constructor using DI).
- Finding a matching controller/router.
- Obtaining the generated HTML for the response object.
Front controller is the first step (entry point) for processing the flow of requests. Basically, the front controller controls all the other controllers. In Magento 2 it collects routes, matches the controllers, and receives the HTML generated for the response object (converting the query to a result). It is not used in the API or console.
Hey there.
Could you guys please re-check “Designing a customization that acts on every request” paragraph.
I think those mentioned approaches will not work in case of REST API or SOAP API requests.
“controller_front_send_response_before” event should make the deal.
Cheers!