Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
LayoutRule.php
Go to the documentation of this file.
1 <?php
9 
11 {
17  protected $_defaultModules = [
18  'frontend' => 'Magento\Theme',
19  'adminhtml' => 'Magento\Adminhtml',
20  ];
21 
29  protected $_namespaces;
30 
40  protected $_mapRouters = [];
41 
52  protected $_mapLayoutBlocks = [];
53 
64  protected $_mapLayoutHandles = [];
65 
69  const EXCEPTION_TYPE_UNKNOWN_HANDLE = 'UNKNOWN_HANDLE';
70 
74  const EXCEPTION_TYPE_UNKNOWN_BLOCK = 'UNKNOWN_BLOCK';
75 
79  const EXCEPTION_TYPE_UNDEFINED_DEPENDENCY = 'UNDEFINED_DEPENDENCY';
80 
88  public function __construct(array $mapRouters, array $mapLayoutBlocks, array $mapLayoutHandles)
89  {
90  $this->_mapRouters = $mapRouters;
91  $this->_mapLayoutBlocks = $mapLayoutBlocks;
92  $this->_mapLayoutHandles = $mapLayoutHandles;
93  $this->_namespaces = implode('|', \Magento\Framework\App\Utility\Files::init()->getNamespaces());
94  }
95 
105  public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
106  {
107  if ('layout' != $fileType) {
108  return [];
109  }
110 
111  $attributes = $this->_caseAttributeModule($currentModule, $contents);
112  $blocks = $this->_caseElementBlock($currentModule, $contents);
113  $actions = $this->_caseElementAction($currentModule, $contents);
114  $handle = $this->_caseLayoutHandle($currentModule, $file, $contents);
115  $handleParents = $this->_caseLayoutHandleParent($currentModule, $file, $contents);
116  $handleUpdates = $this->_caseLayoutHandleUpdate($currentModule, $file, $contents);
117  $references = $this->_caseLayoutReference($currentModule, $file, $contents);
118 
119  $dependencies = array_merge(
120  $attributes,
121  $blocks,
122  $actions,
123  $handle,
124  $handleParents,
125  $handleUpdates,
126  $references
127  );
128  return $dependencies;
129  }
130 
140  protected function _caseAttributeModule($currentModule, &$contents)
141  {
142  $patterns = [
143  '/(?<source><.+module\s*=\s*[\'"](?<namespace>' .
144  $this->_namespaces .
145  ')[_\\\\]' .
146  '(?<module>[A-Z][a-zA-Z]+)[\'"].*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
147  ];
148  return $this->_checkDependenciesByRegexp($currentModule, $contents, $patterns);
149  }
150 
161  protected function _caseElementBlock($currentModule, &$contents)
162  {
163  $patterns = [
164  '/(?<source><block.*class\s*=\s*[\'"](?<namespace>' .
165  $this->_namespaces .
166  ')[_\\\\]' .
167  '(?<module>[A-Z][a-zA-Z]+)[_\\\\]' .
168  '(?:[A-Z][a-zA-Z]+[_\\\\]?){1,}[\'"].*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_HARD,
169  '/(?<source><block.*template\s*=\s*[\'"](?<namespace>' .
170  $this->_namespaces .
171  ')[_\\\\]' .
172  '(?<module>[A-Z][a-zA-Z]+)::[\w\/\.]+[\'"].*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
173  ];
174  return $this->_checkDependenciesByRegexp($currentModule, $contents, $patterns);
175  }
176 
189  protected function _caseElementAction($currentModule, &$contents)
190  {
191  $patterns = [
192  '/(?<source><block\s*>(?<namespace>' .
193  $this->_namespaces .
194  ')[_\\\\]' .
195  '(?<module>[A-Z][a-zA-Z]+)[_\\\\]' .
196  '(?:[A-Z][a-zA-Z]+[_\\\\]?){1,}<\/block\s*>)/' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
197  '/(?<source><template\s*>(?<namespace>' .
198  $this->_namespaces .
199  ')[_\\\\]' .
200  '(?<module>[A-Z][a-zA-Z]+)::[\w\/\.]+' .
202  '/(?<source><file\s*>(?<namespace>' .
203  $this->_namespaces .
204  ')[_\\\\]' .
205  '(?<module>[A-Z][a-zA-Z]+)::[\w\/\.-]+' .
207  '/(?<source><.*helper\s*=\s*[\'"](?<namespace>' .
208  $this->_namespaces .
209  ')[_\\\\]' .
210  '(?<module>[A-Z][a-zA-Z]+)[_\\\\](?:[A-Z][a-z]+[_\\\\]?){1,}::[\w]+' .
212  ];
213  return $this->_checkDependenciesByRegexp($currentModule, $contents, $patterns);
214  }
215 
226  protected function _caseLayoutHandle($currentModule, $file, &$contents)
227  {
228  $xml = simplexml_load_string($contents);
229  if (!$xml) {
230  return [];
231  }
232 
233  $area = $this->_getAreaByFile($file);
234 
235  $result = [];
236  foreach ((array)$xml->xpath('/layout/child::*') as $element) {
237  $check = $this->_checkDependencyLayoutHandle($currentModule, $area, $element->getName());
238  $modules = isset($check['module']) ? $check['module'] : null;
239  if ($modules) {
240  if (!is_array($modules)) {
241  $modules = [$modules];
242  }
243  foreach ($modules as $module) {
244  $result[$module] = [
246  'source' => $element->getName(),
247  ];
248  }
249  }
250  }
251  return $this->_getUniqueDependencies($result);
252  }
253 
264  protected function _caseLayoutHandleParent($currentModule, $file, &$contents)
265  {
266  $xml = simplexml_load_string($contents);
267  if (!$xml) {
268  return [];
269  }
270 
271  $area = $this->_getAreaByFile($file);
272 
273  $result = [];
274  foreach ((array)$xml->xpath('/layout/child::*/@parent') as $element) {
275  $check = $this->_checkDependencyLayoutHandle($currentModule, $area, (string)$element);
276  $modules = isset($check['module']) ? $check['module'] : null;
277  if ($modules) {
278  if (!is_array($modules)) {
279  $modules = [$modules];
280  }
281  foreach ($modules as $module) {
282  $result[$module] = [
284  'source' => (string)$element,
285  ];
286  }
287  }
288  }
289  return $this->_getUniqueDependencies($result);
290  }
291 
302  protected function _caseLayoutHandleUpdate($currentModule, $file, &$contents)
303  {
304  $xml = simplexml_load_string($contents);
305  if (!$xml) {
306  return [];
307  }
308 
309  $area = $this->_getAreaByFile($file);
310 
311  $result = [];
312  foreach ((array)$xml->xpath('//update/@handle') as $element) {
313  $check = $this->_checkDependencyLayoutHandle($currentModule, $area, (string)$element);
314  $modules = isset($check['module']) ? $check['module'] : null;
315  if ($modules) {
316  if (!is_array($modules)) {
317  $modules = [$modules];
318  }
319  foreach ($modules as $module) {
320  $result[$module] = [
322  'source' => (string)$element,
323  ];
324  }
325  }
326  }
327  return $this->_getUniqueDependencies($result);
328  }
329 
340  protected function _caseLayoutReference($currentModule, $file, &$contents)
341  {
342  $xml = simplexml_load_string($contents);
343  if (!$xml) {
344  return [];
345  }
346 
347  $area = $this->_getAreaByFile($file);
348 
349  $result = [];
350  foreach ((array)$xml->xpath('//reference/@name') as $element) {
351  $check = $this->_checkDependencyLayoutBlock($currentModule, $area, (string)$element);
352  $module = isset($check['module']) ? $check['module'] : null;
353  if ($module) {
354  $result[$module] = [
356  'source' => (string)$element,
357  ];
358  }
359  }
360  return $this->_getUniqueDependencies($result);
361  }
362 
371  protected function _checkDependenciesByRegexp($currentModule, &$contents, $patterns = [])
372  {
373  $result = [];
374 
375  foreach ($patterns as $pattern => $type) {
376  if (preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)) {
377  foreach ($matches as $match) {
378  $module = $match['namespace'] . '\\' . $match['module'];
379 
380  if ($currentModule != $module) {
381  $result[$module] = ['type' => $type, 'source' => $match['source']];
382  }
383  }
384  }
385  }
386  return $this->_getUniqueDependencies($result);
387  }
388 
402  protected function _checkDependencyLayoutHandle($currentModule, $area, $handle)
403  {
404  $chunks = explode('_', $handle);
405  if (count($chunks) > 1) {
406  // Remove 'action' part from handle name
407  array_pop($chunks);
408  }
409  $router = implode('_', $chunks);
410  if (isset($this->_mapRouters[$router])) {
411  // CASE 1: Single dependency
412  $modules = $this->_mapRouters[$router];
413  if (!in_array($currentModule, $modules)) {
414  return ['module' => $modules];
415  }
416  }
417 
418  if (isset($this->_mapLayoutHandles[$area][$handle])) {
419  // CASE 2: No dependencies
420  $modules = $this->_mapLayoutHandles[$area][$handle];
421  if (isset($modules[$currentModule])) {
422  return ['module' => null];
423  }
424 
425  // CASE 3: Single dependency
426  if (1 == count($modules)) {
427  return ['module' => current($modules)];
428  }
429 
430  // CASE 4: Default module dependency
431  $defaultModule = $this->_getDefaultModuleName($area);
432  if (isset($modules[$defaultModule])) {
433  return ['module' => $defaultModule];
434  }
435  }
436 
437  return [];
438  }
439 
453  protected function _checkDependencyLayoutBlock($currentModule, $area, $block)
454  {
455  if (isset($this->_mapLayoutBlocks[$area][$block])) {
456  // CASE 1: No dependencies
457  $modules = $this->_mapLayoutBlocks[$area][$block];
458  if (isset($modules[$currentModule])) {
459  return ['module' => null];
460  }
461 
462  // CASE 2: Single dependency
463  if (1 == count($modules)) {
464  return ['module' => current($modules)];
465  }
466 
467  // CASE 3: Default module dependency
468  $defaultModule = $this->_getDefaultModuleName($area);
469  if (isset($modules[$defaultModule])) {
470  return ['module' => $defaultModule];
471  }
472  }
473  return [];
474  }
475 
482  protected function _getAreaByFile($file)
483  {
484  $area = 'default';
485  if (preg_match('/\/(?<area>adminhtml|frontend)\//', $file, $matches)) {
486  $area = $matches['area'];
487  }
488  return $area;
489  }
490 
497  protected function _getUniqueDependencies($dependencies = [])
498  {
499  $result = [];
500  foreach ($dependencies as $module => $value) {
501  $result[] = ['module' => $module, 'type' => $value['type'], 'source' => $value['source']];
502  }
503  return $result;
504  }
505 
512  protected function _getDefaultModuleName($area = 'default')
513  {
514  if (isset($this->_defaultModules[$area])) {
515  return $this->_defaultModules[$area];
516  }
517  return null;
518  }
519 }
_checkDependenciesByRegexp($currentModule, &$contents, $patterns=[])
Definition: LayoutRule.php:371
$contents
Definition: website.php:14
_caseLayoutHandle($currentModule, $file, &$contents)
Definition: LayoutRule.php:226
$pattern
Definition: website.php:22
_caseAttributeModule($currentModule, &$contents)
Definition: LayoutRule.php:140
getDependencyInfo($currentModule, $fileType, $file, &$contents)
Definition: LayoutRule.php:105
$block
Definition: block.php:8
$type
Definition: item.phtml:13
$value
Definition: gender.phtml:16
_caseLayoutHandleUpdate($currentModule, $file, &$contents)
Definition: LayoutRule.php:302
_checkDependencyLayoutBlock($currentModule, $area, $block)
Definition: LayoutRule.php:453
$attributes
Definition: matrix.phtml:13
_caseLayoutReference($currentModule, $file, &$contents)
Definition: LayoutRule.php:340
_caseElementBlock($currentModule, &$contents)
Definition: LayoutRule.php:161
_checkDependencyLayoutHandle($currentModule, $area, $handle)
Definition: LayoutRule.php:402
$handle
__construct(array $mapRouters, array $mapLayoutBlocks, array $mapLayoutHandles)
Definition: LayoutRule.php:88
_caseElementAction($currentModule, &$contents)
Definition: LayoutRule.php:189
_caseLayoutHandleParent($currentModule, $file, &$contents)
Definition: LayoutRule.php:264
$element
Definition: element.phtml:12