4 * This file is part of the Behat Testwork.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
11 namespace Behat\Testwork\Cli;
13 use Behat\Testwork\ServiceContainer\Configuration\ConfigurationLoader;
14 use Behat\Testwork\ServiceContainer\ContainerLoader;
15 use Behat\Testwork\ServiceContainer\Exception\ConfigurationLoadingException;
16 use Behat\Testwork\ServiceContainer\ExtensionManager;
17 use Symfony\Component\Console\Application as BaseApplication;
18 use Symfony\Component\Console\Command\Command as SymfonyCommand;
19 use Symfony\Component\Console\Input\ArrayInput;
20 use Symfony\Component\Console\Input\InputDefinition;
21 use Symfony\Component\Console\Input\InputInterface;
22 use Symfony\Component\Console\Input\InputOption;
23 use Symfony\Component\Console\Output\OutputInterface;
24 use Symfony\Component\DependencyInjection\ContainerBuilder;
25 use Symfony\Component\DependencyInjection\ContainerInterface;
28 * Extends Symfony console application with testwork functionality.
30 * @author Konstantin Kudryashov <ever.zet@gmail.com>
32 final class Application extends BaseApplication
35 * @var ConfigurationLoader
37 private $configurationLoader;
39 * @var ExtensionManager
41 private $extensionManager;
44 * Initializes application.
47 * @param string $version
48 * @param ConfigurationLoader $configLoader
49 * @param ExtensionManager $extensionManager
51 public function __construct($name, $version, ConfigurationLoader $configLoader, ExtensionManager $extensionManager)
53 putenv('COLUMNS=9999');
55 $this->configurationLoader = $configLoader;
56 $this->extensionManager = $extensionManager;
58 parent::__construct($name, $version);
62 * Gets the default input definition.
64 * @return InputDefinition An InputDefinition instance
66 public function getDefaultInputDefinition()
68 return new InputDefinition(array(
69 new InputOption('--profile', '-p', InputOption::VALUE_REQUIRED, 'Specify config profile to use.'),
70 new InputOption('--config', '-c', InputOption::VALUE_REQUIRED, 'Specify config file to use.'),
72 '--verbose', '-v', InputOption::VALUE_OPTIONAL,
73 'Increase verbosity of exceptions.' . PHP_EOL .
74 'Use -vv or --verbose=2 to display backtraces in addition to exceptions.'
76 new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
77 new InputOption('--config-reference', null, InputOption::VALUE_NONE, 'Display the configuration reference.'),
78 new InputOption('--debug', null, InputOption::VALUE_NONE, 'Provide debugging information about current environment.'),
79 new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display version.'),
80 new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'),
82 '--colors', null, InputOption::VALUE_NONE,
83 'Force ANSI color in the output. By default color support is' . PHP_EOL .
84 'guessed based on your platform and the output if not specified.'
86 new InputOption('--no-colors', null, InputOption::VALUE_NONE, 'Force no ANSI color in the output.'),
91 * Runs the current application.
93 * @param InputInterface $input An Input instance
94 * @param OutputInterface $output An Output instance
96 * @return integer 0 if everything went fine, or an error code
98 public function doRun(InputInterface $input, OutputInterface $output)
100 // xdebug's default nesting level of 100 is not enough
101 if (extension_loaded('xdebug')
102 && false === strpos(ini_get('disable_functions'), 'ini_set')
104 $oldValue = ini_get('xdebug.max_nesting_level');
105 if ($oldValue === false || $oldValue < 256) {
106 ini_set('xdebug.max_nesting_level', 256);
110 if ($input->hasParameterOption(array('--config-reference'))) {
111 $input = new ArrayInput(array('--config-reference' => true));
114 if ($path = $input->getParameterOption(array('--config', '-c'))) {
115 if (!is_file($path)) {
116 throw new ConfigurationLoadingException("The requested config file does not exist");
119 $this->configurationLoader->setConfigurationFilePath($path);
122 $this->add($this->createCommand($input, $output));
124 return parent::doRun($input, $output);
127 protected function getDefaultCommands()
129 $commands = parent::getDefaultCommands();
131 $commands[] = new DumpReferenceCommand($this->extensionManager);
132 $commands[] = new DebugCommand($this, $this->configurationLoader, $this->extensionManager);
138 * Configures container based on provided config file and profile.
140 * @param InputInterface $input
144 private function loadConfiguration(InputInterface $input)
146 $profile = $input->getParameterOption(array('--profile', '-p')) ? : 'default';
148 return $this->configurationLoader->loadConfiguration($profile);
152 * Creates main command for application.
154 * @param InputInterface $input
155 * @param OutputInterface $output
157 * @return SymfonyCommand
159 private function createCommand(InputInterface $input, OutputInterface $output)
161 return $this->createContainer($input, $output)->get('cli.command');
165 * Creates container instance, loads extensions and freezes it.
167 * @param InputInterface $input
168 * @param OutputInterface $output
170 * @return ContainerInterface
172 private function createContainer(InputInterface $input, OutputInterface $output)
174 $basePath = rtrim($this->getBasePath(), DIRECTORY_SEPARATOR);
176 $container = new ContainerBuilder();
178 $container->setParameter('cli.command.name', $this->getName());
179 $container->setParameter('paths.base', $basePath);
181 $container->set('cli.input', $input);
182 $container->set('cli.output', $output);
184 $extension = new ContainerLoader($this->extensionManager);
185 $extension->load($container, $this->loadConfiguration($input));
186 $container->addObjectResource($extension);
187 $container->compile(true);
197 private function getBasePath()
199 if ($configPath = $this->configurationLoader->getConfigurationFilePath()) {
200 return realpath(dirname($configPath));
203 return realpath(getcwd());
207 * Gets the name of the command based on input.
209 * @param InputInterface $input The input interface
211 * @return string The command name
213 protected function getCommandName(InputInterface $input)
215 if ($input->hasParameterOption(array('--config-reference'))) {
216 return 'dump-reference';
219 if ($input->hasParameterOption(array('--debug'))) {
223 return $this->getName();
226 protected function configureIO(InputInterface $input, OutputInterface $output)
228 if (true === $input->hasParameterOption(array('--colors'))) {
229 $output->setDecorated(true);
230 } elseif (true === $input->hasParameterOption(array('--no-colors'))) {
231 $output->setDecorated(false);
234 parent::configureIO($input, $output);