1 | <?php |
---|
2 | |
---|
3 | /* |
---|
4 | * This file is part of the Symfony package. |
---|
5 | * |
---|
6 | * (c) Fabien Potencier <fabien@symfony.com> |
---|
7 | * |
---|
8 | * For the full copyright and license information, please view the LICENSE |
---|
9 | * file that was distributed with this source code. |
---|
10 | */ |
---|
11 | |
---|
12 | namespace Symfony\Component\Console; |
---|
13 | |
---|
14 | use Symfony\Component\Console\Application; |
---|
15 | use Symfony\Component\Console\Input\StringInput; |
---|
16 | use Symfony\Component\Console\Output\ConsoleOutput; |
---|
17 | |
---|
18 | /** |
---|
19 | * A Shell wraps an Application to add shell capabilities to it. |
---|
20 | * |
---|
21 | * This class only works with a PHP compiled with readline support |
---|
22 | * (either --with-readline or --with-libedit) |
---|
23 | * |
---|
24 | * @author Fabien Potencier <fabien@symfony.com> |
---|
25 | */ |
---|
26 | class Shell |
---|
27 | { |
---|
28 | private $application; |
---|
29 | private $history; |
---|
30 | private $output; |
---|
31 | |
---|
32 | /** |
---|
33 | * Constructor. |
---|
34 | * |
---|
35 | * If there is no readline support for the current PHP executable |
---|
36 | * a \RuntimeException exception is thrown. |
---|
37 | * |
---|
38 | * @param Application $application An application instance |
---|
39 | * |
---|
40 | * @throws \RuntimeException When Readline extension is not enabled |
---|
41 | */ |
---|
42 | public function __construct(Application $application) |
---|
43 | { |
---|
44 | if (!function_exists('readline')) { |
---|
45 | throw new \RuntimeException('Unable to start the shell as the Readline extension is not enabled.'); |
---|
46 | } |
---|
47 | |
---|
48 | $this->application = $application; |
---|
49 | $this->history = getenv('HOME').'/.history_'.$application->getName(); |
---|
50 | $this->output = new ConsoleOutput(); |
---|
51 | } |
---|
52 | |
---|
53 | /** |
---|
54 | * Runs the shell. |
---|
55 | */ |
---|
56 | public function run() |
---|
57 | { |
---|
58 | $this->application->setAutoExit(false); |
---|
59 | $this->application->setCatchExceptions(true); |
---|
60 | |
---|
61 | readline_read_history($this->history); |
---|
62 | readline_completion_function(array($this, 'autocompleter')); |
---|
63 | |
---|
64 | $this->output->writeln($this->getHeader()); |
---|
65 | while (true) { |
---|
66 | $command = readline($this->application->getName().' > '); |
---|
67 | |
---|
68 | if (false === $command) { |
---|
69 | $this->output->writeln("\n"); |
---|
70 | |
---|
71 | break; |
---|
72 | } |
---|
73 | |
---|
74 | readline_add_history($command); |
---|
75 | readline_write_history($this->history); |
---|
76 | |
---|
77 | if (0 !== $ret = $this->application->run(new StringInput($command), $this->output)) { |
---|
78 | $this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret)); |
---|
79 | } |
---|
80 | } |
---|
81 | } |
---|
82 | |
---|
83 | /** |
---|
84 | * Tries to return autocompletion for the current entered text. |
---|
85 | * |
---|
86 | * @param string $text The last segment of the entered text |
---|
87 | * @param integer $position The current position |
---|
88 | */ |
---|
89 | private function autocompleter($text, $position) |
---|
90 | { |
---|
91 | $info = readline_info(); |
---|
92 | $text = substr($info['line_buffer'], 0, $info['end']); |
---|
93 | |
---|
94 | if ($info['point'] !== $info['end']) { |
---|
95 | return true; |
---|
96 | } |
---|
97 | |
---|
98 | // task name? |
---|
99 | if (false === strpos($text, ' ') || !$text) { |
---|
100 | return array_keys($this->application->all()); |
---|
101 | } |
---|
102 | |
---|
103 | // options and arguments? |
---|
104 | try { |
---|
105 | $command = $this->application->find(substr($text, 0, strpos($text, ' '))); |
---|
106 | } catch (\Exception $e) { |
---|
107 | return true; |
---|
108 | } |
---|
109 | |
---|
110 | $list = array('--help'); |
---|
111 | foreach ($command->getDefinition()->getOptions() as $option) { |
---|
112 | $list[] = '--'.$option->getName(); |
---|
113 | } |
---|
114 | |
---|
115 | return $list; |
---|
116 | } |
---|
117 | |
---|
118 | /** |
---|
119 | * Returns the shell header. |
---|
120 | * |
---|
121 | * @return string The header string |
---|
122 | */ |
---|
123 | protected function getHeader() |
---|
124 | { |
---|
125 | return <<<EOF |
---|
126 | |
---|
127 | Welcome to the <info>{$this->application->getName()}</info> shell (<comment>{$this->application->getVersion()}</comment>). |
---|
128 | |
---|
129 | At the prompt, type <comment>help</comment> for some help, |
---|
130 | or <comment>list</comment> to get a list available commands. |
---|
131 | |
---|
132 | To exit the shell, type <comment>^D</comment>. |
---|
133 | |
---|
134 | EOF; |
---|
135 | } |
---|
136 | } |
---|