mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-22 00:32:16 +02:00
N°8910 - Upgrade Symfony packages (#811)
This commit is contained in:
@@ -258,11 +258,7 @@ class QuestionHelper extends Helper
|
||||
$ofs = -1;
|
||||
$matches = $autocomplete($ret);
|
||||
$numMatches = \count($matches);
|
||||
|
||||
$sttyMode = shell_exec('stty -g');
|
||||
$isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null);
|
||||
$r = [$inputStream];
|
||||
$w = [];
|
||||
$inputHelper = new TerminalInputHelper($inputStream);
|
||||
|
||||
// Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
|
||||
shell_exec('stty -icanon -echo');
|
||||
@@ -272,15 +268,13 @@ class QuestionHelper extends Helper
|
||||
|
||||
// Read a keypress
|
||||
while (!feof($inputStream)) {
|
||||
while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) {
|
||||
// Give signal handlers a chance to run
|
||||
$r = [$inputStream];
|
||||
}
|
||||
$inputHelper->waitForInput();
|
||||
$c = fread($inputStream, 1);
|
||||
|
||||
// as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
|
||||
if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
|
||||
shell_exec('stty '.$sttyMode);
|
||||
// Restore the terminal so it behaves normally again
|
||||
$inputHelper->finish();
|
||||
throw new MissingInputException('Aborted.');
|
||||
} elseif ("\177" === $c) { // Backspace Character
|
||||
if (0 === $numMatches && 0 !== $i) {
|
||||
@@ -317,12 +311,12 @@ class QuestionHelper extends Helper
|
||||
$ofs += ('A' === $c[2]) ? -1 : 1;
|
||||
$ofs = ($numMatches + $ofs) % $numMatches;
|
||||
}
|
||||
} elseif (\ord($c) < 32) {
|
||||
} elseif ('' === $c || \ord($c) < 32) {
|
||||
if ("\t" === $c || "\n" === $c) {
|
||||
if ($numMatches > 0 && -1 !== $ofs) {
|
||||
$ret = (string) $matches[$ofs];
|
||||
// Echo out remaining chars for current match
|
||||
$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
|
||||
$remainingCharacters = substr($ret, \strlen($this->mostRecentlyEnteredValue($fullChoice)));
|
||||
$output->write($remainingCharacters);
|
||||
$fullChoice .= $remainingCharacters;
|
||||
$i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
|
||||
@@ -376,14 +370,14 @@ class QuestionHelper extends Helper
|
||||
if ($numMatches > 0 && -1 !== $ofs) {
|
||||
$cursor->savePosition();
|
||||
// Write highlighted text, complete the partially entered response
|
||||
$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
|
||||
$charactersEntered = \strlen($this->mostRecentlyEnteredValue($fullChoice));
|
||||
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
|
||||
$cursor->restorePosition();
|
||||
}
|
||||
}
|
||||
|
||||
// Reset stty so it behaves normally again
|
||||
shell_exec('stty '.$sttyMode);
|
||||
// Restore the terminal so it behaves normally again
|
||||
$inputHelper->finish();
|
||||
|
||||
return $fullChoice;
|
||||
}
|
||||
@@ -434,27 +428,25 @@ class QuestionHelper extends Helper
|
||||
return $value;
|
||||
}
|
||||
|
||||
$inputHelper = null;
|
||||
|
||||
if (self::$stty && Terminal::hasSttyAvailable()) {
|
||||
$sttyMode = shell_exec('stty -g');
|
||||
$inputHelper = new TerminalInputHelper($inputStream);
|
||||
shell_exec('stty -echo');
|
||||
} elseif ($this->isInteractiveInput($inputStream)) {
|
||||
throw new RuntimeException('Unable to hide the response.');
|
||||
}
|
||||
|
||||
$value = fgets($inputStream, 4096);
|
||||
$value = $this->doReadInput($inputStream, helper: $inputHelper);
|
||||
|
||||
if (4095 === \strlen($value)) {
|
||||
$errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
|
||||
$errOutput->warning('The value was possibly truncated by your shell or terminal emulator');
|
||||
}
|
||||
|
||||
if (self::$stty && Terminal::hasSttyAvailable()) {
|
||||
shell_exec('stty '.$sttyMode);
|
||||
}
|
||||
// Restore the terminal so it behaves normally again
|
||||
$inputHelper?->finish();
|
||||
|
||||
if (false === $value) {
|
||||
throw new MissingInputException('Aborted.');
|
||||
}
|
||||
if ($trimmable) {
|
||||
$value = trim($value);
|
||||
}
|
||||
@@ -514,7 +506,7 @@ class QuestionHelper extends Helper
|
||||
{
|
||||
if (!$question->isMultiline()) {
|
||||
$cp = $this->setIOCodepage();
|
||||
$ret = fgets($inputStream, 4096);
|
||||
$ret = $this->doReadInput($inputStream);
|
||||
|
||||
return $this->resetIOCodepage($cp, $ret);
|
||||
}
|
||||
@@ -524,14 +516,8 @@ class QuestionHelper extends Helper
|
||||
return false;
|
||||
}
|
||||
|
||||
$ret = '';
|
||||
$cp = $this->setIOCodepage();
|
||||
while (false !== ($char = fgetc($multiLineStreamReader))) {
|
||||
if ("\x4" === $char || \PHP_EOL === "{$ret}{$char}") {
|
||||
break;
|
||||
}
|
||||
$ret .= $char;
|
||||
}
|
||||
$ret = $this->doReadInput($multiLineStreamReader, "\x4");
|
||||
|
||||
if (stream_get_meta_data($inputStream)['seekable']) {
|
||||
fseek($inputStream, ftell($multiLineStreamReader));
|
||||
@@ -601,4 +587,35 @@ class QuestionHelper extends Helper
|
||||
|
||||
return $cloneStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $inputStream
|
||||
*/
|
||||
private function doReadInput($inputStream, ?string $exitChar = null, ?TerminalInputHelper $helper = null): string
|
||||
{
|
||||
$ret = '';
|
||||
$helper ??= new TerminalInputHelper($inputStream, false);
|
||||
|
||||
while (!feof($inputStream)) {
|
||||
$helper->waitForInput();
|
||||
$char = fread($inputStream, 1);
|
||||
|
||||
// as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
|
||||
if (false === $char || ('' === $ret && '' === $char)) {
|
||||
throw new MissingInputException('Aborted.');
|
||||
}
|
||||
|
||||
if (\PHP_EOL === "{$ret}{$char}" || $exitChar === $char) {
|
||||
break;
|
||||
}
|
||||
|
||||
$ret .= $char;
|
||||
|
||||
if (null === $exitChar && "\n" === $char) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class SymfonyQuestionHelper extends QuestionHelper
|
||||
$default = $question->getDefault();
|
||||
|
||||
if ($question->isMultiline()) {
|
||||
$text .= \sprintf(' (press %s to continue)', $this->getEofShortcut());
|
||||
$text .= \sprintf(' (press %s to continue)', $this->getEofShortcut($output));
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
@@ -98,9 +98,9 @@ class SymfonyQuestionHelper extends QuestionHelper
|
||||
parent::writeError($output, $error);
|
||||
}
|
||||
|
||||
private function getEofShortcut(): string
|
||||
private function getEofShortcut(OutputInterface $output): string
|
||||
{
|
||||
if ('Windows' === \PHP_OS_FAMILY) {
|
||||
if ('\\' === \DIRECTORY_SEPARATOR && !$output->isDecorated()) {
|
||||
return '<comment>Ctrl+Z</comment> then <comment>Enter</comment>';
|
||||
}
|
||||
|
||||
|
||||
@@ -77,10 +77,11 @@ class TableStyle
|
||||
*
|
||||
* <code>
|
||||
* ╔═══════════════╤══════════════════════════╤══════════════════╗
|
||||
* 1 ISBN 2 Title │ Author ║
|
||||
* ╠═══════════════╪══════════════════════════╪══════════════════╣
|
||||
* ║ ISBN │ Title │ Author ║
|
||||
* ╠═══════1═══════╪══════════════════════════╪══════════════════╣
|
||||
* ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
|
||||
* ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
|
||||
* ╟───────2───────┼──────────────────────────┼──────────────────╢
|
||||
* ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
|
||||
* ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
|
||||
* ╚═══════════════╧══════════════════════════╧══════════════════╝
|
||||
@@ -101,11 +102,10 @@ class TableStyle
|
||||
*
|
||||
* <code>
|
||||
* ╔═══════════════╤══════════════════════════╤══════════════════╗
|
||||
* ║ ISBN │ Title │ Author ║
|
||||
* ╠═══════1═══════╪══════════════════════════╪══════════════════╣
|
||||
* 1 ISBN 2 Title │ Author ║
|
||||
* ╠═══════════════╪══════════════════════════╪══════════════════╣
|
||||
* ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
|
||||
* ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
|
||||
* ╟───────2───────┼──────────────────────────┼──────────────────╢
|
||||
* ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
|
||||
* ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
|
||||
* ╚═══════════════╧══════════════════════════╧══════════════════╝
|
||||
|
||||
156
lib/symfony/console/Helper/TerminalInputHelper.php
Normal file
156
lib/symfony/console/Helper/TerminalInputHelper.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Console\Helper;
|
||||
|
||||
/**
|
||||
* TerminalInputHelper stops Ctrl-C and similar signals from leaving the terminal in
|
||||
* an unusable state if its settings have been modified when reading user input.
|
||||
* This can be an issue on non-Windows platforms.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* $inputHelper = new TerminalInputHelper($inputStream);
|
||||
*
|
||||
* ...change terminal settings
|
||||
*
|
||||
* // Wait for input before all input reads
|
||||
* $inputHelper->waitForInput();
|
||||
*
|
||||
* ...read input
|
||||
*
|
||||
* // Call finish to restore terminal settings and signal handlers
|
||||
* $inputHelper->finish()
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class TerminalInputHelper
|
||||
{
|
||||
/** @var resource */
|
||||
private $inputStream;
|
||||
private bool $isStdin;
|
||||
private string $initialState = '';
|
||||
private int $signalToKill = 0;
|
||||
private array $signalHandlers = [];
|
||||
private array $targetSignals = [];
|
||||
private bool $withStty;
|
||||
|
||||
/**
|
||||
* @param resource $inputStream
|
||||
*
|
||||
* @throws \RuntimeException If unable to read terminal settings
|
||||
*/
|
||||
public function __construct($inputStream, bool $withStty = true)
|
||||
{
|
||||
$this->inputStream = $inputStream;
|
||||
$this->isStdin = 'php://stdin' === stream_get_meta_data($inputStream)['uri'];
|
||||
$this->withStty = $withStty;
|
||||
|
||||
if ($withStty) {
|
||||
if (!\is_string($state = shell_exec('stty -g'))) {
|
||||
throw new \RuntimeException('Unable to read the terminal settings.');
|
||||
}
|
||||
|
||||
$this->initialState = $state;
|
||||
|
||||
$this->createSignalHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for input.
|
||||
*/
|
||||
public function waitForInput(): void
|
||||
{
|
||||
if ($this->isStdin) {
|
||||
$r = [$this->inputStream];
|
||||
$w = [];
|
||||
|
||||
// Allow signal handlers to run
|
||||
while (0 === @stream_select($r, $w, $w, 0, 100)) {
|
||||
$r = [$this->inputStream];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->withStty) {
|
||||
$this->checkForKillSignal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores terminal state and signal handlers.
|
||||
*/
|
||||
public function finish(): void
|
||||
{
|
||||
if (!$this->withStty) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safeguard in case an unhandled kill signal exists
|
||||
$this->checkForKillSignal();
|
||||
shell_exec('stty '.$this->initialState);
|
||||
$this->signalToKill = 0;
|
||||
|
||||
foreach ($this->signalHandlers as $signal => $originalHandler) {
|
||||
pcntl_signal($signal, $originalHandler);
|
||||
}
|
||||
$this->signalHandlers = [];
|
||||
$this->targetSignals = [];
|
||||
}
|
||||
|
||||
private function createSignalHandlers(): void
|
||||
{
|
||||
if (!\function_exists('pcntl_async_signals') || !\function_exists('pcntl_signal')) {
|
||||
return;
|
||||
}
|
||||
|
||||
pcntl_async_signals(true);
|
||||
$this->targetSignals = [\SIGINT, \SIGQUIT, \SIGTERM];
|
||||
|
||||
foreach ($this->targetSignals as $signal) {
|
||||
$this->signalHandlers[$signal] = pcntl_signal_get_handler($signal);
|
||||
|
||||
pcntl_signal($signal, function ($signal) {
|
||||
// Save current state, then restore to initial state
|
||||
$currentState = shell_exec('stty -g');
|
||||
shell_exec('stty '.$this->initialState);
|
||||
$originalHandler = $this->signalHandlers[$signal];
|
||||
|
||||
if (\is_callable($originalHandler)) {
|
||||
$originalHandler($signal);
|
||||
// Handler did not exit, so restore to current state
|
||||
shell_exec('stty '.$currentState);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not a callable, so SIG_DFL or SIG_IGN
|
||||
if (\SIG_DFL === $originalHandler) {
|
||||
$this->signalToKill = $signal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function checkForKillSignal(): void
|
||||
{
|
||||
if (\in_array($this->signalToKill, $this->targetSignals, true)) {
|
||||
// Try posix_kill
|
||||
if (\function_exists('posix_kill')) {
|
||||
pcntl_signal($this->signalToKill, \SIG_DFL);
|
||||
posix_kill(getmypid(), $this->signalToKill);
|
||||
}
|
||||
|
||||
// Best attempt fallback
|
||||
exit(128 + $this->signalToKill);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user