PackageInfo.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Module;
  7. use Magento\Framework\Component\ComponentRegistrar;
  8. /**
  9. * Provide information of dependencies and conflicts in composer.json files, mapping of package name to module name,
  10. * and mapping of module name to package version
  11. */
  12. class PackageInfo
  13. {
  14. /**
  15. * Package name to module name map
  16. *
  17. * @var string[]
  18. */
  19. private $packageModuleMap;
  20. /**
  21. * Map of module name to package version
  22. *
  23. * @var string[]
  24. */
  25. private $modulePackageVersionMap;
  26. /**
  27. * "require" field of each module, contains depending modules' name
  28. *
  29. * @var array[]
  30. */
  31. private $requireMap;
  32. /**
  33. * "conflict" field of each module, contains conflicting modules' name and version constraint
  34. *
  35. * @var array[]
  36. */
  37. private $conflictMap;
  38. /**
  39. * Reader of composer.json files
  40. *
  41. * @var Dir\Reader
  42. */
  43. private $reader;
  44. /**
  45. * @var ComponentRegistrar
  46. */
  47. private $componentRegistrar;
  48. /**
  49. * @var array
  50. */
  51. protected $nonExistingDependencies = [];
  52. /**
  53. * @var \Magento\Framework\Serialize\Serializer\Json
  54. */
  55. private $serializer;
  56. /**
  57. * @param Dir\Reader $reader
  58. * @param ComponentRegistrar $componentRegistrar
  59. * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
  60. * @throws \RuntimeException
  61. */
  62. public function __construct(
  63. Dir\Reader $reader,
  64. ComponentRegistrar $componentRegistrar,
  65. \Magento\Framework\Serialize\Serializer\Json $serializer = null
  66. ) {
  67. $this->reader = $reader;
  68. $this->componentRegistrar = $componentRegistrar;
  69. $this->serializer = $serializer?: \Magento\Framework\App\ObjectManager::getInstance()
  70. ->get(\Magento\Framework\Serialize\Serializer\Json::class);
  71. }
  72. /**
  73. * Load the packages information
  74. *
  75. * @return void
  76. * @throws \InvalidArgumentException
  77. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  78. */
  79. private function load()
  80. {
  81. if ($this->packageModuleMap === null) {
  82. $jsonData = $this->reader->getComposerJsonFiles()->toArray();
  83. foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) {
  84. $key = $moduleDir . '/composer.json';
  85. if (isset($jsonData[$key]) && $jsonData[$key]) {
  86. try {
  87. $packageData = $this->serializer->unserialize($jsonData[$key]);
  88. } catch (\InvalidArgumentException $e) {
  89. throw new \InvalidArgumentException(
  90. sprintf(
  91. "%s composer.json error: %s",
  92. $moduleName,
  93. $e->getMessage()
  94. )
  95. );
  96. }
  97. if (isset($packageData['name'])) {
  98. $this->packageModuleMap[$packageData['name']] = $moduleName;
  99. }
  100. if (isset($packageData['version'])) {
  101. $this->modulePackageVersionMap[$moduleName] = $packageData['version'];
  102. }
  103. if (!empty($packageData['require'])) {
  104. $this->requireMap[$moduleName] = array_keys($packageData['require']);
  105. }
  106. if (!empty($packageData['conflict'])) {
  107. $this->conflictMap[$moduleName] = $packageData['conflict'];
  108. }
  109. }
  110. }
  111. }
  112. }
  113. /**
  114. * Get module name of a package
  115. *
  116. * @param string $packageName
  117. * @return string
  118. */
  119. public function getModuleName($packageName)
  120. {
  121. $this->load();
  122. $moduleName = null;
  123. if (isset($this->packageModuleMap[$packageName])) {
  124. $moduleName = $this->packageModuleMap[$packageName];
  125. } elseif ($this->isMagentoPackage($packageName)) {
  126. $moduleName = $this->convertPackageNameToModuleName($packageName);
  127. $this->addNonExistingDependency($moduleName);
  128. }
  129. return $moduleName;
  130. }
  131. /**
  132. * Add non existing dependency
  133. *
  134. * @param string $dependency
  135. * @return void
  136. */
  137. protected function addNonExistingDependency($dependency)
  138. {
  139. if (!isset($this->nonExistingDependencies[$dependency])) {
  140. $this->nonExistingDependencies[$dependency] = $dependency;
  141. }
  142. }
  143. /**
  144. * Return list of non existing dependencies
  145. *
  146. * @return array
  147. */
  148. public function getNonExistingDependencies()
  149. {
  150. return $this->nonExistingDependencies;
  151. }
  152. /**
  153. * Build module name based on internal package name
  154. *
  155. * @param string $packageName
  156. * @return string|null
  157. */
  158. protected function convertPackageNameToModuleName($packageName)
  159. {
  160. $moduleName = str_replace('magento/module-', '', $packageName);
  161. $moduleName = str_replace('-', ' ', $moduleName);
  162. $moduleName = str_replace(' ', '', ucwords($moduleName));
  163. return 'Magento_' . $moduleName;
  164. }
  165. /**
  166. * Check if package is internal magento module
  167. *
  168. * @param string $packageName
  169. * @return bool
  170. */
  171. protected function isMagentoPackage($packageName)
  172. {
  173. return strpos($packageName, 'magento/module-') === 0;
  174. }
  175. /**
  176. * Get package name of a module
  177. *
  178. * @param string $moduleName
  179. * @return string
  180. */
  181. public function getPackageName($moduleName)
  182. {
  183. $this->load();
  184. return array_search($moduleName, $this->packageModuleMap) ?: '';
  185. }
  186. /**
  187. * Convert an array of package names to module names
  188. *
  189. * @param string[] $packageNames
  190. * @return string[]
  191. */
  192. private function convertToModuleNames($packageNames)
  193. {
  194. $moduleNames = [];
  195. foreach ($packageNames as $package) {
  196. $moduleNames[] = $this->getModuleName($package);
  197. }
  198. return $moduleNames;
  199. }
  200. /**
  201. * Get all module names a module requires
  202. *
  203. * @param string $moduleName
  204. * @return array
  205. */
  206. public function getRequire($moduleName)
  207. {
  208. $this->load();
  209. $require = [];
  210. if (isset($this->requireMap[$moduleName])) {
  211. $require = $this->convertToModuleNames($this->requireMap[$moduleName]);
  212. }
  213. return $require;
  214. }
  215. /**
  216. * Get all module names a module required by
  217. *
  218. * @param string $requiredModuleName
  219. * @return array
  220. */
  221. public function getRequiredBy($requiredModuleName)
  222. {
  223. $this->load();
  224. $requiredBy = [];
  225. foreach ($this->requireMap as $moduleName => $moduleRequireList) {
  226. if (in_array($requiredModuleName, $moduleRequireList)) {
  227. $requiredBy[] = $moduleName;
  228. }
  229. }
  230. return $requiredBy;
  231. }
  232. /**
  233. * Get all module names a module conflicts
  234. *
  235. * @param string $moduleName
  236. * @return array
  237. */
  238. public function getConflict($moduleName)
  239. {
  240. $this->load();
  241. $conflict = [];
  242. if (isset($this->conflictMap[$moduleName])) {
  243. $conflict = array_combine(
  244. $this->convertToModuleNames(array_keys($this->conflictMap[$moduleName])),
  245. $this->conflictMap[$moduleName]
  246. );
  247. }
  248. return $conflict;
  249. }
  250. /**
  251. * Get package version of a module
  252. *
  253. * @param string $moduleName
  254. * @return string
  255. */
  256. public function getVersion($moduleName)
  257. {
  258. $this->load();
  259. return $this->modulePackageVersionMap[$moduleName] ?? '';
  260. }
  261. }