This is the sequel which delves into the details of Magento 2 initialization. The first article provides a comprehensive description of initialization in Magento 1. We’re sure that you will take on board the present knowledge described below.
Module definition
In Magento 2 module definition consists of two files:
- app/code/Vendor/Module/registration.php:
1 2 3 4 5 6 |
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, Vendor_Module, __DIR__ ); |
The file declares module type, name, and directory.
- app/code/Vendor/Module/etc/module.xml:
1 2 3 4 |
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Vendor_Module" setup_version="1.0.0" /> </config> |
It declares module version and optional dependencies.
Module initialization process. Module declaration
The first step of module initialization is to load module declaration from registration.php. For modules installed through composer these files are listed in vendor/composer/autoload_files.php file which is generated by the composer after each installation or command upgrading.
The last file is app/etc/NonComposerComponentRegistration.php file, that allows loading modules from app/code folder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php /** * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ $pathList[] = dirname(__DIR__) . '/code/*/*/cli_commands.php'; $pathList[] = dirname(__DIR__) . '/code/*/*/registration.php'; $pathList[] = dirname(__DIR__) . '/design/*/*/*/registration.php'; $pathList[] = dirname(__DIR__) . '/i18n/*/*/registration.php'; $pathList[] = dirname(dirname(__DIR__)) . '/lib/internal/*/*/registration.php'; $pathList[] = dirname(dirname(__DIR__)) . '/lib/internal/*/*/*/registration.php'; foreach ($pathList as $path) { // Sorting is disabled intentionally for performance improvement $files = glob($path, GLOB_NOSORT); if ($files === false) { throw new \RuntimeException('glob() returned error while searching in \'' . $path . '\''); } foreach ($files as $file) { include $file; } } |
This file lists all locations available for registration.php files and, besides, it includes them.
registration.php file consists of single call to
\Magento\Framework\Component\ComponentRegistrar::register method and passes module type, module name, and module directory as the argument.
\Magento\Framework\Component\ComponentRegistrar::register method validates module type via
\Magento\Framework\Component\ComponentRegistrar::validateType method and if the type is valid, it stores module name and path.
1 2 3 4 5 6 7 8 9 10 11 12 |
public static function register($type, $componentName, $path) { self::validateType($type); if (isset(self::$paths[$type][$componentName])) { throw new \LogicException( ucfirst($type) . ' \'' . $componentName . '\' from \'' . $path . '\' ' . 'has been already defined in \'' . self::$paths[$type][$componentName] . '\'.' ); } else { self::$paths[$type][$componentName] = str_replace('\\', '/', $path); } } |
module.xml files are processed by \Magento\Framework\Module\ModuleList\Loader::load method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public function load(array $exclude = []) { $result = []; foreach ($this->getModuleConfigs() as list($file, $contents)) { try { $this->parser->loadXML($contents); } catch (\Magento\Framework\Exception\LocalizedException $e) { throw new \Magento\Framework\Exception\LocalizedException( new \Magento\Framework\Phrase( 'Invalid Document: %1%2 Error: %3', [$file, PHP_EOL, $e->getMessage()] ), $e ); } $data = $this->converter->convert($this->parser->getDom()); $name = key($data); if (!in_array($name, $exclude)) { $result[$name] = $data[$name]; } } return $this->sortBySequence($result); } |
Module dependencies
Module dependencies can be defined in app/code/Vendor/Module/etc/module.xml file.
1 2 3 4 5 6 7 8 |
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Vendor_Module" setup_version="1.0.0"> <sequence> <module name="Magento_Catalog"/> </sequence> </module> </config> |
Just like in Magento 1, module definitions which have already been loaded need to be sorted out according to their dependencies (dependent modules must be loaded after their dependencies) and it is performed in \Magento\Framework\Module\ModuleList\Loader::sortBySequence 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 |
private function sortBySequence($origList) { ksort($origList); $expanded = []; foreach ($origList as $moduleName => $value) { $expanded[] = [ 'name' => $moduleName, 'sequence' => $this->expandSequence($origList, $moduleName), ]; } // Use "bubble sorting" because usort does not check each pair of elements and in this case it is important $total = count($expanded); for ($i = 0; $i < $total - 1; $i++) { for ($j = $i; $j < $total; $j++) { if (in_array($expanded[$j]['name'], $expanded[$i]['sequence'])) { $temp = $expanded[$i]; $expanded[$i] = $expanded[$j]; $expanded[$j] = $temp; } } } $result = []; foreach ($expanded as $pair) { $result[$pair['name']] = $origList[$pair['name']]; } return $result; } |
If you’re interested in learning details related to Magento 2 module initialization, follow our blog, as a new article will be published soon.