[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

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


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1