|
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 } |