[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

/vendor/symfony/console/Helper/ -> DialogHelper.php (source)

   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\Helper;
  13  
  14  use Symfony\Component\Console\Exception\InvalidArgumentException;
  15  use Symfony\Component\Console\Exception\RuntimeException;
  16  use Symfony\Component\Console\Formatter\OutputFormatterStyle;
  17  use Symfony\Component\Console\Output\ConsoleOutputInterface;
  18  use Symfony\Component\Console\Output\OutputInterface;
  19  
  20  /**
  21   * The Dialog class provides helpers to interact with the user.
  22   *
  23   * @author Fabien Potencier <fabien@symfony.com>
  24   *
  25   * @deprecated since version 2.5, to be removed in 3.0.
  26   *             Use {@link \Symfony\Component\Console\Helper\QuestionHelper} instead.
  27   */
  28  class DialogHelper extends InputAwareHelper
  29  {
  30      private $inputStream;
  31      private static $shell;
  32      private static $stty;
  33  
  34      public function __construct($triggerDeprecationError = true)
  35      {
  36          if ($triggerDeprecationError) {
  37              @trigger_error('"Symfony\Component\Console\Helper\DialogHelper" is deprecated since Symfony 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\QuestionHelper" instead.', E_USER_DEPRECATED);
  38          }
  39      }
  40  
  41      /**
  42       * Asks the user to select a value.
  43       *
  44       * @param OutputInterface $output       An Output instance
  45       * @param string|array    $question     The question to ask
  46       * @param array           $choices      List of choices to pick from
  47       * @param bool|string     $default      The default answer if the user enters nothing
  48       * @param bool|int        $attempts     Max number of times to ask before giving up (false by default, which means infinite)
  49       * @param string          $errorMessage Message which will be shown if invalid value from choice list would be picked
  50       * @param bool            $multiselect  Select more than one value separated by comma
  51       *
  52       * @return int|string|array The selected value or values (the key of the choices array)
  53       *
  54       * @throws InvalidArgumentException
  55       */
  56      public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
  57      {
  58          if ($output instanceof ConsoleOutputInterface) {
  59              $output = $output->getErrorOutput();
  60          }
  61  
  62          $width = max(array_map('strlen', array_keys($choices)));
  63  
  64          $messages = (array) $question;
  65          foreach ($choices as $key => $value) {
  66              $messages[] = sprintf("  [<info>%-{$width}s</info>] %s", $key, $value);
  67          }
  68  
  69          $output->writeln($messages);
  70  
  71          $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
  72              // Collapse all spaces.
  73              $selectedChoices = str_replace(' ', '', $picked);
  74  
  75              if ($multiselect) {
  76                  // Check for a separated comma values
  77                  if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
  78                      throw new InvalidArgumentException(sprintf($errorMessage, $picked));
  79                  }
  80                  $selectedChoices = explode(',', $selectedChoices);
  81              } else {
  82                  $selectedChoices = array($picked);
  83              }
  84  
  85              $multiselectChoices = array();
  86  
  87              foreach ($selectedChoices as $value) {
  88                  if (empty($choices[$value])) {
  89                      throw new InvalidArgumentException(sprintf($errorMessage, $value));
  90                  }
  91                  $multiselectChoices[] = $value;
  92              }
  93  
  94              if ($multiselect) {
  95                  return $multiselectChoices;
  96              }
  97  
  98              return $picked;
  99          }, $attempts, $default);
 100  
 101          return $result;
 102      }
 103  
 104      /**
 105       * Asks a question to the user.
 106       *
 107       * @param OutputInterface $output       An Output instance
 108       * @param string|array    $question     The question to ask
 109       * @param string          $default      The default answer if none is given by the user
 110       * @param array           $autocomplete List of values to autocomplete
 111       *
 112       * @return string The user answer
 113       *
 114       * @throws RuntimeException If there is no data to read in the input stream
 115       */
 116      public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null)
 117      {
 118          if ($this->input && !$this->input->isInteractive()) {
 119              return $default;
 120          }
 121  
 122          if ($output instanceof ConsoleOutputInterface) {
 123              $output = $output->getErrorOutput();
 124          }
 125  
 126          $output->write($question);
 127  
 128          $inputStream = $this->inputStream ?: STDIN;
 129  
 130          if (null === $autocomplete || !$this->hasSttyAvailable()) {
 131              $ret = fgets($inputStream, 4096);
 132              if (false === $ret) {
 133                  throw new RuntimeException('Aborted');
 134              }
 135              $ret = trim($ret);
 136          } else {
 137              $ret = '';
 138  
 139              $i = 0;
 140              $ofs = -1;
 141              $matches = $autocomplete;
 142              $numMatches = \count($matches);
 143  
 144              $sttyMode = shell_exec('stty -g');
 145  
 146              // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
 147              shell_exec('stty -icanon -echo');
 148  
 149              // Add highlighted text style
 150              $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
 151  
 152              // Read a keypress
 153              while (!feof($inputStream)) {
 154                  $c = fread($inputStream, 1);
 155  
 156                  // Backspace Character
 157                  if ("\177" === $c) {
 158                      if (0 === $numMatches && 0 !== $i) {
 159                          --$i;
 160                          // Move cursor backwards
 161                          $output->write("\033[1D");
 162                      }
 163  
 164                      if (0 === $i) {
 165                          $ofs = -1;
 166                          $matches = $autocomplete;
 167                          $numMatches = \count($matches);
 168                      } else {
 169                          $numMatches = 0;
 170                      }
 171  
 172                      // Pop the last character off the end of our string
 173                      $ret = substr($ret, 0, $i);
 174                  } elseif ("\033" === $c) {
 175                      // Did we read an escape sequence?
 176                      $c .= fread($inputStream, 2);
 177  
 178                      // A = Up Arrow. B = Down Arrow
 179                      if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
 180                          if ('A' === $c[2] && -1 === $ofs) {
 181                              $ofs = 0;
 182                          }
 183  
 184                          if (0 === $numMatches) {
 185                              continue;
 186                          }
 187  
 188                          $ofs += ('A' === $c[2]) ? -1 : 1;
 189                          $ofs = ($numMatches + $ofs) % $numMatches;
 190                      }
 191                  } elseif (\ord($c) < 32) {
 192                      if ("\t" === $c || "\n" === $c) {
 193                          if ($numMatches > 0 && -1 !== $ofs) {
 194                              $ret = $matches[$ofs];
 195                              // Echo out remaining chars for current match
 196                              $output->write(substr($ret, $i));
 197                              $i = \strlen($ret);
 198                          }
 199  
 200                          if ("\n" === $c) {
 201                              $output->write($c);
 202                              break;
 203                          }
 204  
 205                          $numMatches = 0;
 206                      }
 207  
 208                      continue;
 209                  } else {
 210                      $output->write($c);
 211                      $ret .= $c;
 212                      ++$i;
 213  
 214                      $numMatches = 0;
 215                      $ofs = 0;
 216  
 217                      foreach ($autocomplete as $value) {
 218                          // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
 219                          if (0 === strpos($value, $ret) && $i !== \strlen($value)) {
 220                              $matches[$numMatches++] = $value;
 221                          }
 222                      }
 223                  }
 224  
 225                  // Erase characters from cursor to end of line
 226                  $output->write("\033[K");
 227  
 228                  if ($numMatches > 0 && -1 !== $ofs) {
 229                      // Save cursor position
 230                      $output->write("\0337");
 231                      // Write highlighted text
 232                      $output->write('<hl>'.substr($matches[$ofs], $i).'</hl>');
 233                      // Restore cursor position
 234                      $output->write("\0338");
 235                  }
 236              }
 237  
 238              // Reset stty so it behaves normally again
 239              shell_exec(sprintf('stty %s', $sttyMode));
 240          }
 241  
 242          return \strlen($ret) > 0 ? $ret : $default;
 243      }
 244  
 245      /**
 246       * Asks a confirmation to the user.
 247       *
 248       * The question will be asked until the user answers by nothing, yes, or no.
 249       *
 250       * @param OutputInterface $output   An Output instance
 251       * @param string|array    $question The question to ask
 252       * @param bool            $default  The default answer if the user enters nothing
 253       *
 254       * @return bool true if the user has confirmed, false otherwise
 255       */
 256      public function askConfirmation(OutputInterface $output, $question, $default = true)
 257      {
 258          $answer = 'z';
 259          while ($answer && !\in_array(strtolower($answer[0]), array('y', 'n'))) {
 260              $answer = $this->ask($output, $question);
 261          }
 262  
 263          if (false === $default) {
 264              return $answer && 'y' == strtolower($answer[0]);
 265          }
 266  
 267          return !$answer || 'y' == strtolower($answer[0]);
 268      }
 269  
 270      /**
 271       * Asks a question to the user, the response is hidden.
 272       *
 273       * @param OutputInterface $output   An Output instance
 274       * @param string|array    $question The question
 275       * @param bool            $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
 276       *
 277       * @return string The answer
 278       *
 279       * @throws RuntimeException In case the fallback is deactivated and the response can not be hidden
 280       */
 281      public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
 282      {
 283          if ($output instanceof ConsoleOutputInterface) {
 284              $output = $output->getErrorOutput();
 285          }
 286  
 287          if ('\\' === \DIRECTORY_SEPARATOR) {
 288              $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
 289  
 290              // handle code running from a phar
 291              if ('phar:' === substr(__FILE__, 0, 5)) {
 292                  $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
 293                  copy($exe, $tmpExe);
 294                  $exe = $tmpExe;
 295              }
 296  
 297              $output->write($question);
 298              $value = rtrim(shell_exec($exe));
 299              $output->writeln('');
 300  
 301              if (isset($tmpExe)) {
 302                  unlink($tmpExe);
 303              }
 304  
 305              return $value;
 306          }
 307  
 308          if ($this->hasSttyAvailable()) {
 309              $output->write($question);
 310  
 311              $sttyMode = shell_exec('stty -g');
 312  
 313              shell_exec('stty -echo');
 314              $value = fgets($this->inputStream ?: STDIN, 4096);
 315              shell_exec(sprintf('stty %s', $sttyMode));
 316  
 317              if (false === $value) {
 318                  throw new RuntimeException('Aborted');
 319              }
 320  
 321              $value = trim($value);
 322              $output->writeln('');
 323  
 324              return $value;
 325          }
 326  
 327          if (false !== $shell = $this->getShell()) {
 328              $output->write($question);
 329              $readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
 330              $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
 331              $value = rtrim(shell_exec($command));
 332              $output->writeln('');
 333  
 334              return $value;
 335          }
 336  
 337          if ($fallback) {
 338              return $this->ask($output, $question);
 339          }
 340  
 341          throw new RuntimeException('Unable to hide the response');
 342      }
 343  
 344      /**
 345       * Asks for a value 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 string          $default      The default answer if none is given by the user
 356       * @param array           $autocomplete List of values to autocomplete
 357       *
 358       * @return mixed
 359       *
 360       * @throws \Exception When any of the validators return an error
 361       */
 362      public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null)
 363      {
 364          $that = $this;
 365  
 366          $interviewer = function () use ($output, $question, $default, $autocomplete, $that) {
 367              return $that->ask($output, $question, $default, $autocomplete);
 368          };
 369  
 370          return $this->validateAttempts($interviewer, $output, $validator, $attempts);
 371      }
 372  
 373      /**
 374       * Asks for a value, hide and validates the response.
 375       *
 376       * The validator receives the data to validate. It must return the
 377       * validated data when the data is valid and throw an exception
 378       * otherwise.
 379       *
 380       * @param OutputInterface $output    An Output instance
 381       * @param string|array    $question  The question to ask
 382       * @param callable        $validator A PHP callback
 383       * @param int|false       $attempts  Max number of times to ask before giving up (false by default, which means infinite)
 384       * @param bool            $fallback  In case the response can not be hidden, whether to fallback on non-hidden question or not
 385       *
 386       * @return string The response
 387       *
 388       * @throws \Exception       When any of the validators return an error
 389       * @throws RuntimeException In case the fallback is deactivated and the response can not be hidden
 390       */
 391      public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
 392      {
 393          $that = $this;
 394  
 395          $interviewer = function () use ($output, $question, $fallback, $that) {
 396              return $that->askHiddenResponse($output, $question, $fallback);
 397          };
 398  
 399          return $this->validateAttempts($interviewer, $output, $validator, $attempts);
 400      }
 401  
 402      /**
 403       * Sets the input stream to read from when interacting with the user.
 404       *
 405       * This is mainly useful for testing purpose.
 406       *
 407       * @param resource $stream The input stream
 408       */
 409      public function setInputStream($stream)
 410      {
 411          $this->inputStream = $stream;
 412      }
 413  
 414      /**
 415       * Returns the helper's input stream.
 416       *
 417       * @return resource|null The input stream or null if the default STDIN is used
 418       */
 419      public function getInputStream()
 420      {
 421          return $this->inputStream;
 422      }
 423  
 424      /**
 425       * {@inheritdoc}
 426       */
 427      public function getName()
 428      {
 429          return 'dialog';
 430      }
 431  
 432      /**
 433       * Return a valid Unix shell.
 434       *
 435       * @return string|bool The valid shell name, false in case no valid shell is found
 436       */
 437      private function getShell()
 438      {
 439          if (null !== self::$shell) {
 440              return self::$shell;
 441          }
 442  
 443          self::$shell = false;
 444  
 445          if (file_exists('/usr/bin/env')) {
 446              // handle other OSs with bash/zsh/ksh/csh if available to hide the answer
 447              $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
 448              foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
 449                  if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
 450                      self::$shell = $sh;
 451                      break;
 452                  }
 453              }
 454          }
 455  
 456          return self::$shell;
 457      }
 458  
 459      private function hasSttyAvailable()
 460      {
 461          if (null !== self::$stty) {
 462              return self::$stty;
 463          }
 464  
 465          exec('stty 2>&1', $output, $exitcode);
 466  
 467          return self::$stty = 0 === $exitcode;
 468      }
 469  
 470      /**
 471       * Validate an attempt.
 472       *
 473       * @param callable        $interviewer A callable that will ask for a question and return the result
 474       * @param OutputInterface $output      An Output instance
 475       * @param callable        $validator   A PHP callback
 476       * @param int|false       $attempts    Max number of times to ask before giving up; false will ask infinitely
 477       *
 478       * @return string The validated response
 479       *
 480       * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
 481       */
 482      private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts)
 483      {
 484          if ($output instanceof ConsoleOutputInterface) {
 485              $output = $output->getErrorOutput();
 486          }
 487  
 488          $e = null;
 489          while (false === $attempts || $attempts--) {
 490              if (null !== $e) {
 491                  $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($e->getMessage(), 'error'));
 492              }
 493  
 494              try {
 495                  return \call_user_func($validator, $interviewer());
 496              } catch (\Exception $e) {
 497              }
 498          }
 499  
 500          throw $e;
 501      }
 502  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1