Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
DialogHelper.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\Console\Helper;
013
014 use Symfony\Component\Console\Output\OutputInterface;
015 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
016
017 /**
018 * The Dialog class provides helpers to interact with the user.
019 *
020 * @author Fabien Potencier <fabien@symfony.com>
021 */
022 class DialogHelper extends Helper
023 {
024 private $inputStream;
025 private static $shell;
026 private static $stty;
027
028 /**
029 * Asks the user to select a value.
030 *
031 * @param OutputInterface $output An Output instance
032 * @param string|array $question The question to ask
033 * @param array $choices List of choices to pick from
034 * @param bool|string $default The default answer if the user enters nothing
035 * @param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite)
036 * @param string $errorMessage Message which will be shown if invalid value from choice list would be picked
037 * @param bool $multiselect Select more than one value separated by comma
038 *
039 * @return int|string|array The selected value or values (the key of the choices array)
040 *
041 * @throws \InvalidArgumentException
042 */
043 public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
044 {
045 $width = max(array_map('strlen', array_keys($choices)));
046
047 $messages = (array) $question;
048 foreach ($choices as $key => $value) {
049 $messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
050 }
051
052 $output->writeln($messages);
053
054 $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
055 // Collapse all spaces.
056 $selectedChoices = str_replace(" ", "", $picked);
057
058 if ($multiselect) {
059 // Check for a separated comma values
060 if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
061 throw new \InvalidArgumentException(sprintf($errorMessage, $picked));
062 }
063 $selectedChoices = explode(",", $selectedChoices);
064 } else {
065 $selectedChoices = array($picked);
066 }
067
068 $multiselectChoices = array();
069
070 foreach ($selectedChoices as $value) {
071 if (empty($choices[$value])) {
072 throw new \InvalidArgumentException(sprintf($errorMessage, $value));
073 }
074 array_push($multiselectChoices, $value);
075 }
076
077 if ($multiselect) {
078 return $multiselectChoices;
079 }
080
081 return $picked;
082 }, $attempts, $default);
083
084 return $result;
085 }
086
087 /**
088 * Asks a question to the user.
089 *
090 * @param OutputInterface $output An Output instance
091 * @param string|array $question The question to ask
092 * @param string $default The default answer if none is given by the user
093 * @param array $autocomplete List of values to autocomplete
094 *
095 * @return string The user answer
096 *
097 * @throws \RuntimeException If there is no data to read in the input stream
098 */
099 public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null)
100 {
101 $output->write($question);
102
103 $inputStream = $this->inputStream ?: STDIN;
104
105 if (null === $autocomplete || !$this->hasSttyAvailable()) {
106 $ret = fgets($inputStream, 4096);
107 if (false === $ret) {
108 throw new \RuntimeException('Aborted');
109 }
110 $ret = trim($ret);
111 } else {
112 $ret = '';
113
114 $i = 0;
115 $ofs = -1;
116 $matches = $autocomplete;
117 $numMatches = count($matches);
118
119 $sttyMode = shell_exec('stty -g');
120
121 // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
122 shell_exec('stty -icanon -echo');
123
124 // Add highlighted text style
125 $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
126
127 // Read a keypress
128 while (!feof($inputStream)) {
129 $c = fread($inputStream, 1);
130
131 // Backspace Character
132 if ("\177" === $c) {
133 if (0 === $numMatches && 0 !== $i) {
134 $i--;
135 // Move cursor backwards
136 $output->write("\033[1D");
137 }
138
139 if ($i === 0) {
140 $ofs = -1;
141 $matches = $autocomplete;
142 $numMatches = count($matches);
143 } else {
144 $numMatches = 0;
145 }
146
147 // Pop the last character off the end of our string
148 $ret = substr($ret, 0, $i);
149 } elseif ("\033" === $c) {
150 // Did we read an escape sequence?
151 $c .= fread($inputStream, 2);
152
153 // A = Up Arrow. B = Down Arrow
154 if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
155 if ('A' === $c[2] && -1 === $ofs) {
156 $ofs = 0;
157 }
158
159 if (0 === $numMatches) {
160 continue;
161 }
162
163 $ofs += ('A' === $c[2]) ? -1 : 1;
164 $ofs = ($numMatches + $ofs) % $numMatches;
165 }
166 } elseif (ord($c) < 32) {
167 if ("\t" === $c || "\n" === $c) {
168 if ($numMatches > 0 && -1 !== $ofs) {
169 $ret = $matches[$ofs];
170 // Echo out remaining chars for current match
171 $output->write(substr($ret, $i));
172 $i = strlen($ret);
173 }
174
175 if ("\n" === $c) {
176 $output->write($c);
177 break;
178 }
179
180 $numMatches = 0;
181 }
182
183 continue;
184 } else {
185 $output->write($c);
186 $ret .= $c;
187 $i++;
188
189 $numMatches = 0;
190 $ofs = 0;
191
192 foreach ($autocomplete as $value) {
193 // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
194 if (0 === strpos($value, $ret) && $i !== strlen($value)) {
195 $matches[$numMatches++] = $value;
196 }
197 }
198 }
199
200 // Erase characters from cursor to end of line
201 $output->write("\033[K");
202
203 if ($numMatches > 0 && -1 !== $ofs) {
204 // Save cursor position
205 $output->write("\0337");
206 // Write highlighted text
207 $output->write('<hl>'.substr($matches[$ofs], $i).'</hl>');
208 // Restore cursor position
209 $output->write("\0338");
210 }
211 }
212
213 // Reset stty so it behaves normally again
214 shell_exec(sprintf('stty %s', $sttyMode));
215 }
216
217 return strlen($ret) > 0 ? $ret : $default;
218 }
219
220 /**
221 * Asks a confirmation to the user.
222 *
223 * The question will be asked until the user answers by nothing, yes, or no.
224 *
225 * @param OutputInterface $output An Output instance
226 * @param string|array $question The question to ask
227 * @param bool $default The default answer if the user enters nothing
228 *
229 * @return bool true if the user has confirmed, false otherwise
230 */
231 public function askConfirmation(OutputInterface $output, $question, $default = true)
232 {
233 $answer = 'z';
234 while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) {
235 $answer = $this->ask($output, $question);
236 }
237
238 if (false === $default) {
239 return $answer && 'y' == strtolower($answer[0]);
240 }
241
242 return !$answer || 'y' == strtolower($answer[0]);
243 }
244
245 /**
246 * Asks a question to the user, the response is hidden
247 *
248 * @param OutputInterface $output An Output instance
249 * @param string|array $question The question
250 * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
251 *
252 * @return string The answer
253 *
254 * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
255 */
256 public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
257 {
258 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
259 $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
260
261 // handle code running from a phar
262 if ('phar:' === substr(__FILE__, 0, 5)) {
263 $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
264 copy($exe, $tmpExe);
265 $exe = $tmpExe;
266 }
267
268 $output->write($question);
269 $value = rtrim(shell_exec($exe));
270 $output->writeln('');
271
272 if (isset($tmpExe)) {
273 unlink($tmpExe);
274 }
275
276 return $value;
277 }
278
279 if ($this->hasSttyAvailable()) {
280 $output->write($question);
281
282 $sttyMode = shell_exec('stty -g');
283
284 shell_exec('stty -echo');
285 $value = fgets($this->inputStream ?: STDIN, 4096);
286 shell_exec(sprintf('stty %s', $sttyMode));
287
288 if (false === $value) {
289 throw new \RuntimeException('Aborted');
290 }
291
292 $value = trim($value);
293 $output->writeln('');
294
295 return $value;
296 }
297
298 if (false !== $shell = $this->getShell()) {
299 $output->write($question);
300 $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
301 $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
302 $value = rtrim(shell_exec($command));
303 $output->writeln('');
304
305 return $value;
306 }
307
308 if ($fallback) {
309 return $this->ask($output, $question);
310 }
311
312 throw new \RuntimeException('Unable to hide the response');
313 }
314
315 /**
316 * Asks for a value and validates the response.
317 *
318 * The validator receives the data to validate. It must return the
319 * validated data when the data is valid and throw an exception
320 * otherwise.
321 *
322 * @param OutputInterface $output An Output instance
323 * @param string|array $question The question to ask
324 * @param callable $validator A PHP callback
325 * @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite)
326 * @param string $default The default answer if none is given by the user
327 * @param array $autocomplete List of values to autocomplete
328 *
329 * @return mixed
330 *
331 * @throws \Exception When any of the validators return an error
332 */
333 public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null)
334 {
335 $that = $this;
336
337 $interviewer = function () use ($output, $question, $default, $autocomplete, $that) {
338 return $that->ask($output, $question, $default, $autocomplete);
339 };
340
341 return $this->validateAttempts($interviewer, $output, $validator, $attempts);
342 }
343
344 /**
345 * Asks for a value, hide and validates the response.
346 *
347 * The validator receives the data to validate. It must return the
348 * validated data when the data is valid and throw an exception
349 * otherwise.
350 *
351 * @param OutputInterface $output An Output instance
352 * @param string|array $question The question to ask
353 * @param callable $validator A PHP callback
354 * @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite)
355 * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
356 *
357 * @return string The response
358 *
359 * @throws \Exception When any of the validators return an error
360 * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
361 *
362 */
363 public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
364 {
365 $that = $this;
366
367 $interviewer = function () use ($output, $question, $fallback, $that) {
368 return $that->askHiddenResponse($output, $question, $fallback);
369 };
370
371 return $this->validateAttempts($interviewer, $output, $validator, $attempts);
372 }
373
374 /**
375 * Sets the input stream to read from when interacting with the user.
376 *
377 * This is mainly useful for testing purpose.
378 *
379 * @param resource $stream The input stream
380 */
381 public function setInputStream($stream)
382 {
383 $this->inputStream = $stream;
384 }
385
386 /**
387 * Returns the helper's input stream
388 *
389 * @return string
390 */
391 public function getInputStream()
392 {
393 return $this->inputStream;
394 }
395
396 /**
397 * {@inheritdoc}
398 */
399 public function getName()
400 {
401 return 'dialog';
402 }
403
404 /**
405 * Return a valid Unix shell
406 *
407 * @return string|bool The valid shell name, false in case no valid shell is found
408 */
409 private function getShell()
410 {
411 if (null !== self::$shell) {
412 return self::$shell;
413 }
414
415 self::$shell = false;
416
417 if (file_exists('/usr/bin/env')) {
418 // handle other OSs with bash/zsh/ksh/csh if available to hide the answer
419 $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
420 foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
421 if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
422 self::$shell = $sh;
423 break;
424 }
425 }
426 }
427
428 return self::$shell;
429 }
430
431 private function hasSttyAvailable()
432 {
433 if (null !== self::$stty) {
434 return self::$stty;
435 }
436
437 exec('stty 2>&1', $output, $exitcode);
438
439 return self::$stty = $exitcode === 0;
440 }
441
442 /**
443 * Validate an attempt
444 *
445 * @param callable $interviewer A callable that will ask for a question and return the result
446 * @param OutputInterface $output An Output instance
447 * @param callable $validator A PHP callback
448 * @param int|false $attempts Max number of times to ask before giving up ; false will ask infinitely
449 *
450 * @return string The validated response
451 *
452 * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
453 */
454 private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts)
455 {
456 $error = null;
457 while (false === $attempts || $attempts--) {
458 if (null !== $error) {
459 $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
460 }
461
462 try {
463 return call_user_func($validator, $interviewer());
464 } catch (\Exception $error) {
465 }
466 }
467
468 throw $error;
469 }
470 }
471