Application.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. namespace Codeception;
  3. use Codeception\Exception\ConfigurationException;
  4. use Symfony\Component\Console\Application as BaseApplication;
  5. use Symfony\Component\Console\Input\InputDefinition;
  6. use Symfony\Component\Console\Input\InputInterface;
  7. use Symfony\Component\Console\Input\InputOption;
  8. use Symfony\Component\Console\Output\ConsoleOutput;
  9. use Symfony\Component\Console\Input\ArgvInput;
  10. use Symfony\Component\Console\Output\OutputInterface;
  11. class Application extends BaseApplication
  12. {
  13. /**
  14. * @var ArgvInput
  15. */
  16. protected $coreArguments = null;
  17. /**
  18. * Register commands from config file
  19. *
  20. * extensions:
  21. * commands:
  22. * - Project\Command\MyCustomCommand
  23. *
  24. */
  25. public function registerCustomCommands()
  26. {
  27. try {
  28. $this->readCustomCommandsFromConfig();
  29. } catch (ConfigurationException $e) {
  30. if ($e->getCode() === 404) {
  31. return;
  32. }
  33. $this->renderException($e, new ConsoleOutput());
  34. exit(1);
  35. } catch (\Exception $e) {
  36. $this->renderException($e, new ConsoleOutput());
  37. exit(1);
  38. }
  39. }
  40. /**
  41. * Search custom commands and register them.
  42. *
  43. * @throws ConfigurationException
  44. */
  45. protected function readCustomCommandsFromConfig()
  46. {
  47. $this->getCoreArguments(); // Maybe load outside configfile
  48. $config = Configuration::config();
  49. if (empty($config['extensions']['commands'])) {
  50. return;
  51. }
  52. foreach ($config['extensions']['commands'] as $commandClass) {
  53. $commandName = $this->getCustomCommandName($commandClass);
  54. $this->add(new $commandClass($commandName));
  55. }
  56. }
  57. /**
  58. * Validate and get the name of the command
  59. *
  60. * @param CustomCommandInterface $commandClass
  61. *
  62. * @throws ConfigurationException
  63. *
  64. * @return string
  65. */
  66. protected function getCustomCommandName($commandClass)
  67. {
  68. if (!class_exists($commandClass)) {
  69. throw new ConfigurationException("Extension: Command class $commandClass not found");
  70. }
  71. $interfaces = class_implements($commandClass);
  72. if (!in_array('Codeception\CustomCommandInterface', $interfaces)) {
  73. throw new ConfigurationException("Extension: Command {$commandClass} must implement " .
  74. "the interface `Codeception\\CustomCommandInterface`");
  75. }
  76. return $commandClass::getCommandName();
  77. }
  78. /**
  79. * To cache Class ArgvInput
  80. *
  81. * @inheritDoc
  82. */
  83. public function run(InputInterface $input = null, OutputInterface $output = null)
  84. {
  85. if ($input === null) {
  86. $input = $this->getCoreArguments();
  87. }
  88. return parent::run($input, $output);
  89. }
  90. /**
  91. * Add global a --config option.
  92. *
  93. * @return InputDefinition
  94. */
  95. protected function getDefaultInputDefinition()
  96. {
  97. $inputDefinition = parent::getDefaultInputDefinition();
  98. $inputDefinition->addOption(
  99. new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config')
  100. );
  101. return $inputDefinition;
  102. }
  103. /**
  104. * Search for --config Option and if found will be loaded
  105. *
  106. * example:
  107. * -c file.yml|dir
  108. * -cfile.yml|dir
  109. * --config file.yml|dir
  110. * --config=file.yml|dir
  111. *
  112. * @return ArgvInput
  113. */
  114. protected function getCoreArguments()
  115. {
  116. if ($this->coreArguments !== null) {
  117. return $this->coreArguments;
  118. }
  119. $argv = $_SERVER['argv'];
  120. $argvWithoutConfig = [];
  121. for ($i = 0; $i < count($argv); $i++) {
  122. if (preg_match('/^(?:-([^c-]*)?c|--config(?:=|$))(.*)$/', $argv[$i], $match)) {
  123. if (!empty($match[2])) { //same index
  124. $this->preloadConfiguration($match[2]);
  125. } elseif (isset($argv[$i + 1])) { //next index
  126. $this->preloadConfiguration($argv[++$i]);
  127. }
  128. if (!empty($match[1])) {
  129. $argvWithoutConfig[] = "-".$match[1]; //rest commands
  130. }
  131. continue;
  132. }
  133. $argvWithoutConfig[] = $argv[$i];
  134. }
  135. return $this->coreArguments = new ArgvInput($argvWithoutConfig);
  136. }
  137. /**
  138. * Pre load Configuration, the config option is use.
  139. *
  140. * @param string $configFile Path to Configuration
  141. *
  142. * @throws ConfigurationException
  143. */
  144. protected function preloadConfiguration($configFile)
  145. {
  146. try {
  147. Configuration::config($configFile);
  148. } catch (ConfigurationException $e) {
  149. if ($e->getCode() == 404) {
  150. throw new ConfigurationException("Your configuration file `{$configFile}` could not be found.", 405);
  151. }
  152. throw $e;
  153. }
  154. }
  155. }