123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Framework\Module\ModuleList;
- use Magento\Framework\Module\Declaration\Converter\Dom;
- use Magento\Framework\Xml\Parser;
- use Magento\Framework\Component\ComponentRegistrarInterface;
- use Magento\Framework\Component\ComponentRegistrar;
- use Magento\Framework\Filesystem\DriverInterface;
- /**
- * Loader of module list information from the filesystem
- *
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- */
- class Loader
- {
- /**
- * Converter of XML-files to associative arrays (specific to module.xml file format)
- *
- * @var Dom
- */
- private $converter;
- /**
- * Parser
- *
- * @var \Magento\Framework\Xml\Parser
- */
- private $parser;
- /**
- * Module registry
- *
- * @var ComponentRegistrarInterface
- */
- private $moduleRegistry;
- /**
- * Filesystem driver to allow reading of module.xml files which live outside of app/code
- *
- * @var DriverInterface
- */
- private $filesystemDriver;
- /**
- * Constructor
- *
- * @param Dom $converter
- * @param Parser $parser
- * @param ComponentRegistrarInterface $moduleRegistry
- * @param DriverInterface $filesystemDriver
- */
- public function __construct(
- Dom $converter,
- Parser $parser,
- ComponentRegistrarInterface $moduleRegistry,
- DriverInterface $filesystemDriver
- ) {
- $this->converter = $converter;
- $this->parser = $parser;
- $this->parser->initErrorHandler();
- $this->moduleRegistry = $moduleRegistry;
- $this->filesystemDriver = $filesystemDriver;
- }
- /**
- * Loads the full module list information. Excludes modules specified in $exclude.
- *
- * @param array $exclude
- * @throws \Magento\Framework\Exception\LocalizedException
- * @return array
- */
- 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);
- }
- /**
- * Returns module config data and a path to the module.xml file.
- *
- * Example of data returned by generator:
- * <code>
- * [ 'vendor/module/etc/module.xml', '<xml>contents</xml>' ]
- * </code>
- *
- * @return \Traversable
- *
- * @author Josh Di Fabio <joshdifabio@gmail.com>
- */
- private function getModuleConfigs()
- {
- $modulePaths = $this->moduleRegistry->getPaths(ComponentRegistrar::MODULE);
- foreach ($modulePaths as $modulePath) {
- $filePath = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, "$modulePath/etc/module.xml");
- yield [$filePath, $this->filesystemDriver->fileGetContents($filePath)];
- }
- }
- /**
- * Sort the list of modules using "sequence" key in meta-information
- *
- * @param array $origList
- * @return array
- * @SuppressWarnings(PHPMD.UnusedLocalVariable)
- */
- 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;
- }
- /**
- * Accumulate information about all transitive "sequence" references
- *
- * @param array $list
- * @param string $name
- * @param array $accumulated
- * @return array
- * @throws \Exception
- */
- private function expandSequence($list, $name, $accumulated = [])
- {
- $accumulated[] = $name;
- $result = $list[$name]['sequence'];
- foreach ($result as $relatedName) {
- if (in_array($relatedName, $accumulated)) {
- throw new \Exception("Circular sequence reference from '{$name}' to '{$relatedName}'.");
- }
- if (!isset($list[$relatedName])) {
- continue;
- }
- $relatedResult = $this->expandSequence($list, $relatedName, $accumulated);
- $result = array_unique(array_merge($result, $relatedResult));
- }
- return $result;
- }
- }
|