* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\ConsoleOutput; /** * A Shell wraps an Application to add shell capabilities to it. * * Support for history and completion only works with a PHP compiled * with readline support (either --with-readline or --with-libedit) * * @author Fabien Potencier * @author Martin HasoĊˆ */ class Shell { private $application; private $history; private $output; private $hasReadline; private $prompt; /** * Constructor. * * If there is no readline support for the current PHP executable * a \RuntimeException exception is thrown. * * @param Application $application An application instance * * @throws \RuntimeException When Readline extension is not enabled */ public function __construct(Application $application) { $this->hasReadline = function_exists('readline'); $this->application = $application; $this->history = getenv('HOME').'/.history_'.$application->getName(); $this->output = new ConsoleOutput(); $this->prompt = $application->getName().' > '; } /** * Runs the shell. */ public function run() { $this->application->setAutoExit(false); $this->application->setCatchExceptions(true); if ($this->hasReadline) { readline_read_history($this->history); readline_completion_function(array($this, 'autocompleter')); } $this->output->writeln($this->getHeader()); while (true) { $command = $this->readline(); if (false === $command) { $this->output->writeln("\n"); break; } if ($this->hasReadline) { readline_add_history($command); readline_write_history($this->history); } if (0 !== $ret = $this->application->run(new StringInput($command), $this->output)) { $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); } } } /** * Returns the shell header. * * @return string The header string */ protected function getHeader() { return <<{$this->application->getName()} shell ({$this->application->getVersion()}). At the prompt, type help for some help, or list to get a list of available commands. To exit the shell, type ^D. EOF; } /** * Tries to return autocompletion for the current entered text. * * @param string $text The last segment of the entered text * @return Boolean|array A list of guessed strings or true */ private function autocompleter($text) { $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->all()); } // options and arguments? try { $command = $this->application->find(substr($text, 0, strpos($text, ' '))); } catch (\Exception $e) { return true; } $list = array('--help'); foreach ($command->getDefinition()->getOptions() as $option) { $list[] = '--'.$option->getName(); } return $list; } /** * Reads a single line from standard input. * * @return string The single line from standard input */ private function readline() { if ($this->hasReadline) { $line = readline($this->prompt); } else { $this->output->write($this->prompt); $line = fgets(STDIN, 1024); $line = (!$line && strlen($line) == 0) ? false : rtrim($line); } return $line; } }