PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/magento/magento2-base/dev/tests/static/framework/Magento/TestFramework/Dependency/LayoutRule.php

https://gitlab.com/daigiangaitu91/magento
PHP | 519 lines | 325 code | 31 blank | 163 comment | 15 complexity | d97a4b035b841d2fde8160b9367d8af5 MD5 | raw file
  1. <?php
  2. /**
  3. * Rule for searching dependencies in layout files
  4. *
  5. * Copyright © 2015 Magento. All rights reserved.
  6. * See COPYING.txt for license details.
  7. */
  8. namespace Magento\TestFramework\Dependency;
  9. class LayoutRule implements \Magento\TestFramework\Dependency\RuleInterface
  10. {
  11. /**
  12. * Default modules list.
  13. *
  14. * @var array
  15. */
  16. protected $_defaultModules = [
  17. 'frontend' => 'Magento\Theme',
  18. 'adminhtml' => 'Magento\Adminhtml',
  19. ];
  20. /**
  21. * Namespaces to analyze
  22. *
  23. * Format: {Namespace}|{Namespace}|...
  24. *
  25. * @var string
  26. */
  27. protected $_namespaces;
  28. /**
  29. * List of routers
  30. *
  31. * Format: array(
  32. * '{Router}' => '{Module_Name}'
  33. * )
  34. *
  35. * @var array
  36. */
  37. protected $_mapRouters = [];
  38. /**
  39. * List of layout blocks
  40. *
  41. * Format: array(
  42. * '{Area}' => array(
  43. * '{Block_Name}' => array('{Module_Name}' => '{Module_Name}')
  44. * ))
  45. *
  46. * @var array
  47. */
  48. protected $_mapLayoutBlocks = [];
  49. /**
  50. * List of layout handles
  51. *
  52. * Format: array(
  53. * '{Area}' => array(
  54. * '{Handle_Name}' => array('{Module_Name}' => '{Module_Name}')
  55. * ))
  56. *
  57. * @var array
  58. */
  59. protected $_mapLayoutHandles = [];
  60. /**
  61. * Unknown layout handle
  62. */
  63. const EXCEPTION_TYPE_UNKNOWN_HANDLE = 'UNKNOWN_HANDLE';
  64. /**
  65. * Unknown layout block
  66. */
  67. const EXCEPTION_TYPE_UNKNOWN_BLOCK = 'UNKNOWN_BLOCK';
  68. /**
  69. * Undefined dependency
  70. */
  71. const EXCEPTION_TYPE_UNDEFINED_DEPENDENCY = 'UNDEFINED_DEPENDENCY';
  72. /**
  73. * Constructor
  74. *
  75. * @param array $mapRouters
  76. * @param array $mapLayoutBlocks
  77. * @param array $mapLayoutHandles
  78. */
  79. public function __construct(array $mapRouters, array $mapLayoutBlocks, array $mapLayoutHandles)
  80. {
  81. $this->_mapRouters = $mapRouters;
  82. $this->_mapLayoutBlocks = $mapLayoutBlocks;
  83. $this->_mapLayoutHandles = $mapLayoutHandles;
  84. $this->_namespaces = implode('|', \Magento\Framework\App\Utility\Files::init()->getNamespaces());
  85. }
  86. /**
  87. * Retrieve dependencies information for current module
  88. *
  89. * @param string $currentModule
  90. * @param string $fileType
  91. * @param string $file
  92. * @param string $contents
  93. * @return array
  94. */
  95. public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
  96. {
  97. if ('layout' != $fileType) {
  98. return [];
  99. }
  100. $attributes = $this->_caseAttributeModule($currentModule, $contents);
  101. $blocks = $this->_caseElementBlock($currentModule, $contents);
  102. $actions = $this->_caseElementAction($currentModule, $contents);
  103. $handle = $this->_caseLayoutHandle($currentModule, $file, $contents);
  104. $handleParents = $this->_caseLayoutHandleParent($currentModule, $file, $contents);
  105. $handleUpdates = $this->_caseLayoutHandleUpdate($currentModule, $file, $contents);
  106. $references = $this->_caseLayoutReference($currentModule, $file, $contents);
  107. $dependencies = array_merge(
  108. $attributes,
  109. $blocks,
  110. $actions,
  111. $handle,
  112. $handleParents,
  113. $handleUpdates,
  114. $references
  115. );
  116. return $dependencies;
  117. }
  118. /**
  119. * Check dependencies for 'module' attribute
  120. *
  121. * Ex.: <element module="{module}">
  122. *
  123. * @param $currentModule
  124. * @param $contents
  125. * @return array
  126. */
  127. protected function _caseAttributeModule($currentModule, &$contents)
  128. {
  129. $patterns = [
  130. '/(?<source><.+module\s*=\s*[\'"](?<namespace>' .
  131. $this->_namespaces .
  132. ')[_\\\\]' .
  133. '(?<module>[A-Z][a-zA-Z]+)[\'"].*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  134. ];
  135. return $this->_checkDependenciesByRegexp($currentModule, $contents, $patterns);
  136. }
  137. /**
  138. * Check dependencies for <block> element
  139. *
  140. * Ex.: <block class="{name}">
  141. * <block template="{path}">
  142. *
  143. * @param $currentModule
  144. * @param $contents
  145. * @return array
  146. */
  147. protected function _caseElementBlock($currentModule, &$contents)
  148. {
  149. $patterns = [
  150. '/(?<source><block.*class\s*=\s*[\'"](?<namespace>' .
  151. $this->_namespaces .
  152. ')[_\\\\]' .
  153. '(?<module>[A-Z][a-zA-Z]+)[_\\\\]' .
  154. '(?:[A-Z][a-zA-Z]+[_\\\\]?){1,}[\'"].*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_HARD,
  155. '/(?<source><block.*template\s*=\s*[\'"](?<namespace>' .
  156. $this->_namespaces .
  157. ')[_\\\\]' .
  158. '(?<module>[A-Z][a-zA-Z]+)::[\w\/\.]+[\'"].*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  159. ];
  160. return $this->_checkDependenciesByRegexp($currentModule, $contents, $patterns);
  161. }
  162. /**
  163. * Check dependencies for <action> element
  164. *
  165. * Ex.: <block>{name}
  166. * <template>{path}
  167. * <file>{path}
  168. * <element helper="{name}">
  169. *
  170. * @param $currentModule
  171. * @param $contents
  172. * @return array
  173. */
  174. protected function _caseElementAction($currentModule, &$contents)
  175. {
  176. $patterns = [
  177. '/(?<source><block\s*>(?<namespace>' .
  178. $this->_namespaces .
  179. ')[_\\\\]' .
  180. '(?<module>[A-Z][a-zA-Z]+)[_\\\\]' .
  181. '(?:[A-Z][a-zA-Z]+[_\\\\]?){1,}<\/block\s*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  182. '/(?<source><template\s*>(?<namespace>' .
  183. $this->_namespaces .
  184. ')[_\\\\]' .
  185. '(?<module>[A-Z][a-zA-Z]+)::[\w\/\.]+' .
  186. '<\/template\s*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  187. '/(?<source><file\s*>(?<namespace>' .
  188. $this->_namespaces .
  189. ')[_\\\\]' .
  190. '(?<module>[A-Z][a-zA-Z]+)::[\w\/\.-]+' .
  191. '<\/file\s*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  192. '/(?<source><.*helper\s*=\s*[\'"](?<namespace>' .
  193. $this->_namespaces .
  194. ')[_\\\\]' .
  195. '(?<module>[A-Z][a-zA-Z]+)[_\\\\](?:[A-Z][a-z]+[_\\\\]?){1,}::[\w]+' .
  196. '[\'"].*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  197. ];
  198. return $this->_checkDependenciesByRegexp($currentModule, $contents, $patterns);
  199. }
  200. /**
  201. * Check layout handles
  202. *
  203. * Ex.: <layout><{name}>...</layout>
  204. *
  205. * @param $currentModule
  206. * @param $file
  207. * @param $contents
  208. * @return array
  209. */
  210. protected function _caseLayoutHandle($currentModule, $file, &$contents)
  211. {
  212. $xml = simplexml_load_string($contents);
  213. if (!$xml) {
  214. return [];
  215. }
  216. $area = $this->_getAreaByFile($file);
  217. $result = [];
  218. foreach ((array)$xml->xpath('/layout/child::*') as $element) {
  219. $check = $this->_checkDependencyLayoutHandle($currentModule, $area, $element->getName());
  220. $modules = isset($check['module']) ? $check['module'] : null;
  221. if ($modules) {
  222. if (!is_array($modules)) {
  223. $modules = [$modules];
  224. }
  225. foreach ($modules as $module) {
  226. $result[$module] = [
  227. 'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  228. 'source' => $element->getName(),
  229. ];
  230. }
  231. }
  232. }
  233. return $this->_getUniqueDependencies($result);
  234. }
  235. /**
  236. * Check layout handles parents
  237. *
  238. * Ex.: <layout_name parent="{name}">
  239. *
  240. * @param $currentModule
  241. * @param $file
  242. * @param $contents
  243. * @return array
  244. */
  245. protected function _caseLayoutHandleParent($currentModule, $file, &$contents)
  246. {
  247. $xml = simplexml_load_string($contents);
  248. if (!$xml) {
  249. return [];
  250. }
  251. $area = $this->_getAreaByFile($file);
  252. $result = [];
  253. foreach ((array)$xml->xpath('/layout/child::*/@parent') as $element) {
  254. $check = $this->_checkDependencyLayoutHandle($currentModule, $area, (string)$element);
  255. $modules = isset($check['module']) ? $check['module'] : null;
  256. if ($modules) {
  257. if (!is_array($modules)) {
  258. $modules = [$modules];
  259. }
  260. foreach ($modules as $module) {
  261. $result[$module] = [
  262. 'type' => \Magento\Test\Integrity\DependencyTest::TYPE_HARD,
  263. 'source' => (string)$element,
  264. ];
  265. }
  266. }
  267. }
  268. return $this->_getUniqueDependencies($result);
  269. }
  270. /**
  271. * Check layout handles updates
  272. *
  273. * Ex.: <update handle="{name}" />
  274. *
  275. * @param $currentModule
  276. * @param $file
  277. * @param $contents
  278. * @return array
  279. */
  280. protected function _caseLayoutHandleUpdate($currentModule, $file, &$contents)
  281. {
  282. $xml = simplexml_load_string($contents);
  283. if (!$xml) {
  284. return [];
  285. }
  286. $area = $this->_getAreaByFile($file);
  287. $result = [];
  288. foreach ((array)$xml->xpath('//update/@handle') as $element) {
  289. $check = $this->_checkDependencyLayoutHandle($currentModule, $area, (string)$element);
  290. $modules = isset($check['module']) ? $check['module'] : null;
  291. if ($modules) {
  292. if (!is_array($modules)) {
  293. $modules = [$modules];
  294. }
  295. foreach ($modules as $module) {
  296. $result[$module] = [
  297. 'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
  298. 'source' => (string)$element,
  299. ];
  300. }
  301. }
  302. }
  303. return $this->_getUniqueDependencies($result);
  304. }
  305. /**
  306. * Check layout references
  307. *
  308. * Ex.: <reference name="{name}">
  309. *
  310. * @param $currentModule
  311. * @param $file
  312. * @param $contents
  313. * @return array
  314. */
  315. protected function _caseLayoutReference($currentModule, $file, &$contents)
  316. {
  317. $xml = simplexml_load_string($contents);
  318. if (!$xml) {
  319. return [];
  320. }
  321. $area = $this->_getAreaByFile($file);
  322. $result = [];
  323. foreach ((array)$xml->xpath('//reference/@name') as $element) {
  324. $check = $this->_checkDependencyLayoutBlock($currentModule, $area, (string)$element);
  325. $module = isset($check['module']) ? $check['module'] : null;
  326. if ($module) {
  327. $result[$module] = [
  328. 'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_SOFT,
  329. 'source' => (string)$element,
  330. ];
  331. }
  332. }
  333. return $this->_getUniqueDependencies($result);
  334. }
  335. /**
  336. * Search dependencies by defined regexp patterns
  337. *
  338. * @param $currentModule
  339. * @param $contents
  340. * @param array $patterns
  341. * @return array
  342. */
  343. protected function _checkDependenciesByRegexp($currentModule, &$contents, $patterns = [])
  344. {
  345. $result = [];
  346. foreach ($patterns as $pattern => $type) {
  347. if (preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)) {
  348. foreach ($matches as $match) {
  349. $module = $match['namespace'] . '\\' . $match['module'];
  350. if ($currentModule != $module) {
  351. $result[$module] = ['type' => $type, 'source' => $match['source']];
  352. }
  353. }
  354. }
  355. }
  356. return $this->_getUniqueDependencies($result);
  357. }
  358. /**
  359. * Check layout handle dependency
  360. *
  361. * Return: array(
  362. * 'module' // dependent module
  363. * 'source' // source text
  364. * )
  365. *
  366. * @param $currentModule
  367. * @param $area
  368. * @param $handle
  369. * @return array
  370. */
  371. protected function _checkDependencyLayoutHandle($currentModule, $area, $handle)
  372. {
  373. $chunks = explode('_', $handle);
  374. if (count($chunks) > 1) {
  375. // Remove 'action' part from handle name
  376. array_pop($chunks);
  377. }
  378. $router = implode('_', $chunks);
  379. if (isset($this->_mapRouters[$router])) {
  380. // CASE 1: Single dependency
  381. $modules = $this->_mapRouters[$router];
  382. if (!in_array($currentModule, $modules)) {
  383. return ['module' => $modules];
  384. }
  385. }
  386. if (isset($this->_mapLayoutHandles[$area][$handle])) {
  387. // CASE 2: No dependencies
  388. $modules = $this->_mapLayoutHandles[$area][$handle];
  389. if (isset($modules[$currentModule])) {
  390. return ['module' => null];
  391. }
  392. // CASE 3: Single dependency
  393. if (1 == count($modules)) {
  394. return ['module' => current($modules)];
  395. }
  396. // CASE 4: Default module dependency
  397. $defaultModule = $this->_getDefaultModuleName($area);
  398. if (isset($modules[$defaultModule])) {
  399. return ['module' => $defaultModule];
  400. }
  401. }
  402. return [];
  403. }
  404. /**
  405. * Check layout block dependency
  406. *
  407. * Return: array(
  408. * 'module' // dependent module
  409. * 'source' // source text
  410. * )
  411. *
  412. * @param $currentModule
  413. * @param $area
  414. * @param $block
  415. * @return array
  416. */
  417. protected function _checkDependencyLayoutBlock($currentModule, $area, $block)
  418. {
  419. if (isset($this->_mapLayoutBlocks[$area][$block])) {
  420. // CASE 1: No dependencies
  421. $modules = $this->_mapLayoutBlocks[$area][$block];
  422. if (isset($modules[$currentModule])) {
  423. return ['module' => null];
  424. }
  425. // CASE 2: Single dependency
  426. if (1 == count($modules)) {
  427. return ['module' => current($modules)];
  428. }
  429. // CASE 3: Default module dependency
  430. $defaultModule = $this->_getDefaultModuleName($area);
  431. if (isset($modules[$defaultModule])) {
  432. return ['module' => $defaultModule];
  433. }
  434. }
  435. return [];
  436. }
  437. /**
  438. * Get area from file path
  439. *
  440. * @param $file
  441. * @return string
  442. */
  443. protected function _getAreaByFile($file)
  444. {
  445. $area = 'default';
  446. if (preg_match('/\/(?<area>adminhtml|frontend)\//', $file, $matches)) {
  447. $area = $matches['area'];
  448. }
  449. return $area;
  450. }
  451. /**
  452. * Retrieve unique dependencies
  453. *
  454. * @param array $dependencies
  455. * @return array
  456. */
  457. protected function _getUniqueDependencies($dependencies = [])
  458. {
  459. $result = [];
  460. foreach ($dependencies as $module => $value) {
  461. $result[] = ['module' => $module, 'type' => $value['type'], 'source' => $value['source']];
  462. }
  463. return $result;
  464. }
  465. /**
  466. * Retrieve default module name (by area)
  467. *
  468. * @param string $area
  469. * @return null
  470. */
  471. protected function _getDefaultModuleName($area = 'default')
  472. {
  473. if (isset($this->_defaultModules[$area])) {
  474. return $this->_defaultModules[$area];
  475. }
  476. return null;
  477. }
  478. }