Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
ModuleResolver.php
Go to the documentation of this file.
1 <?php
8 
12 use Symfony\Component\HttpFoundation\Response;
13 
20 {
24  const MODULE_WHITELIST = 'MODULE_WHITELIST';
25 
29  const CUSTOM_MODULE_PATHS = 'CUSTOM_MODULE_PATHS';
30 
34  const PATHS = ['module', 'library', 'theme', 'language'];
35 
39  const REGISTRAR_CLASS = "\Magento\Framework\Component\ComponentRegistrar";
40 
44  const MAGENTO_PREFIX = "Magento_";
45 
51  protected $enabledModules = null;
52 
58  protected $enabledModulePaths = null;
59 
65  protected $configuration;
66 
72  protected $adminTokenUrl = "rest/V1/integration/admin/token";
73 
79  protected $moduleUrl = "rest/V1/modules";
80 
86  protected $versionUrl = "magento_version";
87 
93  protected $knownDirectories = ['SampleData' => 1];
94 
100  private static $instance = null;
101 
107  protected $sequenceSorter;
108 
114  protected $moduleBlacklist = [
115  'SampleTests', 'SampleTemplates'
116  ];
117 
123  public static function getInstance()
124  {
125  if (!self::$instance) {
126  self::$instance = new ModuleResolver();
127  }
128  return self::$instance;
129  }
130 
134  private function __construct()
135  {
137  $this->sequenceSorter = $objectManager->get(
138  \Magento\FunctionalTestingFramework\Util\ModuleResolver\SequenceSorterInterface::class
139  );
140  }
141 
147  public function getEnabledModules()
148  {
149  if (isset($this->enabledModules)) {
150  return $this->enabledModules;
151  }
152 
154  $this->printMagentoVersionInfo();
155  }
156 
157  $token = $this->getAdminToken();
158 
159  $url = ConfigSanitizerUtil::sanitizeUrl(getenv('MAGENTO_BASE_URL')) . $this->moduleUrl;
160 
161  $headers = [
162  'Authorization: Bearer ' . $token,
163  ];
164  $ch = curl_init($url);
165  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
166  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
167  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
168  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
169  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
170  $response = curl_exec($ch);
171 
172  if (!$response) {
173  $message = "Could not retrieve Modules from Magento Instance.";
174  $context = [
175  "Admin Module List Url" => $url,
176  "MAGENTO_ADMIN_USERNAME" => getenv("MAGENTO_ADMIN_USERNAME"),
177  "MAGENTO_ADMIN_PASSWORD" => getenv("MAGENTO_ADMIN_PASSWORD"),
178  ];
179  throw new TestFrameworkException($message, $context);
180  }
181 
182  $this->enabledModules = json_decode($response);
183 
184  return $this->enabledModules;
185  }
186 
192  protected function getModuleWhitelist()
193  {
194  $moduleWhitelist = getenv(self::MODULE_WHITELIST);
195 
196  if (empty($moduleWhitelist)) {
197  return [];
198  }
199  return array_map('trim', explode(',', $moduleWhitelist));
200  }
201 
207  public function getModulesPath()
208  {
209  if (isset($this->enabledModulePaths)) {
211  }
212 
213  $allModulePaths = $this->aggregateTestModulePaths();
214 
215  if (MftfApplicationConfig::getConfig()->forceGenerateEnabled()) {
216  $this->enabledModulePaths = $this->applyCustomModuleMethods($allModulePaths);
218  }
219 
220  $enabledModules = array_merge($this->getEnabledModules(), $this->getModuleWhitelist());
221  $enabledDirectoryPaths = $this->getEnabledDirectoryPaths($enabledModules, $allModulePaths);
222 
223  $this->enabledModulePaths = $this->applyCustomModuleMethods($enabledDirectoryPaths);
225  }
226 
232  private function aggregateTestModulePaths()
233  {
234  $allModulePaths = [];
235 
236  // Define the Module paths from magento bp
237  $magentoBaseCodePath = MAGENTO_BP;
238 
239  // Define the Module paths from default TESTS_MODULE_PATH
240  $modulePath = defined('TESTS_MODULE_PATH') ? TESTS_MODULE_PATH : TESTS_BP;
241  $modulePath = rtrim($modulePath, DIRECTORY_SEPARATOR);
242 
243  $vendorCodePath = DIRECTORY_SEPARATOR . "vendor";
244  $appCodePath = DIRECTORY_SEPARATOR . "app" . DIRECTORY_SEPARATOR . "code";
245 
246  $codePathsToPattern = [
247  $modulePath => '',
248  $magentoBaseCodePath . $vendorCodePath => 'Test' . DIRECTORY_SEPARATOR . 'Mftf',
249  $magentoBaseCodePath . $appCodePath => 'Test' . DIRECTORY_SEPARATOR . 'Mftf'
250  ];
251 
252  foreach ($codePathsToPattern as $codePath => $pattern) {
253  $allModulePaths = array_merge_recursive($allModulePaths, $this->globRelevantPaths($codePath, $pattern));
254  }
255 
256  return $allModulePaths;
257  }
258 
268  private function globRelevantPaths($testPath, $pattern)
269  {
270  $modulePaths = [];
271  $relevantPaths = [];
272 
273  if (file_exists($testPath)) {
274  $relevantPaths = $this->globRelevantWrapper($testPath, $pattern);
275  }
276 
277  $allComponents = $this->getRegisteredModuleList();
278 
279  foreach ($relevantPaths as $codePath) {
280  $mainModName = array_search($codePath, $allComponents) ?: basename(str_replace($pattern, '', $codePath));
281  $mainModName = str_replace(self::MAGENTO_PREFIX, "", $mainModName);
282  $modulePaths[$mainModName][] = $codePath;
283 
284  if (MftfApplicationConfig::getConfig()->verboseEnabled()) {
285  LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->debug(
286  "including module",
287  ['module' => $mainModName, 'path' => $codePath]
288  );
289  }
290  }
291 
292  return $modulePaths;
293  }
294 
302  private static function globRelevantWrapper($testPath, $pattern)
303  {
304  if ($pattern == "") {
305  return glob($testPath . '*' . DIRECTORY_SEPARATOR . '*' . $pattern);
306  }
307  $subDirectory = "*" . DIRECTORY_SEPARATOR;
308  $directories = glob($testPath . $subDirectory . $pattern, GLOB_ONLYDIR);
309  foreach (glob($testPath . $subDirectory, GLOB_ONLYDIR) as $dir) {
310  $directories = array_merge_recursive($directories, self::globRelevantWrapper($dir, $pattern));
311  }
312  return $directories;
313  }
314 
321  private function flattenAllModulePaths($modulePaths)
322  {
323  $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($modulePaths));
324  $resultArray = [];
325 
326  foreach ($it as $value) {
327  $resultArray[] = $value;
328  }
329 
330  return $resultArray;
331  }
332 
339  private function getEnabledDirectoryPaths($enabledModules, $allModulePaths)
340  {
341  $enabledDirectoryPaths = [];
342  foreach ($enabledModules as $magentoModuleName) {
343  // Magento_Backend -> Backend or DevDocs -> DevDocs (if whitelisted has no underscore)
344  $moduleShortName = explode('_', $magentoModuleName)[1] ?? $magentoModuleName;
345  if (!isset($this->knownDirectories[$moduleShortName]) && !isset($allModulePaths[$moduleShortName])) {
346  continue;
347  } elseif (isset($this->knownDirectories[$moduleShortName]) && !isset($allModulePaths[$moduleShortName])) {
348  LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->warn(
349  "Known directory could not match to an existing path.",
350  ['knownDirectory' => $moduleShortName]
351  );
352  } else {
353  $enabledDirectoryPaths[$moduleShortName] = $allModulePaths[$moduleShortName];
354  }
355  }
356  return $enabledDirectoryPaths;
357  }
358 
364  private function printMagentoVersionInfo()
365  {
366  if (MftfApplicationConfig::getConfig()->forceGenerateEnabled()) {
367  return;
368  }
369  $url = ConfigSanitizerUtil::sanitizeUrl(getenv('MAGENTO_BASE_URL')) . $this->versionUrl;
370  LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info(
371  "Fetching version information.",
372  ['url' => $url]
373  );
374 
375  $ch = curl_init($url);
376  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
377  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
378  $response = curl_exec($ch);
379 
380  if (!$response) {
381  $response = "No version information available.";
382  }
383 
384  LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info(
385  'version information',
386  ['version' => $response]
387  );
388  }
389 
395  protected function getAdminToken()
396  {
397  $login = $_ENV['MAGENTO_ADMIN_USERNAME'] ?? null;
398  $password = $_ENV['MAGENTO_ADMIN_PASSWORD'] ?? null;
399  if (!$login || !$password || !isset($_ENV['MAGENTO_BASE_URL'])) {
400  $message = "Cannot retrieve API token without credentials and base url, please fill out .env.";
401  $context = [
402  "MAGENTO_BASE_URL" => getenv("MAGENTO_BASE_URL"),
403  "MAGENTO_ADMIN_USERNAME" => getenv("MAGENTO_ADMIN_USERNAME"),
404  "MAGENTO_ADMIN_PASSWORD" => getenv("MAGENTO_ADMIN_PASSWORD"),
405  ];
406  throw new TestFrameworkException($message, $context);
407  }
408 
409  $url = ConfigSanitizerUtil::sanitizeUrl($_ENV['MAGENTO_BASE_URL']) . $this->adminTokenUrl;
410  $data = [
411  'username' => $login,
412  'password' => $password
413  ];
414  $headers = [
415  'Content-Type: application/json',
416  ];
417 
418  $ch = curl_init($url);
419  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
420  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
421  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
422  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
423  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
424  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
425 
426  $response = curl_exec($ch);
427  $responseCode = curl_getinfo($ch)['http_code'];
428 
429  if ($responseCode !== 200) {
430  if ($responseCode == 0) {
431  $details = "Could not find Magento Instance at given MAGENTO_BASE_URL";
432  } else {
433  $details = $responseCode . " " . Response::$statusTexts[$responseCode];
434  }
435 
436  $message = "Could not retrieve API token from Magento Instance ({$details})";
437  $context = [
438  "tokenUrl" => $url,
439  "responseCode" => $responseCode,
440  "MAGENTO_ADMIN_USERNAME" => getenv("MAGENTO_ADMIN_USERNAME"),
441  "MAGENTO_ADMIN_PASSWORD" => getenv("MAGENTO_ADMIN_PASSWORD"),
442  ];
443  throw new TestFrameworkException($message, $context);
444  }
445 
446  return json_decode($response);
447  }
448 
455  public function sortFilesByModuleSequence(array $files)
456  {
457  return $this->sequenceSorter->sort($files);
458  }
459 
466  protected function applyCustomModuleMethods($modulesPath)
467  {
468  $modulePathsResult = $this->removeBlacklistModules($modulesPath);
469  $customModulePaths = $this->getCustomModulePaths();
470 
471  array_map(function ($value) {
472  LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info(
473  "including custom module",
474  ['module' => $value]
475  );
476  }, $customModulePaths);
477 
478  return $this->flattenAllModulePaths(array_merge($modulePathsResult, $customModulePaths));
479  }
480 
487  private function removeBlacklistModules($modulePaths)
488  {
489  $modulePathsResult = $modulePaths;
490  foreach ($modulePathsResult as $moduleName => $modulePath) {
491  if (in_array($moduleName, $this->getModuleBlacklist())) {
492  unset($modulePathsResult[$moduleName]);
493  LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->info(
494  "excluding module",
495  ['module' => $moduleName]
496  );
497  }
498  }
499 
500  return $modulePathsResult;
501  }
502 
508  private function getCustomModulePaths()
509  {
510  $customModulePaths = getenv(self::CUSTOM_MODULE_PATHS);
511 
512  if (!$customModulePaths) {
513  return [];
514  }
515 
516  return array_map('trim', explode(',', $customModulePaths));
517  }
518 
524  private function getModuleBlacklist()
525  {
526  return $this->moduleBlacklist;
527  }
528 
534  private function getRegisteredModuleList()
535  {
536  if (array_key_exists('MAGENTO_BP', $_ENV)) {
537  $autoloadPath = realpath(MAGENTO_BP . "/app/autoload.php");
538  if ($autoloadPath) {
539  require_once($autoloadPath);
540  } else {
541  throw new TestFrameworkException("Magento app/autoload.php not found with given MAGENTO_BP:"
542  . MAGENTO_BP);
543  }
544  }
545 
546  try {
547  $allComponents = [];
548  if (!class_exists(self::REGISTRAR_CLASS)) {
549  throw new TestFrameworkException("Magento Installation not found when loading registered modules.\n");
550  }
551  $components = new \Magento\Framework\Component\ComponentRegistrar();
552  foreach (self::PATHS as $componentType) {
553  $allComponents = array_merge($allComponents, $components->getPaths($componentType));
554  }
555  array_walk($allComponents, function (&$value) {
556  $value .= DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR . 'Mftf';
557  });
558  return $allComponents;
559  } catch (TestFrameworkException $e) {
560  LoggingUtil::getInstance()->getLogger(ModuleResolver::class)->warning(
561  "$e"
562  );
563  }
564  return [];
565  }
566 }
$response
Definition: 404.php:11
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$objectManager
Definition: bootstrap.php:17
$pattern
Definition: website.php:22
$details
Definition: vault.phtml:10
$message
$value
Definition: gender.phtml:16
foreach($appDirs as $dir) $files