Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
PhpRule.php
Go to the documentation of this file.
1 <?php
9 
11 
12 class PhpRule implements RuleInterface
13 {
19  private $diFiles;
20 
26  private $pluginMap;
27 
37  protected $_mapRouters = [];
38 
49  protected $_mapLayoutBlocks = [];
50 
56  protected $_defaultModules = [
57  'frontend' => 'Magento\Theme',
58  'adminhtml' => 'Magento\Adminhtml',
59  ];
60 
68  public function __construct(array $mapRouters, array $mapLayoutBlocks, array $pluginMap = [])
69  {
70  $this->_mapRouters = $mapRouters;
71  $this->_mapLayoutBlocks = $mapLayoutBlocks;
72  $this->_namespaces = implode('|', \Magento\Framework\App\Utility\Files::init()->getNamespaces());
73  $this->pluginMap = $pluginMap ?: null;
74  }
75 
85  public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
86  {
87  if (!in_array($fileType, ['php', 'template'])) {
88  return [];
89  }
90 
91  $dependenciesInfo = [];
92  $dependenciesInfo = $this->considerCaseDependencies(
93  $dependenciesInfo,
94  $this->caseClassesAndIdentifiers($currentModule, $file, $contents)
95  );
96  $dependenciesInfo = $this->considerCaseDependencies(
97  $dependenciesInfo,
98  $this->_caseGetUrl($currentModule, $contents)
99  );
100  $dependenciesInfo = $this->considerCaseDependencies(
101  $dependenciesInfo,
102  $this->_caseLayoutBlock($currentModule, $fileType, $file, $contents)
103  );
104  return $dependenciesInfo;
105  }
106 
115  private function caseClassesAndIdentifiers($currentModule, $file, &$contents)
116  {
117  $pattern = '~\b(?<class>(?<module>('
118  . implode(
119  '[_\\\\]|',
120  Files::init()->getNamespaces()
121  )
122  . '[_\\\\])[a-zA-Z0-9]+)'
123  . '(?<class_inside_module>[a-zA-Z0-9_\\\\]*))\b(?:::(?<module_scoped_key>[a-z0-9_]+)[\'"])?~';
124 
125  if (!preg_match_all($pattern, $contents, $matches)) {
126  return [];
127  }
128 
129  $dependenciesInfo = [];
130  $matches['module'] = array_unique($matches['module']);
131  foreach ($matches['module'] as $i => $referenceModule) {
132  $referenceModule = str_replace('_', '\\', $referenceModule);
133  if ($currentModule == $referenceModule) {
134  continue;
135  }
136 
137  $dependencyClass = trim($matches['class'][$i]);
138  if (empty($matches['class_inside_module'][$i]) && !empty($matches['module_scoped_key'][$i])) {
139  $dependencyType = RuleInterface::TYPE_SOFT;
140  } else {
141  $currentClass = $this->getClassFromFilepath($file, $currentModule);
142  $dependencyType = $this->isPluginDependency($currentClass, $dependencyClass)
145  }
146 
147  $dependenciesInfo[] = [
148  'module' => $referenceModule,
149  'type' => $dependencyType,
150  'source' => $dependencyClass,
151  ];
152  }
153 
154  return $dependenciesInfo;
155  }
156 
164  private function getClassFromFilepath($filepath, $module)
165  {
166  $class = strstr($filepath, str_replace(['_', '\\', '/'], DIRECTORY_SEPARATOR, $module));
167  $class = str_replace(DIRECTORY_SEPARATOR, '\\', strstr($class, '.php', true));
168  return $class;
169  }
170 
175  private function loadDiFiles()
176  {
177  if (!$this->diFiles) {
178  $this->diFiles = Files::init()->getDiConfigs();
179  }
180  return $this->diFiles;
181  }
182 
188  private function loadPluginMap()
189  {
190  if (!$this->pluginMap) {
191  foreach ($this->loadDiFiles() as $filepath) {
192  $dom = new \DOMDocument();
193  $dom->loadXML(file_get_contents($filepath));
194  $typeNodes = $dom->getElementsByTagName('type');
196  foreach ($typeNodes as $type) {
198  foreach ($type->getElementsByTagName('plugin') as $plugin) {
199  $subject = $type->getAttribute('name');
200  $pluginType = $plugin->getAttribute('type');
201  $this->pluginMap[$pluginType] = $subject;
202  }
203  }
204  }
205  }
206  return $this->pluginMap;
207  }
208 
218  private function isPluginDependency($dependent, $dependency)
219  {
220  $pluginMap = $this->loadPluginMap();
221  $subject = isset($pluginMap[$dependent])
222  ? $pluginMap[$dependent]
223  : null;
224  if ($subject === $dependency) {
225  return true;
226  } elseif ($subject) {
227  $subjectModule = substr($subject, 0, strpos($subject, '\\', 9)); // (strlen('Magento\\') + 1) === 9
228  return strpos($dependency, $subjectModule) === 0;
229  } else {
230  return false;
231  }
232  }
233 
243  protected function _caseGetUrl($currentModule, &$contents)
244  {
245  $pattern = '/[\->:]+(?<source>getUrl\([\'"](?<router>[\w\/*]+)[\'"])/';
246 
247  $dependencies = [];
248  if (!preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)) {
249  return $dependencies;
250  }
251 
252  foreach ($matches as $item) {
253  $router = str_replace('/', '\\', $item['router']);
254  if (isset($this->_mapRouters[$router])) {
255  $modules = $this->_mapRouters[$router];
256  if (!in_array($currentModule, $modules)) {
257  foreach ($modules as $module) {
258  $dependencies[] = [
259  'module' => $module,
260  'type' => RuleInterface::TYPE_HARD,
261  'source' => $item['source'],
262  ];
263  }
264  }
265  }
266  }
267  return $dependencies;
268  }
269 
279  protected function _caseLayoutBlock($currentModule, $fileType, $file, &$contents)
280  {
281  $pattern = '/[\->:]+(?<source>(?:getBlock|getBlockHtml)\([\'"](?<block>[\w\.\-]+)[\'"]\))/';
282 
283  $area = $this->_getAreaByFile($file, $fileType);
284 
285  $result = [];
286  if (!preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)) {
287  return $result;
288  }
289 
290  foreach ($matches as $match) {
291  if (in_array($match['block'], ['root', 'content'])) {
292  continue;
293  }
294  $check = $this->_checkDependencyLayoutBlock($currentModule, $area, $match['block']);
295  $module = isset($check['module']) ? $check['module'] : null;
296  if ($module) {
297  $result[$module] = [
298  'type' => RuleInterface::TYPE_HARD,
299  'source' => $match['source'],
300  ];
301  }
302  }
303  return $this->_getUniqueDependencies($result);
304  }
305 
313  protected function _getAreaByFile($file, $fileType)
314  {
315  if ($fileType == 'php') {
316  return null;
317  }
318  $area = 'default';
319  if (preg_match('/\/(?<area>adminhtml|frontend)\//', $file, $matches)) {
320  $area = $matches['area'];
321  }
322  return $area;
323  }
324 
338  protected function _checkDependencyLayoutBlock($currentModule, $area, $block)
339  {
340  if (isset($this->_mapLayoutBlocks[$area][$block]) || $area === null) {
341  // CASE 1: No dependencies
342  $modules = [];
343  if ($area === null) {
344  foreach ($this->_mapLayoutBlocks as $blocks) {
345  if (array_key_exists($block, $blocks)) {
346  $modules += $blocks[$block];
347  }
348  }
349  } else {
350  $modules = $this->_mapLayoutBlocks[$area][$block];
351  }
352  if (isset($modules[$currentModule])) {
353  return ['module' => null];
354  }
355  // CASE 2: Single dependency
356  if (1 == count($modules)) {
357  return ['module' => current($modules)];
358  }
359  // CASE 3: Default module dependency
360  $defaultModule = $this->_getDefaultModuleName($area);
361  if (isset($modules[$defaultModule])) {
362  return ['module' => $defaultModule];
363  }
364  }
365  // CASE 4: \Exception - Undefined block
366  return [];
367  }
368 
375  protected function _getDefaultModuleName($area = 'default')
376  {
377  if (isset($this->_defaultModules[$area])) {
378  return $this->_defaultModules[$area];
379  }
380  return null;
381  }
382 
389  protected function _getUniqueDependencies($dependencies = [])
390  {
391  $result = [];
392  foreach ($dependencies as $module => $value) {
393  $result[] = ['module' => $module, 'type' => $value['type'], 'source' => $value['source']];
394  }
395  return $result;
396  }
397 
403  private function considerCaseDependencies($known, $new)
404  {
405  return array_merge($known, $new);
406  }
407 }
$contents
Definition: website.php:14
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$pattern
Definition: website.php:22
$block
Definition: block.php:8
getDependencyInfo($currentModule, $fileType, $file, &$contents)
Definition: PhpRule.php:85
$type
Definition: item.phtml:13
__construct(array $mapRouters, array $mapLayoutBlocks, array $pluginMap=[])
Definition: PhpRule.php:68
$_option $_optionId $class
Definition: date.phtml:13
$value
Definition: gender.phtml:16
_checkDependencyLayoutBlock($currentModule, $area, $block)
Definition: PhpRule.php:338
_caseLayoutBlock($currentModule, $fileType, $file, &$contents)
Definition: PhpRule.php:279
_caseGetUrl($currentModule, &$contents)
Definition: PhpRule.php:243
_getUniqueDependencies($dependencies=[])
Definition: PhpRule.php:389
$i
Definition: gallery.phtml:31