diff --git a/src/Symfony/Components/Console/Command/Command.php b/src/Symfony/Components/Console/Command/Command.php index 19b42a131df9..013b239cbfab 100644 --- a/src/Symfony/Components/Console/Command/Command.php +++ b/src/Symfony/Components/Console/Command/Command.php @@ -196,6 +196,16 @@ public function setDefinition($definition) return $this; } + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition $definition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + /** * Adds an argument. * diff --git a/src/Symfony/Components/Console/Shell.php b/src/Symfony/Components/Console/Shell.php new file mode 100644 index 000000000000..08e0152ffc13 --- /dev/null +++ b/src/Symfony/Components/Console/Shell.php @@ -0,0 +1,146 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * A Shell wraps an Application to add shell capabilities to it. + * + * This class only works with a PHP compiled with readline support + * (either --with-readline or --with-libedit) + * + * @package symfony + * @subpackage cli + * @author Fabien Potencier + */ +class Shell +{ + protected $application; + protected $history; + protected $output; + + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + */ + public function __construct(Application $application) + { + if (!function_exists('readline')) + { + throw new \RuntimeException('Unable to start the shell as the Readline extension is not enabled.'); + } + + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); + } + + /** + * Runs the shell. + */ + public function run() + { + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); + + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); + + $this->output->write($this->getHeader()); + while (true) + { + $command = readline($this->application->getName().' > '); + + if (false === $command) + { + $this->output->write("\n"); + + break; + } + + readline_add_history($command); + readline_write_history($this->history); + + if (0 !== $ret = $this->application->run(new StringInput($command), $this->output)) + { + $this->output->write(sprintf('The command terminated with an error status (%s)', $ret)); + } + } + } + + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * @param integer $position The current position + */ + protected function autocompleter($text, $position) + { + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) + { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) + { + return array_keys($this->application->getCommands()); + } + + // options and arguments? + try + { + $command = $this->application->findCommand(substr($text, 0, strpos($text, ' '))); + } + catch (\Exception $e) + { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) + { + $list[] = '--'.$option->getName(); + } + + return $list; + } + + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return <<{$this->application->getName()} shell. + +At the prompt, type help for some help, +or list to get a list available commands. + +To exist the shell, type ^D. + +EOF; + } +} diff --git a/tests/fixtures/Symfony/Components/Console/TestCommand.php b/tests/fixtures/Symfony/Components/Console/TestCommand.php index b42544f75352..c0c02dd28012 100644 --- a/tests/fixtures/Symfony/Components/Console/TestCommand.php +++ b/tests/fixtures/Symfony/Components/Console/TestCommand.php @@ -26,11 +26,6 @@ public function getApplication() return $this->application; } - public function getDefinition() - { - return $this->definition; - } - protected function execute(InputInterface $input, OutputInterface $output) { $output->write('execute called'); diff --git a/tests/unit/Symfony/Components/Console/Command/CommandTest.php b/tests/unit/Symfony/Components/Console/Command/CommandTest.php index e3c921577582..b3f734b46f5c 100644 --- a/tests/unit/Symfony/Components/Console/Command/CommandTest.php +++ b/tests/unit/Symfony/Components/Console/Command/CommandTest.php @@ -50,8 +50,8 @@ $command->setApplication($application); $t->is($command->getApplication(), $application, '->setApplication() sets the current application'); -// ->setDefinition() -$t->diag('->setDefinition()'); +// ->setDefinition() ->getDefinition() +$t->diag('->setDefinition() ->getDefinition()'); $ret = $command->setDefinition($definition = new InputDefinition()); $t->is($ret, $command, '->setDefinition() implements a fluent interface'); $t->is($command->getDefinition(), $definition, '->setDefinition() sets the current InputDefinition instance');