Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

QuestionHelper.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 16.50 KiB


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\Exception\InvalidArgumentException;
015  use Symfony\Component\Console\Exception\RuntimeException;
016  use Symfony\Component\Console\Formatter\OutputFormatter;
017  use Symfony\Component\Console\Formatter\OutputFormatterStyle;
018  use Symfony\Component\Console\Input\InputInterface;
019  use Symfony\Component\Console\Input\StreamableInputInterface;
020  use Symfony\Component\Console\Output\ConsoleOutputInterface;
021  use Symfony\Component\Console\Output\OutputInterface;
022  use Symfony\Component\Console\Question\ChoiceQuestion;
023  use Symfony\Component\Console\Question\Question;
024  use Symfony\Component\Console\Terminal;
025   
026  /**
027   * The QuestionHelper class provides helpers to interact with the user.
028   *
029   * @author Fabien Potencier <fabien@symfony.com>
030   */
031  class QuestionHelper extends Helper
032  {
033      private $inputStream;
034      private static $shell;
035      private static $stty = true;
036   
037      /**
038       * Asks a question to the user.
039       *
040       * @return mixed The user answer
041       *
042       * @throws RuntimeException If there is no data to read in the input stream
043       */
044      public function ask(InputInterface $input, OutputInterface $output, Question $question)
045      {
046          if ($output instanceof ConsoleOutputInterface) {
047              $output = $output->getErrorOutput();
048          }
049   
050          if (!$input->isInteractive()) {
051              $default = $question->getDefault();
052   
053              if (null === $default) {
054                  return $default;
055              }
056   
057              if ($validator = $question->getValidator()) {
058                  return \call_user_func($question->getValidator(), $default);
059              } elseif ($question instanceof ChoiceQuestion) {
060                  $choices = $question->getChoices();
061   
062                  if (!$question->isMultiselect()) {
063                      return isset($choices[$default]) ? $choices[$default] : $default;
064                  }
065   
066                  $default = explode(',', $default);
067                  foreach ($default as $k => $v) {
068                      $v = trim($v);
069                      $default[$k] = isset($choices[$v]) ? $choices[$v] : $v;
070                  }
071              }
072   
073              return $default;
074          }
075   
076          if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
077              $this->inputStream = $stream;
078          }
079   
080          if (!$question->getValidator()) {
081              return $this->doAsk($output, $question);
082          }
083   
084          $interviewer = function () use ($output, $question) {
085              return $this->doAsk($output, $question);
086          };
087   
088          return $this->validateAttempts($interviewer, $output, $question);
089      }
090   
091      /**
092       * Sets the input stream to read from when interacting with the user.
093       *
094       * This is mainly useful for testing purpose.
095       *
096       * @deprecated since version 3.2, to be removed in 4.0. Use
097       *             StreamableInputInterface::setStream() instead.
098       *
099       * @param resource $stream The input stream
100       *
101       * @throws InvalidArgumentException In case the stream is not a resource
102       */
103      public function setInputStream($stream)
104      {
105          @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::setStream() instead.', __METHOD__, StreamableInputInterface::class), \E_USER_DEPRECATED);
106   
107          if (!\is_resource($stream)) {
108              throw new InvalidArgumentException('Input stream must be a valid resource.');
109          }
110   
111          $this->inputStream = $stream;
112      }
113   
114      /**
115       * Returns the helper's input stream.
116       *
117       * @deprecated since version 3.2, to be removed in 4.0. Use
118       *             StreamableInputInterface::getStream() instead.
119       *
120       * @return resource
121       */
122      public function getInputStream()
123      {
124          if (0 === \func_num_args() || func_get_arg(0)) {
125              @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::getStream() instead.', __METHOD__, StreamableInputInterface::class), \E_USER_DEPRECATED);
126          }
127   
128          return $this->inputStream;
129      }
130   
131      /**
132       * {@inheritdoc}
133       */
134      public function getName()
135      {
136          return 'question';
137      }
138   
139      /**
140       * Prevents usage of stty.
141       */
142      public static function disableStty()
143      {
144          self::$stty = false;
145      }
146   
147      /**
148       * Asks the question to the user.
149       *
150       * @return bool|mixed|string|null
151       *
152       * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
153       */
154      private function doAsk(OutputInterface $output, Question $question)
155      {
156          $this->writePrompt($output, $question);
157   
158          $inputStream = $this->inputStream ?: \STDIN;
159          $autocomplete = $question->getAutocompleterValues();
160   
161          if (\function_exists('sapi_windows_cp_set')) {
162              // Codepage used by cmd.exe on Windows to allow special characters (éàüñ).
163              @sapi_windows_cp_set(1252);
164          }
165   
166          if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
167              $ret = false;
168              if ($question->isHidden()) {
169                  try {
170                      $ret = trim($this->getHiddenResponse($output, $inputStream));
171                  } catch (RuntimeException $e) {
172                      if (!$question->isHiddenFallback()) {
173                          throw $e;
174                      }
175                  }
176              }
177   
178              if (false === $ret) {
179                  $ret = fgets($inputStream, 4096);
180                  if (false === $ret) {
181                      throw new RuntimeException('Aborted.');
182                  }
183                  $ret = trim($ret);
184              }
185          } else {
186              $ret = trim($this->autocomplete($output, $question, $inputStream, \is_array($autocomplete) ? $autocomplete : iterator_to_array($autocomplete, false)));
187          }
188   
189          $ret = \strlen($ret) > 0 ? $ret : $question->getDefault();
190   
191          if ($normalizer = $question->getNormalizer()) {
192              return $normalizer($ret);
193          }
194   
195          return $ret;
196      }
197   
198      /**
199       * Outputs the question prompt.
200       */
201      protected function writePrompt(OutputInterface $output, Question $question)
202      {
203          $message = $question->getQuestion();
204   
205          if ($question instanceof ChoiceQuestion) {
206              $output->writeln(array_merge([
207                  $question->getQuestion(),
208              ], $this->formatChoiceQuestionChoices($question, 'info')));
209   
210              $message = $question->getPrompt();
211          }
212   
213          $output->write($message);
214      }
215   
216      /**
217       * @param string $tag
218       *
219       * @return string[]
220       */
221      protected function formatChoiceQuestionChoices(ChoiceQuestion $question, $tag)
222      {
223          $messages = [];
224   
225          $maxWidth = max(array_map('self::strlen', array_keys($choices = $question->getChoices())));
226   
227          foreach ($choices as $key => $value) {
228              $padding = str_repeat(' ', $maxWidth - self::strlen($key));
229   
230              $messages[] = sprintf("  [<$tag>%s$padding</$tag>] %s", $key, $value);
231          }
232   
233          return $messages;
234      }
235   
236      /**
237       * Outputs an error message.
238       */
239      protected function writeError(OutputInterface $output, \Exception $error)
240      {
241          if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
242              $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
243          } else {
244              $message = '<error>'.$error->getMessage().'</error>';
245          }
246   
247          $output->writeln($message);
248      }
249   
250      /**
251       * Autocompletes a question.
252       *
253       * @param resource $inputStream
254       *
255       * @return string
256       */
257      private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete)
258      {
259          $fullChoice = '';
260          $ret = '';
261   
262          $i = 0;
263          $ofs = -1;
264          $matches = $autocomplete;
265          $numMatches = \count($matches);
266   
267          $sttyMode = shell_exec('stty -g');
268   
269          // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
270          shell_exec('stty -icanon -echo');
271   
272          // Add highlighted text style
273          $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
274   
275          // Read a keypress
276          while (!feof($inputStream)) {
277              $c = fread($inputStream, 1);
278   
279              // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
280              if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
281                  shell_exec(sprintf('stty %s', $sttyMode));
282                  throw new RuntimeException('Aborted.');
283              } elseif ("\177" === $c) { // Backspace Character
284                  if (0 === $numMatches && 0 !== $i) {
285                      --$i;
286                      $fullChoice = self::substr($fullChoice, 0, $i);
287                      // Move cursor backwards
288                      $output->write("\033[1D");
289                  }
290   
291                  if (0 === $i) {
292                      $ofs = -1;
293                      $matches = $autocomplete;
294                      $numMatches = \count($matches);
295                  } else {
296                      $numMatches = 0;
297                  }
298   
299                  // Pop the last character off the end of our string
300                  $ret = self::substr($ret, 0, $i);
301              } elseif ("\033" === $c) {
302                  // Did we read an escape sequence?
303                  $c .= fread($inputStream, 2);
304   
305                  // A = Up Arrow. B = Down Arrow
306                  if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
307                      if ('A' === $c[2] && -1 === $ofs) {
308                          $ofs = 0;
309                      }
310   
311                      if (0 === $numMatches) {
312                          continue;
313                      }
314   
315                      $ofs += ('A' === $c[2]) ? -1 : 1;
316                      $ofs = ($numMatches + $ofs) % $numMatches;
317                  }
318              } elseif (\ord($c) < 32) {
319                  if ("\t" === $c || "\n" === $c) {
320                      if ($numMatches > 0 && -1 !== $ofs) {
321                          $ret = $matches[$ofs];
322                          // Echo out remaining chars for current match
323                          $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
324                          $output->write($remainingCharacters);
325                          $fullChoice .= $remainingCharacters;
326                          $i = self::strlen($fullChoice);
327                      }
328   
329                      if ("\n" === $c) {
330                          $output->write($c);
331                          break;
332                      }
333   
334                      $numMatches = 0;
335                  }
336   
337                  continue;
338              } else {
339                  if ("\x80" <= $c) {
340                      $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
341                  }
342   
343                  $output->write($c);
344                  $ret .= $c;
345                  $fullChoice .= $c;
346                  ++$i;
347   
348                  $tempRet = $ret;
349   
350                  if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
351                      $tempRet = $this->mostRecentlyEnteredValue($fullChoice);
352                  }
353   
354                  $numMatches = 0;
355                  $ofs = 0;
356   
357                  foreach ($autocomplete as $value) {
358                      // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
359                      if (0 === strpos($value, $tempRet)) {
360                          $matches[$numMatches++] = $value;
361                      }
362                  }
363              }
364   
365              // Erase characters from cursor to end of line
366              $output->write("\033[K");
367   
368              if ($numMatches > 0 && -1 !== $ofs) {
369                  // Save cursor position
370                  $output->write("\0337");
371                  // Write highlighted text, complete the partially entered response
372                  $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
373                  $output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
374                  // Restore cursor position
375                  $output->write("\0338");
376              }
377          }
378   
379          // Reset stty so it behaves normally again
380          shell_exec(sprintf('stty %s', $sttyMode));
381   
382          return $fullChoice;
383      }
384   
385      private function mostRecentlyEnteredValue($entered)
386      {
387          // Determine the most recent value that the user entered
388          if (false === strpos($entered, ',')) {
389              return $entered;
390          }
391   
392          $choices = explode(',', $entered);
393          if (\strlen($lastChoice = trim($choices[\count($choices) - 1])) > 0) {
394              return $lastChoice;
395          }
396   
397          return $entered;
398      }
399   
400      /**
401       * Gets a hidden response from user.
402       *
403       * @param OutputInterface $output      An Output instance
404       * @param resource        $inputStream The handler resource
405       *
406       * @return string The answer
407       *
408       * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
409       */
410      private function getHiddenResponse(OutputInterface $output, $inputStream)
411      {
412          if ('\\' === \DIRECTORY_SEPARATOR) {
413              $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
414   
415              // handle code running from a phar
416              if ('phar:' === substr(__FILE__, 0, 5)) {
417                  $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
418                  copy($exe, $tmpExe);
419                  $exe = $tmpExe;
420              }
421   
422              $value = rtrim(shell_exec($exe));
423              $output->writeln('');
424   
425              if (isset($tmpExe)) {
426                  unlink($tmpExe);
427              }
428   
429              return $value;
430          }
431   
432          if (self::$stty && Terminal::hasSttyAvailable()) {
433              $sttyMode = shell_exec('stty -g');
434   
435              shell_exec('stty -echo');
436              $value = fgets($inputStream, 4096);
437              shell_exec(sprintf('stty %s', $sttyMode));
438   
439              if (false === $value) {
440                  throw new RuntimeException('Aborted.');
441              }
442   
443              $value = trim($value);
444              $output->writeln('');
445   
446              return $value;
447          }
448   
449          if (false !== $shell = $this->getShell()) {
450              $readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
451              $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
452              $value = rtrim(shell_exec($command));
453              $output->writeln('');
454   
455              return $value;
456          }
457   
458          throw new RuntimeException('Unable to hide the response.');
459      }
460   
461      /**
462       * Validates an attempt.
463       *
464       * @param callable        $interviewer A callable that will ask for a question and return the result
465       * @param OutputInterface $output      An Output instance
466       * @param Question        $question    A Question instance
467       *
468       * @return mixed The validated response
469       *
470       * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
471       */
472      private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
473      {
474          $error = null;
475          $attempts = $question->getMaxAttempts();
476          while (null === $attempts || $attempts--) {
477              if (null !== $error) {
478                  $this->writeError($output, $error);
479              }
480   
481              try {
482                  return \call_user_func($question->getValidator(), $interviewer());
483              } catch (RuntimeException $e) {
484                  throw $e;
485              } catch (\Exception $error) {
486              }
487          }
488   
489          throw $error;
490      }
491   
492      /**
493       * Returns a valid unix shell.
494       *
495       * @return string|bool The valid shell name, false in case no valid shell is found
496       */
497      private function getShell()
498      {
499          if (null !== self::$shell) {
500              return self::$shell;
501          }
502   
503          self::$shell = false;
504   
505          if (file_exists('/usr/bin/env')) {
506              // handle other OSs with bash/zsh/ksh/csh if available to hide the answer
507              $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
508              foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) {
509                  if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
510                      self::$shell = $sh;
511                      break;
512                  }
513              }
514          }
515   
516          return self::$shell;
517      }
518  }
519