/vendor/codeception/codeception/src/Codeception/Lib/ModuleContainer.php

https://gitlab.com/merial/WETE_Ryhma3 · PHP · 244 lines · 208 code · 21 blank · 15 comment · 11 complexity · ba5978cfa3d40ef1c7f6cdb4fdebe644 MD5 · raw file

  1. <?php
  2. namespace Codeception\Lib;
  3. use Codeception\Configuration;
  4. use Codeception\Exception\ConfigurationException as ConfigurationException;
  5. use Codeception\Exception\ModuleException as ModuleException;
  6. use Codeception\Exception\ModuleConflictException as ModuleConflictException;
  7. use Codeception\Exception\ModuleRequireException;
  8. use Codeception\Lib\Interfaces\ConflictsWithModule;
  9. use Codeception\Lib\Interfaces\DependsOnModule;
  10. use Codeception\Lib\Interfaces\PartedModule;
  11. use Codeception\Module;
  12. use Codeception\Util\Annotation;
  13. class ModuleContainer
  14. {
  15. const MODULE_NAMESPACE = '\\Codeception\\Module\\';
  16. protected $config;
  17. /**
  18. * @var Di
  19. */
  20. protected $di;
  21. protected $modules = [];
  22. protected $active = [];
  23. protected $actions = [];
  24. public function __construct(Di $di, $config)
  25. {
  26. $this->di = $di;
  27. $this->di->set($this);
  28. $this->config = $config;
  29. }
  30. /**
  31. * @param $moduleName
  32. * @param bool $active
  33. * @throws ConfigurationException
  34. * @return Module
  35. */
  36. public function create($moduleName, $active = true)
  37. {
  38. $this->active[$moduleName] = $active;
  39. $config = $this->getModuleConfig($moduleName);
  40. // skip config validation on dependent module
  41. if (empty($config) && !$active) {
  42. $config = null;
  43. }
  44. // helper
  45. $hasNamespace = (strpos($moduleName, '\\') !== false);
  46. if ($hasNamespace) {
  47. return $this->instantiate($moduleName, $moduleName, $config);
  48. }
  49. // standard module
  50. $moduleClass = self::MODULE_NAMESPACE . $moduleName;
  51. if (class_exists($moduleClass)) {
  52. return $this->instantiate($moduleName, $moduleClass, $config);
  53. }
  54. // (deprecated) try find module under namespace setting
  55. $namespace = isset($this->config['namespace']) ? $this->config['namespace'] : '';
  56. $moduleClass = $namespace . self::MODULE_NAMESPACE . $moduleName;
  57. if (class_exists($moduleClass)) {
  58. return $this->instantiate($moduleName, $moduleClass, $config);
  59. }
  60. throw new ConfigurationException("Module $moduleName could not be found and loaded");
  61. }
  62. public function hasModule($module)
  63. {
  64. return isset($this->modules[$module]);
  65. }
  66. public function getModule($module)
  67. {
  68. if (!$this->hasModule($module)) {
  69. throw new ModuleException(__CLASS__, "Module $module couldn't be connected");
  70. }
  71. return $this->modules[$module];
  72. }
  73. public function moduleForAction($action)
  74. {
  75. if (!isset($this->actions[$action])) {
  76. return null;
  77. }
  78. return $this->modules[$this->actions[$action]];
  79. }
  80. public function getActions()
  81. {
  82. return $this->actions;
  83. }
  84. public function all()
  85. {
  86. return $this->modules;
  87. }
  88. private function instantiate($name, $class, $config)
  89. {
  90. $module = $this->di->instantiate($class, [$this, $config], false);
  91. $this->modules[$name] = $module;
  92. if (!$this->active[$name]) {
  93. // if module is not active, its actions should not be included into actor class
  94. return $module;
  95. }
  96. if ($module instanceof DependsOnModule) {
  97. $this->injectDependentModule($name, $module);
  98. }
  99. $class = new \ReflectionClass($module);
  100. $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
  101. foreach ($methods as $method) {
  102. $inherit = $class->getStaticPropertyValue('includeInheritedActions');
  103. $only = $class->getStaticPropertyValue('onlyActions');
  104. $exclude = $class->getStaticPropertyValue('excludeActions');
  105. // exclude methods when they are listed as excluded
  106. if (in_array($method->name, $exclude)) {
  107. continue;
  108. }
  109. if (!empty($only)) {
  110. // skip if method is not listed
  111. if (!in_array($method->name, $only)) {
  112. continue;
  113. }
  114. } else {
  115. // skip if method is inherited and inheritActions == false
  116. if (!$inherit && $method->getDeclaringClass() != $class) {
  117. continue;
  118. }
  119. }
  120. // those with underscore at the beginning are considered as hidden
  121. if (strpos($method->name, '_') === 0) {
  122. continue;
  123. }
  124. if ($module instanceof PartedModule && isset($config['part'])) {
  125. if (!$this->moduleActionBelongsToPart($module, $method->name, $config['part'])) {
  126. continue;
  127. }
  128. }
  129. $this->actions[$method->name] = $name;
  130. }
  131. return $module;
  132. }
  133. public function injectDependentModule($name, DependsOnModule $module)
  134. {
  135. $message = '';
  136. $dependency = $module->_depends();
  137. if (empty($dependency)) {
  138. return;
  139. }
  140. if (is_array($dependency)) {
  141. $message = reset($dependency);
  142. $dependency = key($dependency);
  143. }
  144. $config = $this->getModuleConfig($name);
  145. if (!isset($config['depends'])) {
  146. throw new ModuleRequireException($module,
  147. "\nThis module depends on $dependency\n" .
  148. "\n \n$message");
  149. }
  150. $dependentModule = $this->create($config['depends'], false);
  151. if (!method_exists($module, '_inject')) {
  152. throw new ModuleException($module, 'Module requires method _inject to be defined to accept dependencies');
  153. }
  154. $module->_inject($dependentModule);
  155. $dependentModule->_setConfig([]);
  156. }
  157. public function validateConflicts()
  158. {
  159. $moduleNames = array_keys($this->modules);
  160. for ($i = 0; $i < count($this->modules); $i++) {
  161. /** @var $currentModule Module **/
  162. $currentModule = $this->modules[$moduleNames[$i]];
  163. if (!$currentModule instanceof ConflictsWithModule) {
  164. continue;
  165. }
  166. for ($j = $i; $j < count($this->modules); $j++) {
  167. $inspectedModule = $this->modules[$moduleNames[$j]];
  168. $nameAndInterfaces = array_merge([get_class($inspectedModule), $inspectedModule->_getName()], class_implements($inspectedModule));
  169. if (in_array(ltrim($currentModule->_conflicts(), '\\'), $nameAndInterfaces)) {
  170. throw new ModuleConflictException($currentModule, $inspectedModule);
  171. }
  172. }
  173. }
  174. }
  175. protected function moduleActionBelongsToPart($module, $action, $part)
  176. {
  177. if (!is_array($part)) {
  178. $part = [strtolower($part)];
  179. }
  180. $part = array_map('strtolower', $part);
  181. $parts = Annotation::forMethod($module, $action)->fetchAll('part');
  182. $usedParts = array_intersect($parts, $part);
  183. return !empty($usedParts);
  184. }
  185. protected function getModuleConfig($module)
  186. {
  187. // get config for all modules
  188. $config = isset($this->config['modules']['config'][$module])
  189. ? $this->config['modules']['config'][$module]
  190. : [];
  191. if (!isset($this->config['modules']['enabled'])) {
  192. return $config;
  193. }
  194. if (!is_array($this->config['modules']['enabled'])) {
  195. return $config;
  196. }
  197. // get config for enabled modules
  198. foreach ($this->config['modules']['enabled'] as $enabledModuleConfig) {
  199. if (!is_array($enabledModuleConfig)) {
  200. continue;
  201. }
  202. $enabledModuleName = key($enabledModuleConfig);
  203. if ($enabledModuleName !== $module) {
  204. continue;
  205. }
  206. $config = Configuration::mergeConfigs(reset($enabledModuleConfig), $config);
  207. }
  208. return $config;
  209. }
  210. }