Aggiornato Composer

This commit is contained in:
Paolo A
2024-05-17 12:24:19 +00:00
parent 4ac62108b5
commit ec201d75b2
2238 changed files with 38684 additions and 59785 deletions

View File

@@ -134,7 +134,7 @@ class Application implements ResetInterface
*
* @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}.
*/
public function run(InputInterface $input = null, OutputInterface $output = null)
public function run(?InputInterface $input = null, ?OutputInterface $output = null)
{
if (\function_exists('putenv')) {
@putenv('LINES='.$this->terminal->getHeight());
@@ -276,7 +276,9 @@ class Application implements ResetInterface
$alternative = $alternatives[0];
$style = new SymfonyStyle($input, $output);
$style->block(sprintf("\nCommand \"%s\" is not defined.\n", $name), null, 'error');
$output->writeln('');
$formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
$output->writeln($formattedBlock);
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
@@ -363,9 +365,18 @@ class Application implements ResetInterface
CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType()
&& 'command' === $input->getCompletionName()
) {
$suggestions->suggestValues(array_filter(array_map(function (Command $command) {
return $command->isHidden() ? null : $command->getName();
}, $this->all())));
$commandNames = [];
foreach ($this->all() as $name => $command) {
// skip hidden commands and aliased commands as they already get added below
if ($command->isHidden() || $command->getName() !== $name) {
continue;
}
$commandNames[] = $command->getName();
foreach ($command->getAliases() as $name) {
$commandNames[] = $name;
}
}
$suggestions->suggestValues(array_filter($commandNames));
return;
}
@@ -767,7 +778,7 @@ class Application implements ResetInterface
*
* @return Command[]
*/
public function all(string $namespace = null)
public function all(?string $namespace = null)
{
$this->init();
@@ -924,11 +935,21 @@ class Application implements ResetInterface
}
switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break;
case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break;
case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break;
case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break;
default: $shellVerbosity = 0; break;
case -1:
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
break;
case 1:
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
break;
case 2:
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
break;
case 3:
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
break;
default:
$shellVerbosity = 0;
break;
}
if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
@@ -974,22 +995,26 @@ class Application implements ResetInterface
}
}
if ($command instanceof SignalableCommandInterface && ($this->signalsToDispatchEvent || $command->getSubscribedSignals())) {
if (!$this->signalRegistry) {
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
if ($this->signalsToDispatchEvent) {
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
if ($commandSignals || null !== $this->dispatcher) {
if (!$this->signalRegistry) {
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
shell_exec('stty '.$sttyMode);
});
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
shell_exec('stty '.$sttyMode);
});
}
}
}
if ($this->dispatcher) {
if (null !== $this->dispatcher) {
foreach ($this->signalsToDispatchEvent as $signal) {
$event = new ConsoleSignalEvent($command, $input, $output, $signal);
@@ -1006,7 +1031,7 @@ class Application implements ResetInterface
}
}
foreach ($command->getSubscribedSignals() as $signal) {
foreach ($commandSignals as $signal) {
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
}
}
@@ -1122,7 +1147,7 @@ class Application implements ResetInterface
*
* @return string
*/
public function extractNamespace(string $name, int $limit = null)
public function extractNamespace(string $name, ?int $limit = null)
{
$parts = explode(':', $name, -1);

View File

@@ -57,7 +57,7 @@ class GithubActionReporter
*
* @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
*/
public function error(string $message, string $file = null, int $line = null, int $col = null): void
public function error(string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
{
$this->log('error', $message, $file, $line, $col);
}
@@ -67,7 +67,7 @@ class GithubActionReporter
*
* @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message
*/
public function warning(string $message, string $file = null, int $line = null, int $col = null): void
public function warning(string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
{
$this->log('warning', $message, $file, $line, $col);
}
@@ -77,12 +77,12 @@ class GithubActionReporter
*
* @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message
*/
public function debug(string $message, string $file = null, int $line = null, int $col = null): void
public function debug(string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
{
$this->log('debug', $message, $file, $line, $col);
}
private function log(string $type, string $message, string $file = null, int $line = null, int $col = null): void
private function log(string $type, string $message, ?string $file = null, ?int $line = null, ?int $col = null): void
{
// Some values must be encoded.
$message = strtr($message, self::ESCAPED_DATA);

View File

@@ -96,7 +96,7 @@ class Command
*
* @throws LogicException When the command name is empty
*/
public function __construct(string $name = null)
public function __construct(?string $name = null)
{
$this->definition = new InputDefinition();
@@ -132,7 +132,7 @@ class Command
$this->ignoreValidationErrors = true;
}
public function setApplication(Application $application = null)
public function setApplication(?Application $application = null)
{
$this->application = $application;
if ($application) {
@@ -242,7 +242,7 @@ class Command
*
* @return int The command exit code
*
* @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}.
* @throws ExceptionInterface When input binding fails. Bypass this by calling {@link ignoreValidationErrors()}.
*
* @see setCode()
* @see execute()
@@ -429,11 +429,11 @@ class Command
* @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
*
* @throws InvalidArgumentException When argument mode is not valid
*
* @return $this
*
* @throws InvalidArgumentException When argument mode is not valid
*/
public function addArgument(string $name, int $mode = null, string $description = '', $default = null)
public function addArgument(string $name, ?int $mode = null, string $description = '', $default = null)
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
if (null !== $this->fullDefinition) {
@@ -450,11 +450,11 @@ class Command
* @param int|null $mode The option mode: One of the InputOption::VALUE_* constants
* @param mixed $default The default value (must be null for InputOption::VALUE_NONE)
*
* @throws InvalidArgumentException If option mode is invalid or incompatible
*
* @return $this
*
* @throws InvalidArgumentException If option mode is invalid or incompatible
*/
public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
public function addOption(string $name, $shortcut = null, ?int $mode = null, string $description = '', $default = null)
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
if (null !== $this->fullDefinition) {
@@ -518,7 +518,7 @@ class Command
*
* @final since Symfony 5.1
*/
public function setHidden(bool $hidden /*= true*/)
public function setHidden(bool $hidden /* = true */)
{
$this->hidden = $hidden;

View File

@@ -65,15 +65,15 @@ final class CompleteCommand extends Command
{
try {
// uncomment when a bugfix or BC break has been introduced in the shell completion scripts
//$version = $input->getOption('symfony');
//if ($version && version_compare($version, 'x.y', '>=')) {
// $version = $input->getOption('symfony');
// if ($version && version_compare($version, 'x.y', '>=')) {
// $message = sprintf('Completion script version is not supported ("%s" given, ">=x.y" required).', $version);
// $this->log($message);
// $output->writeln($message.' Install the Symfony completion script again by using the "completion" command.');
// return 126;
//}
// }
$shell = $input->getOption('shell');
if (!$shell) {
@@ -105,11 +105,12 @@ final class CompleteCommand extends Command
} elseif (
$completionInput->mustSuggestArgumentValuesFor('command')
&& $command->getName() !== $completionInput->getCompletionValue()
&& !\in_array($completionInput->getCompletionValue(), $command->getAliases(), true)
) {
$this->log(' No command found, completing using the Application class.');
// expand shortcut names ("cache:cl<TAB>") into their full name ("cache:clear")
$suggestions->suggestValue($command->getName());
$suggestions->suggestValues(array_filter(array_merge([$command->getName()], $command->getAliases())));
} else {
$command->mergeApplicationDefinition();
$completionInput->bind($command->getDefinition());
@@ -154,10 +155,10 @@ final class CompleteCommand extends Command
throw $e;
}
return self::FAILURE;
return 2;
}
return self::SUCCESS;
return 0;
}
private function createCompletionInput(InputInterface $input): CompletionInput

View File

@@ -41,7 +41,7 @@ final class DumpCompletionCommand extends Command
{
$fullCommand = $_SERVER['PHP_SELF'];
$commandName = basename($fullCommand);
$fullCommand = realpath($fullCommand) ?: $fullCommand;
$fullCommand = @realpath($fullCommand) ?: $fullCommand;
$this
->setHelp(<<<EOH
@@ -53,7 +53,7 @@ to use shell autocompletion (currently only bash completion is supported).
Dump the script to a global completion file and restart your shell:
<info>%command.full_name% bash | sudo tee /etc/bash_completion.d/${commandName}</>
<info>%command.full_name% bash | sudo tee /etc/bash_completion.d/{$commandName}</>
Or dump the script to a local file and source it:
@@ -70,7 +70,7 @@ Or dump the script to a local file and source it:
Add this to the end of your shell configuration file (e.g. <info>"~/.bashrc"</>):
<info>eval "$(${fullCommand} completion bash)"</>
<info>eval "$({$fullCommand} completion bash)"</>
EOH
)
->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given')
@@ -85,7 +85,7 @@ EOH
if ($input->getOption('debug')) {
$this->tailDebugLog($commandName, $output);
return self::SUCCESS;
return 0;
}
$shell = $input->getArgument('shell') ?? self::guessShell();
@@ -93,15 +93,21 @@ EOH
if (!file_exists($completionFile)) {
$supportedShells = $this->getSupportedShells();
($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output)
->writeln(sprintf('<error>Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").</>', $shell, implode('", "', $supportedShells)));
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
if ($shell) {
$output->writeln(sprintf('<error>Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").</>', $shell, implode('", "', $supportedShells)));
} else {
$output->writeln(sprintf('<error>Shell not detected, Symfony shell completion only supports "%s").</>', implode('", "', $supportedShells)));
}
return self::INVALID;
return 2;
}
$output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, $this->getApplication()->getVersion()], file_get_contents($completionFile)));
return self::SUCCESS;
return 0;
}
private static function guessShell(): string
@@ -126,8 +132,14 @@ EOH
*/
private function getSupportedShells(): array
{
return array_map(function ($f) {
return pathinfo($f, \PATHINFO_EXTENSION);
}, glob(__DIR__.'/../Resources/completion.*'));
$shells = [];
foreach (new \DirectoryIterator(__DIR__.'/../Resources/') as $file) {
if (str_starts_with($file->getBasename(), 'completion.') && $file->isFile()) {
$shells[] = $file->getExtension();
}
}
return $shells;
}
}

View File

@@ -43,7 +43,7 @@ final class LazyCommand extends Command
$this->getCommand()->ignoreValidationErrors();
}
public function setApplication(Application $application = null): void
public function setApplication(?Application $application = null): void
{
if ($this->command instanceof parent) {
$this->command->setApplication($application);
@@ -117,7 +117,7 @@ final class LazyCommand extends Command
/**
* @return $this
*/
public function addArgument(string $name, int $mode = null, string $description = '', $default = null): self
public function addArgument(string $name, ?int $mode = null, string $description = '', $default = null): self
{
$this->getCommand()->addArgument($name, $mode, $description, $default);
@@ -127,7 +127,7 @@ final class LazyCommand extends Command
/**
* @return $this
*/
public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null): self
public function addOption(string $name, $shortcut = null, ?int $mode = null, string $description = '', $default = null): self
{
$this->getCommand()->addOption($name, $shortcut, $mode, $description, $default);

View File

@@ -30,7 +30,7 @@ trait LockableTrait
/**
* Locks a command.
*/
private function lock(string $name = null, bool $blocking = false): bool
private function lock(?string $name = null, bool $blocking = false): bool
{
if (!class_exists(SemaphoreStore::class)) {
throw new LogicException('To enable the locking feature you must install the symfony/lock component.');

View File

@@ -24,6 +24,9 @@ class BashCompletionOutput implements CompletionOutputInterface
$values = $suggestions->getValueSuggestions();
foreach ($suggestions->getOptionSuggestions() as $option) {
$values[] = '--'.$option->getName();
if ($option->isNegatable()) {
$values[] = '--no-'.$option->getName();
}
}
$output->writeln(implode("\n", $values));
}

View File

@@ -67,7 +67,7 @@ class AddConsoleCommandPass implements CompilerPassInterface
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
}
$aliases = $class::getDefaultName();
$aliases = str_replace('%', '%%', $class::getDefaultName() ?? '');
}
$aliases = explode('|', $aliases ?? '');
@@ -124,7 +124,7 @@ class AddConsoleCommandPass implements CompilerPassInterface
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
}
$description = $class::getDefaultDescription();
$description = str_replace('%', '%%', $class::getDefaultDescription() ?? '');
}
if ($description) {

View File

@@ -43,7 +43,7 @@ class ApplicationDescription
*/
private $aliases;
public function __construct(Application $application, string $namespace = null, bool $showHidden = false)
public function __construct(Application $application, ?string $namespace = null, bool $showHidden = false)
{
$this->application = $application;
$this->namespace = $namespace;
@@ -131,7 +131,7 @@ class ApplicationDescription
}
if ($namespacedCommands) {
ksort($namespacedCommands);
ksort($namespacedCommands, \SORT_STRING);
foreach ($namespacedCommands as $key => $commandsSet) {
ksort($commandsSet);
$sortedCommands[$key] = $commandsSet;

View File

@@ -79,7 +79,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
public function getApplicationDocument(Application $application, string $namespace = null, bool $short = false): \DOMDocument
public function getApplicationDocument(Application $application, ?string $namespace = null, bool $short = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($rootXml = $dom->createElement('symfony'));

View File

@@ -12,7 +12,10 @@
namespace Symfony\Component\Console\Event;
/**
* Allows to do things before the command is executed, like skipping the command or changing the input.
* Allows to do things before the command is executed, like skipping the command or executing code before the command is
* going to be executed.
*
* Changing the input arguments will have no effect.
*
* @author Fabien Potencier <fabien@symfony.com>
*/

View File

@@ -25,7 +25,7 @@ final class ConsoleErrorEvent extends ConsoleEvent
private $error;
private $exitCode;
public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, Command $command = null)
public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, ?Command $command = null)
{
parent::__construct($command, $input, $output);

View File

@@ -26,7 +26,7 @@ class ErrorListener implements EventSubscriberInterface
{
private $logger;
public function __construct(LoggerInterface $logger = null)
public function __construct(?LoggerInterface $logger = null)
{
$this->logger = $logger;
}

View File

@@ -26,7 +26,7 @@ class CommandNotFoundException extends \InvalidArgumentException implements Exce
* @param int $code Exception code
* @param \Throwable|null $previous Previous exception used for the exception chaining
*/
public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null)
public function __construct(string $message, array $alternatives = [], int $code = 0, ?\Throwable $previous = null)
{
parent::__construct($message, $code, $previous);

View File

@@ -12,7 +12,7 @@
namespace Symfony\Component\Console\Exception;
/**
* Represents an incorrect option name typed in the console.
* Represents an incorrect option name or value typed in the console.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/

View File

@@ -27,7 +27,7 @@ final class NullOutputFormatterStyle implements OutputFormatterStyleInterface
/**
* {@inheritdoc}
*/
public function setBackground(string $color = null): void
public function setBackground(?string $color = null): void
{
// do nothing
}
@@ -35,7 +35,7 @@ final class NullOutputFormatterStyle implements OutputFormatterStyleInterface
/**
* {@inheritdoc}
*/
public function setForeground(string $color = null): void
public function setForeground(?string $color = null): void
{
// do nothing
}

View File

@@ -13,6 +13,8 @@ namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use function Symfony\Component\String\b;
/**
* Formatter class for console output.
*
@@ -258,7 +260,7 @@ class OutputFormatter implements WrappableOutputFormatterInterface
}
preg_match('~(\\n)$~', $text, $matches);
$text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text);
$text = $prefix.$this->addLineBreaks($text, $width);
$text = rtrim($text, "\n").($matches[1] ?? '');
if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) {
@@ -282,4 +284,11 @@ class OutputFormatter implements WrappableOutputFormatterInterface
return implode("\n", $lines);
}
private function addLineBreaks(string $text, int $width): string
{
$encoding = mb_detect_encoding($text, null, true) ?: 'UTF-8';
return b($text)->toCodePointString($encoding)->wordwrap($width, "\n", true)->toByteString($encoding);
}
}

View File

@@ -33,7 +33,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
* @param string|null $foreground The style foreground color name
* @param string|null $background The style background color name
*/
public function __construct(string $foreground = null, string $background = null, array $options = [])
public function __construct(?string $foreground = null, ?string $background = null, array $options = [])
{
$this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options);
}
@@ -41,7 +41,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
/**
* {@inheritdoc}
*/
public function setForeground(string $color = null)
public function setForeground(?string $color = null)
{
$this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options);
}
@@ -49,7 +49,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
/**
* {@inheritdoc}
*/
public function setBackground(string $color = null)
public function setBackground(?string $color = null)
{
$this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options);
}
@@ -96,7 +96,8 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
{
if (null === $this->handlesHrefGracefully) {
$this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100);
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100)
&& !isset($_SERVER['IDEA_INITIAL_DIRECTORY']);
}
if (null !== $this->href && $this->handlesHrefGracefully) {

View File

@@ -21,12 +21,12 @@ interface OutputFormatterStyleInterface
/**
* Sets style foreground color.
*/
public function setForeground(string $color = null);
public function setForeground(?string $color = null);
/**
* Sets style background color.
*/
public function setBackground(string $color = null);
public function setBackground(?string $color = null);
/**
* Sets some specific style option.

View File

@@ -26,7 +26,7 @@ class OutputFormatterStyleStack implements ResetInterface
private $emptyStyle;
public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
public function __construct(?OutputFormatterStyleInterface $emptyStyle = null)
{
$this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle();
$this->reset();
@@ -55,7 +55,7 @@ class OutputFormatterStyleStack implements ResetInterface
*
* @throws InvalidArgumentException When style tags incorrectly nested
*/
public function pop(OutputFormatterStyleInterface $style = null)
public function pop(?OutputFormatterStyleInterface $style = null)
{
if (empty($this->styles)) {
return $this->emptyStyle;

View File

@@ -26,7 +26,7 @@ final class Dumper
private $cloner;
private $handler;
public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
public function __construct(OutputInterface $output, ?CliDumper $dumper = null, ?ClonerInterface $cloner = null)
{
$this->output = $output;
$this->dumper = $dumper;

View File

@@ -26,7 +26,7 @@ abstract class Helper implements HelperInterface
/**
* {@inheritdoc}
*/
public function setHelperSet(HelperSet $helperSet = null)
public function setHelperSet(?HelperSet $helperSet = null)
{
$this->helperSet = $helperSet;
}
@@ -96,7 +96,7 @@ abstract class Helper implements HelperInterface
*
* @return string
*/
public static function substr(?string $string, int $from, int $length = null)
public static function substr(?string $string, int $from, ?int $length = null)
{
$string ?? $string = '';
@@ -171,6 +171,8 @@ abstract class Helper implements HelperInterface
$string = $formatter->format($string ?? '');
// remove already formatted characters
$string = preg_replace("/\033\[[^m]*m/", '', $string ?? '');
// remove terminal hyperlinks
$string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string ?? '');
$formatter->setDecorated($isDecorated);
return $string;

View File

@@ -21,7 +21,7 @@ interface HelperInterface
/**
* Sets the helper set associated with this helper.
*/
public function setHelperSet(HelperSet $helperSet = null);
public function setHelperSet(?HelperSet $helperSet = null);
/**
* Gets the helper set associated with this helper.

View File

@@ -37,7 +37,7 @@ class HelperSet implements \IteratorAggregate
}
}
public function set(HelperInterface $helper, string $alias = null)
public function set(HelperInterface $helper, ?string $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
@@ -76,7 +76,7 @@ class HelperSet implements \IteratorAggregate
/**
* @deprecated since Symfony 5.4
*/
public function setCommand(Command $command = null)
public function setCommand(?Command $command = null)
{
trigger_deprecation('symfony/console', '5.4', 'Method "%s()" is deprecated.', __METHOD__);

View File

@@ -32,7 +32,7 @@ class ProcessHelper extends Helper
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
*/
public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
public function run(OutputInterface $output, $cmd, ?string $error = null, ?callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
{
if (!class_exists(Process::class)) {
throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".');
@@ -98,7 +98,7 @@ class ProcessHelper extends Helper
*
* @see run()
*/
public function mustRun(OutputInterface $output, $cmd, string $error = null, callable $callback = null): Process
public function mustRun(OutputInterface $output, $cmd, ?string $error = null, ?callable $callback = null): Process
{
$process = $this->run($output, $cmd, $error, $callback);
@@ -112,7 +112,7 @@ class ProcessHelper extends Helper
/**
* Wraps a Process callback to add debugging output.
*/
public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable
public function wrapCallback(OutputInterface $output, Process $process, ?callable $callback = null): callable
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();

View File

@@ -53,7 +53,6 @@ final class ProgressBar
private $startTime;
private $stepWidth;
private $percent = 0.0;
private $formatLineCount;
private $messages = [];
private $overwrite = true;
private $terminal;
@@ -170,9 +169,12 @@ final class ProgressBar
$this->messages[$name] = $message;
}
/**
* @return string|null
*/
public function getMessage(string $name = 'message')
{
return $this->messages[$name];
return $this->messages[$name] ?? null;
}
public function getStartTime(): int
@@ -294,7 +296,7 @@ final class ProgressBar
*
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable
*/
public function iterate(iterable $iterable, int $max = null): iterable
public function iterate(iterable $iterable, ?int $max = null): iterable
{
$this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0));
@@ -312,7 +314,7 @@ final class ProgressBar
*
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
*/
public function start(int $max = null)
public function start(?int $max = null)
{
$this->startTime = time();
$this->step = 0;
@@ -446,8 +448,6 @@ final class ProgressBar
} else {
$this->format = $format;
}
$this->formatLineCount = substr_count($this->format, "\n");
}
/**
@@ -464,7 +464,7 @@ final class ProgressBar
if ($this->overwrite) {
if (null !== $this->previousMessage) {
if ($this->output instanceof ConsoleSectionOutput) {
$messageLines = explode("\n", $message);
$messageLines = explode("\n", $this->previousMessage);
$lineCount = \count($messageLines);
foreach ($messageLines as $messageLine) {
$messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine));
@@ -474,7 +474,8 @@ final class ProgressBar
}
$this->output->clear($lineCount);
} else {
for ($i = 0; $i < $this->formatLineCount; ++$i) {
$lineCount = substr_count($this->previousMessage, "\n");
for ($i = 0; $i < $lineCount; ++$i) {
$this->cursor->moveToColumn(1);
$this->cursor->clearLine();
$this->cursor->moveUp();

View File

@@ -50,7 +50,7 @@ class ProgressIndicator
* @param int $indicatorChangeInterval Change interval in milliseconds
* @param array|null $indicatorValues Animated indicator characters
*/
public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
public function __construct(OutputInterface $output, ?string $format = null, int $indicatorChangeInterval = 100, ?array $indicatorValues = null)
{
$this->output = $output;
@@ -129,8 +129,6 @@ class ProgressIndicator
/**
* Finish the indicator with message.
*
* @param $message
*/
public function finish(string $message)
{

View File

@@ -24,6 +24,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;
use function Symfony\Component\String\s;
/**
@@ -127,7 +128,18 @@ class QuestionHelper extends Helper
}
if (false === $ret) {
$isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true;
if (!$isBlocked) {
stream_set_blocking($inputStream, true);
}
$ret = $this->readInput($inputStream, $question);
if (!$isBlocked) {
stream_set_blocking($inputStream, false);
}
if (false === $ret) {
throw new MissingInputException('Aborted.');
}
@@ -491,21 +503,7 @@ class QuestionHelper extends Helper
return self::$stdinIsInteractive;
}
if (\function_exists('stream_isatty')) {
return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r'));
}
if (\function_exists('posix_isatty')) {
return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r'));
}
if (!\function_exists('exec')) {
return self::$stdinIsInteractive = true;
}
exec('stty 2> /dev/null', $output, $status);
return self::$stdinIsInteractive = 1 !== $status;
return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r'));
}
/**

View File

@@ -451,7 +451,7 @@ class Table
*
* +-----+-----------+-------+
*/
private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
private function renderRowSeparator(int $type = self::SEPARATOR_MID, ?string $title = null, ?string $titleFormat = null)
{
if (0 === $count = $this->numberOfColumns) {
return;
@@ -516,7 +516,7 @@ class Table
*
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
*/
private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
private function renderRow(array $row, string $cellFormat, ?string $firstCellFormat = null)
{
$rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
$columns = $this->getRowColumns($row);
@@ -621,9 +621,10 @@ class Table
if (!strstr($cell ?? '', "\n")) {
continue;
}
$escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
$eol = str_contains($cell ?? '', "\r\n") ? "\r\n" : "\n";
$escaped = implode($eol, array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode($eol, $cell)));
$cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
$lines = explode($eol, str_replace($eol, '<fg=default;bg=default></>'.$eol, $cell));
foreach ($lines as $lineKey => $line) {
if ($colspan > 1) {
$line = new TableCell($line, ['colspan' => $colspan]);
@@ -678,15 +679,16 @@ class Table
{
$unmergedRows = [];
foreach ($rows[$line] as $column => $cell) {
if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
}
if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
$nbLines = $cell->getRowspan() - 1;
$lines = [$cell];
if (strstr($cell, "\n")) {
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
$nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
$eol = str_contains($cell, "\r\n") ? "\r\n" : "\n";
$lines = explode($eol, str_replace($eol, '<fg=default;bg=default>'.$eol.'</>', $cell));
$nbLines = \count($lines) > $nbLines ? substr_count($cell, $eol) : $nbLines;
$rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
unset($lines[0]);
@@ -800,18 +802,18 @@ class Table
continue;
}
foreach ($row as $i => $cell) {
if ($cell instanceof TableCell) {
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::width($textContent);
if ($textLength > 0) {
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
foreach ($row as $i => $cell) {
if ($cell instanceof TableCell) {
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::width($textContent);
if ($textLength > 0) {
$contentColumns = mb_str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
}
}
}
}
}
$lengths[] = $this->getCellWidth($row, $column);
}

View File

@@ -90,7 +90,7 @@ class TableStyle
*
* @return $this
*/
public function setHorizontalBorderChars(string $outside, string $inside = null): self
public function setHorizontalBorderChars(string $outside, ?string $inside = null): self
{
$this->horizontalOutsideBorderChar = $outside;
$this->horizontalInsideBorderChar = $inside ?? $outside;
@@ -115,7 +115,7 @@ class TableStyle
*
* @return $this
*/
public function setVerticalBorderChars(string $outside, string $inside = null): self
public function setVerticalBorderChars(string $outside, ?string $inside = null): self
{
$this->verticalOutsideBorderChar = $outside;
$this->verticalInsideBorderChar = $inside ?? $outside;
@@ -169,7 +169,7 @@ class TableStyle
*
* @return $this
*/
public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): self
public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, ?string $topLeftBottom = null, ?string $topMidBottom = null, ?string $topRightBottom = null): self
{
$this->crossingChar = $cross;
$this->crossingTopLeftChar = $topLeft;

View File

@@ -43,7 +43,7 @@ class ArgvInput extends Input
private $tokens;
private $parsed;
public function __construct(array $argv = null, InputDefinition $definition = null)
public function __construct(?array $argv = null, ?InputDefinition $definition = null)
{
$argv = $argv ?? $_SERVER['argv'] ?? [];

View File

@@ -27,7 +27,7 @@ class ArrayInput extends Input
{
private $parameters;
public function __construct(array $parameters, InputDefinition $definition = null)
public function __construct(array $parameters, ?InputDefinition $definition = null)
{
$this->parameters = $parameters;

View File

@@ -33,7 +33,7 @@ abstract class Input implements InputInterface, StreamableInputInterface
protected $arguments = [];
protected $interactive = true;
public function __construct(InputDefinition $definition = null)
public function __construct(?InputDefinition $definition = null)
{
if (null === $definition) {
$this->definition = new InputDefinition();

View File

@@ -32,13 +32,13 @@ class InputArgument
/**
* @param string $name The argument name
* @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY
* @param string $description A description text
* @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only)
*
* @throws InvalidArgumentException When argument mode is not valid
*/
public function __construct(string $name, int $mode = null, string $description = '', $default = null)
public function __construct(string $name, ?int $mode = null, string $description = '', $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
@@ -92,7 +92,7 @@ class InputArgument
*/
public function setDefault($default = null)
{
if (self::REQUIRED === $this->mode && null !== $default) {
if ($this->isRequired() && null !== $default) {
throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}

View File

@@ -59,7 +59,7 @@ class InputOption
*
* @throws InvalidArgumentException If option mode is invalid or incompatible
*/
public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
public function __construct(string $name, $shortcut = null, ?int $mode = null, string $description = '', $default = null)
{
if (str_starts_with($name, '--')) {
$name = substr($name, 2);
@@ -69,7 +69,7 @@ class InputOption
throw new InvalidArgumentException('An option name cannot be empty.');
}
if (empty($shortcut)) {
if ('' === $shortcut || [] === $shortcut || false === $shortcut) {
$shortcut = null;
}
@@ -78,10 +78,10 @@ class InputOption
$shortcut = implode('|', $shortcut);
}
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
$shortcuts = array_filter($shortcuts);
$shortcuts = array_filter($shortcuts, 'strlen');
$shortcut = implode('|', $shortcuts);
if (empty($shortcut)) {
if ('' === $shortcut) {
throw new InvalidArgumentException('An option shortcut cannot be empty.');
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -110,7 +110,7 @@ class ConsoleLogger extends AbstractLogger
$replacements = [];
foreach ($context as $key => $val) {
if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
if (null === $val || \is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
$replacements["{{$key}}"] = $val;
} elseif ($val instanceof \DateTimeInterface) {
$replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);

View File

@@ -37,7 +37,7 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
* @param bool|null $decorated Whether to decorate messages (null for auto-guessing)
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*/
public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
public function __construct(int $verbosity = self::VERBOSITY_NORMAL, ?bool $decorated = null, ?OutputFormatterInterface $formatter = null)
{
parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);

View File

@@ -43,7 +43,7 @@ class ConsoleSectionOutput extends StreamOutput
*
* @param int $lines Number of lines to clear. If null, then the entire output of this section is cleared
*/
public function clear(int $lines = null)
public function clear(?int $lines = null)
{
if (empty($this->content) || !$this->isDecorated()) {
return;

View File

@@ -37,7 +37,7 @@ abstract class Output implements OutputInterface
* @param bool $decorated Whether to decorate messages
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*/
public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null)
public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, ?OutputFormatterInterface $formatter = null)
{
$this->verbosity = $verbosity ?? self::VERBOSITY_NORMAL;
$this->formatter = $formatter ?? new OutputFormatter();

View File

@@ -39,7 +39,7 @@ class StreamOutput extends Output
*
* @throws InvalidArgumentException When first argument is not a real stream
*/
public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, ?bool $decorated = null, ?OutputFormatterInterface $formatter = null)
{
if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
@@ -64,9 +64,6 @@ class StreamOutput extends Output
return $this->stream;
}
/**
* {@inheritdoc}
*/
protected function doWrite(string $message, bool $newline)
{
if ($newline) {
@@ -98,18 +95,29 @@ class StreamOutput extends Output
return false;
}
if ('Hyper' === getenv('TERM_PROGRAM')) {
// Detect msysgit/mingw and assume this is a tty because detection
// does not work correctly, see https://github.com/composer/composer/issues/9690
if (!@stream_isatty($this->stream) && !\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) {
return false;
}
if ('\\' === \DIRECTORY_SEPARATOR && @sapi_windows_vt100_support($this->stream)) {
return true;
}
if (\DIRECTORY_SEPARATOR === '\\') {
return (\function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($this->stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
if ('Hyper' === getenv('TERM_PROGRAM')
|| false !== getenv('COLORTERM')
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
) {
return true;
}
return stream_isatty($this->stream);
if ('dumb' === $term = (string) getenv('TERM')) {
return false;
}
// See https://github.com/chalk/supports-color/blob/d4f413efaf8da045c5ab440ed418ef02dbb28bf1/index.js#L157
return preg_match('/^((screen|xterm|vt100|vt220|putty|rxvt|ansi|cygwin|linux).*)|(.*-256(color)?(-bce)?)$/', $term);
}
}

View File

@@ -24,7 +24,7 @@ class TrimmedBufferOutput extends Output
private $maxLength;
private $buffer = '';
public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null)
public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, ?OutputFormatterInterface $formatter = null)
{
if ($maxLength <= 0) {
throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength));

View File

@@ -186,7 +186,7 @@ class Question
*
* @return $this
*/
public function setAutocompleterCallback(callable $callback = null): self
public function setAutocompleterCallback(?callable $callback = null): self
{
if ($this->hidden && null !== $callback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
@@ -202,7 +202,7 @@ class Question
*
* @return $this
*/
public function setValidator(callable $validator = null)
public function setValidator(?callable $validator = null)
{
$this->validator = $validator;

View File

@@ -11,18 +11,21 @@ _sf_{{ COMMAND_NAME }}() {
local sf_cmd="${COMP_WORDS[0]}"
# for an alias, get the real script behind it
if [[ $(type -t $sf_cmd) == "alias" ]]; then
sf_cmd_type=$(type -t $sf_cmd)
if [[ $sf_cmd_type == "alias" ]]; then
sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/")
elif [[ $sf_cmd_type == "file" ]]; then
sf_cmd=$(type -p $sf_cmd)
fi
if [ ! -f "$sf_cmd" ]; then
if [[ $sf_cmd_type != "function" && ! -x $sf_cmd ]]; then
return 1
fi
local cur prev words cword
_get_comp_words_by_ref -n := cur prev words cword
local completecmd=("$sf_cmd" "_complete" "-sbash" "-c$cword" "-S{{ VERSION }}")
local completecmd=("$sf_cmd" "_complete" "--no-interaction" "-sbash" "-c$cword" "-S{{ VERSION }}")
for w in ${words[@]}; do
w=$(printf -- '%b' "$w")
# remove quotes from typed values

View File

@@ -43,7 +43,7 @@ final class SignalRegistry
return false;
}
if (\in_array('pcntl_signal', explode(',', ini_get('disable_functions')))) {
if (\in_array('pcntl_signal', explode(',', \ini_get('disable_functions')))) {
return false;
}

View File

@@ -46,7 +46,7 @@ class SingleCommandApplication extends Command
return $this;
}
public function run(InputInterface $input = null, OutputInterface $output = null): int
public function run(?InputInterface $input = null, ?OutputInterface $output = null): int
{
if ($this->running) {
return parent::run($input, $output);

View File

@@ -85,14 +85,14 @@ interface StyleInterface
*
* @return mixed
*/
public function ask(string $question, string $default = null, callable $validator = null);
public function ask(string $question, ?string $default = null, ?callable $validator = null);
/**
* Asks a question with the user input hidden.
*
* @return mixed
*/
public function askHidden(string $question, callable $validator = null);
public function askHidden(string $question, ?callable $validator = null);
/**
* Asks for confirmation.

View File

@@ -61,7 +61,7 @@ class SymfonyStyle extends OutputStyle
*
* @param string|array $messages The message to write in the block
*/
public function block($messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true)
public function block($messages, ?string $type = null, ?string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true)
{
$messages = \is_array($messages) ? array_values($messages) : [$messages];
@@ -250,7 +250,7 @@ class SymfonyStyle extends OutputStyle
/**
* {@inheritdoc}
*/
public function ask(string $question, string $default = null, callable $validator = null)
public function ask(string $question, ?string $default = null, ?callable $validator = null)
{
$question = new Question($question, $default);
$question->setValidator($validator);
@@ -261,7 +261,7 @@ class SymfonyStyle extends OutputStyle
/**
* {@inheritdoc}
*/
public function askHidden(string $question, callable $validator = null)
public function askHidden(string $question, ?callable $validator = null)
{
$question = new Question($question);
@@ -338,7 +338,7 @@ class SymfonyStyle extends OutputStyle
/**
* @see ProgressBar::iterate()
*/
public function progressIterate(iterable $iterable, int $max = null): iterable
public function progressIterate(iterable $iterable, ?int $max = null): iterable
{
yield from $this->createProgressBar()->iterate($iterable, $max);
@@ -440,18 +440,18 @@ class SymfonyStyle extends OutputStyle
$chars = substr(str_replace(\PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
if (!isset($chars[0])) {
$this->newLine(); //empty history, so we should start with a new line.
$this->newLine(); // empty history, so we should start with a new line.
return;
}
//Prepend new line for each non LF chars (This means no blank line was output before)
// Prepend new line for each non LF chars (This means no blank line was output before)
$this->newLine(2 - substr_count($chars, "\n"));
}
private function autoPrependText(): void
{
$fetched = $this->bufferedOutput->fetch();
//Prepend new line if last char isn't EOL:
// Prepend new line if last char isn't EOL:
if (!str_ends_with($fetched, "\n")) {
$this->newLine();
}
@@ -463,7 +463,7 @@ class SymfonyStyle extends OutputStyle
$this->bufferedOutput->write($message, $newLine, $type);
}
private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
private function createBlock(iterable $messages, ?string $type = null, ?string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
{
$indentLength = 0;
$prefixLength = Helper::width(Helper::removeDecoration($this->getFormatter(), $prefix));

View File

@@ -64,20 +64,19 @@ class Terminal
return self::$stty;
}
// skip check if exec function is disabled
if (!\function_exists('exec')) {
// skip check if shell_exec function is disabled
if (!\function_exists('shell_exec')) {
return false;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = 0 === $exitcode;
return self::$stty = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null'));
}
private static function initDimensions()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) {
$ansicon = getenv('ANSICON');
if (false !== $ansicon && preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim($ansicon), $matches)) {
// extract [w, H] from "wxh (WxH)"
// or [w, h] from "wxh"
self::$width = (int) $matches[1];
@@ -157,6 +156,8 @@ class Terminal
2 => ['pipe', 'w'],
];
$cp = \function_exists('sapi_windows_cp_set') ? sapi_windows_cp_get() : 0;
$process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (!\is_resource($process)) {
return null;
@@ -167,6 +168,10 @@ class Terminal
fclose($pipes[2]);
proc_close($process);
if ($cp) {
sapi_windows_cp_set($cp);
}
return $info;
}
}

View File

@@ -49,17 +49,37 @@ class ApplicationTester
*/
public function run(array $input, array $options = [])
{
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
$prevShellVerbosity = getenv('SHELL_VERBOSITY');
try {
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
if ($this->inputs) {
$this->input->setStream(self::createStream($this->inputs));
}
$this->initOutput($options);
return $this->statusCode = $this->application->run($this->input, $this->output);
} finally {
// SHELL_VERBOSITY is set by Application::configureIO so we need to unset/reset it
// to its previous value to avoid one test's verbosity to spread to the following tests
if (false === $prevShellVerbosity) {
if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY');
}
unset($_ENV['SHELL_VERBOSITY']);
unset($_SERVER['SHELL_VERBOSITY']);
} else {
if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY='.$prevShellVerbosity);
}
$_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity;
$_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity;
}
}
if ($this->inputs) {
$this->input->setStream(self::createStream($this->inputs));
}
$this->initOutput($options);
return $this->statusCode = $this->application->run($this->input, $this->output);
}
}

View File

@@ -35,9 +35,9 @@ trait TesterTrait
/**
* Gets the display returned by the last execution of the command or application.
*
* @throws \RuntimeException If it's called before the execute method
*
* @return string
*
* @throws \RuntimeException If it's called before the execute method
*/
public function getDisplay(bool $normalize = false)
{
@@ -103,9 +103,9 @@ trait TesterTrait
/**
* Gets the status code returned by the last execution of the command or application.
*
* @throws \RuntimeException If it's called before the execute method
*
* @return int
*
* @throws \RuntimeException If it's called before the execute method
*/
public function getStatusCode()
{

View File

@@ -2,7 +2,7 @@
"name": "symfony/console",
"type": "library",
"description": "Eases the creation of beautiful and testable command line interfaces",
"keywords": ["console", "cli", "command line", "terminal"],
"keywords": ["console", "cli", "command-line", "terminal"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-2023 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -49,22 +49,22 @@ class TokenizerPatterns
$this->identifierPattern = '-?(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*';
$this->hashPattern = '#((?:'.$this->nmCharPattern.')+)';
$this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)';
$this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*';
$this->quotedStringPattern = '([^\n\r\f\\\\%s]|'.$this->stringEscapePattern.')*';
}
public function getNewLineEscapePattern(): string
{
return '~^'.$this->newLineEscapePattern.'~';
return '~'.$this->newLineEscapePattern.'~';
}
public function getSimpleEscapePattern(): string
{
return '~^'.$this->simpleEscapePattern.'~';
return '~'.$this->simpleEscapePattern.'~';
}
public function getUnicodeEscapePattern(): string
{
return '~^'.$this->unicodeEscapePattern.'~i';
return '~'.$this->unicodeEscapePattern.'~i';
}
public function getIdentifierPattern(): string

View File

@@ -53,7 +53,7 @@ class BufferingLogger extends AbstractLogger
foreach ($this->logs as [$level, $message, $context]) {
if (false !== strpos($message, '{')) {
foreach ($context as $key => $val) {
if (null === $val || is_scalar($val) || (\is_object($val) && \is_callable([$val, '__toString']))) {
if (null === $val || \is_scalar($val) || (\is_object($val) && \is_callable([$val, '__toString']))) {
$message = str_replace("{{$key}}", $val, $message);
} elseif ($val instanceof \DateTimeInterface) {
$message = str_replace("{{$key}}", $val->format(\DateTime::RFC3339), $message);

View File

@@ -24,14 +24,13 @@ class Debug
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
ini_set('display_errors', 0);
} elseif (!filter_var(ini_get('log_errors'), \FILTER_VALIDATE_BOOLEAN) || ini_get('error_log')) {
} elseif (!filter_var(\ini_get('log_errors'), \FILTER_VALIDATE_BOOLEAN) || \ini_get('error_log')) {
// CLI - display errors only if they're not already logged to STDERR
ini_set('display_errors', 1);
}
@ini_set('zend.assertions', 1);
ini_set('assert.active', 1);
ini_set('assert.warning', 0);
ini_set('assert.exception', 1);
DebugClassLoader::enable();

View File

@@ -21,6 +21,7 @@ use PHPUnit\Framework\MockObject\MockObject;
use Prophecy\Prophecy\ProphecySubjectInterface;
use ProxyManager\Proxy\ProxyInterface;
use Symfony\Component\ErrorHandler\Internal\TentativeTypes;
use Symfony\Component\HttpClient\HttplugClient;
/**
* Autoloader checking if the class is really defined in the file found.
@@ -56,7 +57,7 @@ class DebugClassLoader
'null' => 'null',
'resource' => 'resource',
'boolean' => 'bool',
'true' => 'bool',
'true' => 'true',
'false' => 'false',
'integer' => 'int',
'array' => 'array',
@@ -73,6 +74,8 @@ class DebugClassLoader
'static' => 'static',
'$this' => 'static',
'list' => 'array',
'class-string' => 'string',
'never' => 'never',
];
private const BUILTIN_RETURN_TYPES = [
@@ -90,6 +93,9 @@ class DebugClassLoader
'parent' => true,
'mixed' => true,
'static' => true,
'null' => true,
'true' => true,
'never' => true,
];
private const MAGIC_METHODS = [
@@ -295,7 +301,7 @@ class DebugClassLoader
$this->checkClass($class, $file);
}
private function checkClass(string $class, string $file = null): void
private function checkClass(string $class, ?string $file = null): void
{
$exists = null === $file || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
@@ -409,7 +415,9 @@ class DebugClassLoader
if (!isset(self::$checkedClasses[$use])) {
$this->checkClass($use);
}
if (isset(self::$deprecated[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen) && !isset(self::$deprecated[$class])) {
if (isset(self::$deprecated[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen) && !isset(self::$deprecated[$class])
&& !(HttplugClient::class === $class && \in_array($use, [\Http\Client\HttpClient::class, \Http\Message\RequestFactory::class, \Http\Message\StreamFactory::class, \Http\Message\UriFactory::class], true))
) {
$type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait');
$verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
@@ -755,12 +763,18 @@ class DebugClassLoader
return $ownInterfaces;
}
private function setReturnType(string $types, string $class, string $method, string $filename, ?string $parent, \ReflectionType $returnType = null): void
private function setReturnType(string $types, string $class, string $method, string $filename, ?string $parent, ?\ReflectionType $returnType = null): void
{
if ('__construct' === $method) {
return;
}
if ('null' === $types) {
self::$returnTypes[$class][$method] = ['null', 'null', $class, $filename];
return;
}
if ($nullable = 0 === strpos($types, 'null|')) {
$types = substr($types, 5);
} elseif ($nullable = '|null' === substr($types, -5)) {
@@ -1079,7 +1093,20 @@ EOTXT;
}
$end = $method->isGenerator() ? $i : $method->getEndLine();
$inClosure = false;
$braces = 0;
for (; $i < $end; ++$i) {
if (!$inClosure) {
$inClosure = false !== strpos($code[$i], 'function (');
}
if ($inClosure) {
$braces += substr_count($code[$i], '{') - substr_count($code[$i], '}');
$inClosure = $braces > 0;
continue;
}
if ('void' === $returnType) {
$fixedCode[$i] = str_replace(' return null;', ' return;', $code[$i]);
} elseif ('mixed' === $returnType || '?' === $returnType[0]) {

View File

@@ -20,7 +20,7 @@ class FatalError extends \Error
*
* @param array $error An array as returned by error_get_last()
*/
public function __construct(string $message, int $code, array $error, int $traceOffset = null, bool $traceArgs = true, array $trace = null)
public function __construct(string $message, int $code, array $error, ?int $traceOffset = null, bool $traceArgs = true, ?array $trace = null)
{
parent::__construct($message, $code);

View File

@@ -21,9 +21,6 @@ use Symfony\Component\ErrorHandler\Error\FatalError;
*/
class ClassNotFoundErrorEnhancer implements ErrorEnhancerInterface
{
/**
* {@inheritdoc}
*/
public function enhance(\Throwable $error): ?\Throwable
{
// Some specific versions of PHP produce a fatal error when extending a not found class.
@@ -110,7 +107,8 @@ class ClassNotFoundErrorEnhancer implements ErrorEnhancerInterface
private function findClassInPath(string $path, string $class, string $prefix): array
{
if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) {
$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path);
if (!$path || !is_dir($path)) {
return [];
}

View File

@@ -108,7 +108,7 @@ class ErrorHandler
/**
* Registers the error handler.
*/
public static function register(self $handler = null, bool $replace = true): self
public static function register(?self $handler = null, bool $replace = true): self
{
if (null === self::$reservedMemory) {
self::$reservedMemory = str_repeat('x', 32768);
@@ -181,7 +181,7 @@ class ErrorHandler
}
}
public function __construct(BufferingLogger $bootstrappingLogger = null, bool $debug = false)
public function __construct(?BufferingLogger $bootstrappingLogger = null, bool $debug = false)
{
if ($bootstrappingLogger) {
$this->bootstrappingLogger = $bootstrappingLogger;
@@ -380,8 +380,8 @@ class ErrorHandler
*/
private function reRegister(int $prev): void
{
if ($prev !== $this->thrownErrors | $this->loggedErrors) {
$handler = set_error_handler('var_dump');
if ($prev !== ($this->thrownErrors | $this->loggedErrors)) {
$handler = set_error_handler('is_int');
$handler = \is_array($handler) ? $handler[0] : null;
restore_error_handler();
if ($handler === $this) {
@@ -457,7 +457,7 @@ class ErrorHandler
return true;
}
} else {
if (false !== strpos($message, '@anonymous')) {
if (PHP_VERSION_ID < 80303 && false !== strpos($message, '@anonymous')) {
$backtrace = debug_backtrace(false, 5);
for ($i = 1; isset($backtrace[$i]); ++$i) {
@@ -465,8 +465,7 @@ class ErrorHandler
&& ('trigger_error' === $backtrace[$i]['function'] || 'user_error' === $backtrace[$i]['function'])
) {
if ($backtrace[$i]['args'][0] !== $message) {
$message = $this->parseAnonymousClass($backtrace[$i]['args'][0]);
$logMessage = $this->levels[$type].': '.$message;
$message = $backtrace[$i]['args'][0];
}
break;
@@ -474,6 +473,11 @@ class ErrorHandler
}
}
if (false !== strpos($message, "@anonymous\0")) {
$message = $this->parseAnonymousClass($message);
$logMessage = $this->levels[$type].': '.$message;
}
$errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
if ($throw || $this->tracedErrors & $type) {
@@ -513,7 +517,12 @@ class ErrorHandler
}
// Display the original error message instead of the default one.
$this->handleException($errorAsException);
$exitCode = self::$exitCode;
try {
$this->handleException($errorAsException);
} finally {
self::$exitCode = $exitCode;
}
// Stop the process by giving back the error to the native handler.
return false;
@@ -528,7 +537,7 @@ class ErrorHandler
$log = 0;
} else {
if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404)) {
$currentErrorHandler = set_error_handler('var_dump');
$currentErrorHandler = set_error_handler('is_int');
restore_error_handler();
}
@@ -633,7 +642,7 @@ class ErrorHandler
*
* @internal
*/
public static function handleFatalError(array $error = null): void
public static function handleFatalError(?array $error = null): void
{
if (null === self::$reservedMemory) {
return;
@@ -645,7 +654,7 @@ class ErrorHandler
$sameHandlerLimit = 10;
while (!\is_array($handler) || !$handler[0] instanceof self) {
$handler = set_exception_handler('var_dump');
$handler = set_exception_handler('is_int');
restore_exception_handler();
if (!$handler) {
@@ -665,6 +674,10 @@ class ErrorHandler
set_exception_handler($h);
}
if (!$handler) {
if (null === $error && $exitCode = self::$exitCode) {
register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
}
return;
}
if ($handler !== $h) {
@@ -700,8 +713,7 @@ class ErrorHandler
// Ignore this re-throw
}
if ($exit && self::$exitCode) {
$exitCode = self::$exitCode;
if ($exit && $exitCode = self::$exitCode) {
register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
}
}

View File

@@ -47,7 +47,7 @@ class HtmlErrorRenderer implements ErrorRendererInterface
* @param string|FileLinkFormatter|null $fileLinkFormat
* @param bool|callable $outputBuffer The output buffer as a string or a callable that should return it
*/
public function __construct($debug = false, string $charset = null, $fileLinkFormat = null, string $projectDir = null, $outputBuffer = '', LoggerInterface $logger = null)
public function __construct($debug = false, ?string $charset = null, $fileLinkFormat = null, ?string $projectDir = null, $outputBuffer = '', ?LoggerInterface $logger = null)
{
if (!\is_bool($debug) && !\is_callable($debug)) {
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a boolean or a callable, "%s" given.', __METHOD__, \gettype($debug)));
@@ -58,8 +58,8 @@ class HtmlErrorRenderer implements ErrorRendererInterface
}
$this->debug = $debug;
$this->charset = $charset ?: (ini_get('default_charset') ?: 'UTF-8');
$this->fileLinkFormat = $fileLinkFormat ?: (ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'));
$this->charset = $charset ?: (\ini_get('default_charset') ?: 'UTF-8');
$this->fileLinkFormat = $fileLinkFormat ?: (\ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'));
$this->projectDir = $projectDir;
$this->outputBuffer = $outputBuffer;
$this->logger = $logger;
@@ -173,6 +173,8 @@ class HtmlErrorRenderer implements ErrorRendererInterface
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
} elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>';
} elseif (preg_match('/[^\x07-\x0D\x1B\x20-\xFF]/', $item[1])) {
$formattedValue = '<em>binary string</em>';
} else {
$formattedValue = str_replace("\n", '', $this->escape(var_export($item[1], true)));
}
@@ -233,7 +235,7 @@ class HtmlErrorRenderer implements ErrorRendererInterface
* @param int $line The line number
* @param string $text Use this text for the link rather than the file path
*/
private function formatFile(string $file, int $line, string $text = null): string
private function formatFile(string $file, int $line, ?string $text = null): string
{
$file = trim($file);
@@ -269,13 +271,25 @@ class HtmlErrorRenderer implements ErrorRendererInterface
// highlight_file could throw warnings
// see https://bugs.php.net/25725
$code = @highlight_file($file, true);
// remove main code/span tags
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
// split multiline spans
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', function ($m) {
return "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>';
}, $code);
$content = explode('<br />', $code);
if (\PHP_VERSION_ID >= 80300) {
// remove main pre/code tags
$code = preg_replace('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s', '\\1', $code);
// split multiline code tags
$code = preg_replace_callback('#<code ([^>]++)>((?:[^<]*+\\n)++[^<]*+)</code>#', function ($m) {
return "<code $m[1]>".str_replace("\n", "</code>\n<code $m[1]>", $m[2]).'</code>';
}, $code);
// Convert spaces to html entities to preserve indentation when rendered
$code = str_replace(' ', '&nbsp;', $code);
$content = explode("\n", $code);
} else {
// remove main code/span tags
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
// split multiline spans
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', function ($m) {
return "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>';
}, $code);
$content = explode('<br />', $code);
}
$lines = [];
if (0 > $srcContext) {
@@ -315,7 +329,7 @@ class HtmlErrorRenderer implements ErrorRendererInterface
{
return preg_replace_callback('/in ("|&quot;)?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) {
return 'in '.$this->formatFile($match[2], $match[3]);
}, $text);
}, $text) ?? $text;
}
private function formatLogMessage(string $message, array $context)
@@ -323,7 +337,7 @@ class HtmlErrorRenderer implements ErrorRendererInterface
if ($context && false !== strpos($message, '{')) {
$replacements = [];
foreach ($context as $key => $val) {
if (is_scalar($val)) {
if (\is_scalar($val)) {
$replacements['{'.$key.'}'] = $val;
}
}

View File

@@ -34,7 +34,7 @@ class SerializerErrorRenderer implements ErrorRendererInterface
* formats not supported by Request::getMimeTypes() should be given as mime types
* @param bool|callable $debug The debugging mode as a boolean or a callable that should return it
*/
public function __construct(SerializerInterface $serializer, $format, ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false)
public function __construct(SerializerInterface $serializer, $format, ?ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false)
{
if (!\is_string($format) && !\is_callable($format)) {
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a string or a callable, "%s" given.', __METHOD__, \gettype($format)));
@@ -55,7 +55,7 @@ class SerializerErrorRenderer implements ErrorRendererInterface
*/
public function render(\Throwable $exception): FlattenException
{
$headers = [];
$headers = ['Vary' => 'Accept'];
$debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($exception);
if ($debug) {
$headers['X-Debug-Exception'] = rawurlencode($exception->getMessage());
@@ -66,19 +66,17 @@ class SerializerErrorRenderer implements ErrorRendererInterface
try {
$format = \is_string($this->format) ? $this->format : ($this->format)($flattenException);
$headers = [
'Content-Type' => Request::getMimeTypes($format)[0] ?? $format,
'Vary' => 'Accept',
];
$headers['Content-Type'] = Request::getMimeTypes($format)[0] ?? $format;
return $flattenException->setAsString($this->serializer->serialize($flattenException, $format, [
$flattenException->setAsString($this->serializer->serialize($flattenException, $format, [
'exception' => $exception,
'debug' => $debug,
]))
->setHeaders($flattenException->getHeaders() + $headers);
]));
} catch (NotEncodableValueException $e) {
return $this->fallbackErrorRenderer->render($exception);
$flattenException = $this->fallbackErrorRenderer->render($exception);
}
return $flattenException->setHeaders($flattenException->getHeaders() + $headers);
}
public static function getPreferredFormat(RequestStack $requestStack): \Closure

View File

@@ -63,7 +63,7 @@ class FlattenException
/**
* @return static
*/
public static function create(\Exception $exception, int $statusCode = null, array $headers = []): self
public static function create(\Exception $exception, ?int $statusCode = null, array $headers = []): self
{
return static::createFromThrowable($exception, $statusCode, $headers);
}
@@ -71,7 +71,7 @@ class FlattenException
/**
* @return static
*/
public static function createFromThrowable(\Throwable $exception, int $statusCode = null, array $headers = []): self
public static function createFromThrowable(\Throwable $exception, ?int $statusCode = null, array $headers = []): self
{
$e = new static();
$e->setMessage($exception->getMessage());

View File

@@ -37,7 +37,7 @@ class TentativeTypes
'DateTime' => [
'__wakeup' => 'void',
'__set_state' => 'DateTime',
'createFromImmutable' => 'DateTime',
'createFromImmutable' => 'static',
'createFromFormat' => 'DateTime|false',
'getLastErrors' => 'array|false',
'format' => 'string',
@@ -72,7 +72,7 @@ class TentativeTypes
'setDate' => 'DateTimeImmutable',
'setISODate' => 'DateTimeImmutable',
'setTimestamp' => 'DateTimeImmutable',
'createFromMutable' => 'DateTimeImmutable',
'createFromMutable' => 'static',
],
'DateTimeZone' => [
'getName' => 'string',
@@ -753,6 +753,7 @@ class TentativeTypes
'isVariadic' => 'bool',
'isStatic' => 'bool',
'getClosureThis' => '?object',
'getClosureCalledClass' => '?ReflectionClass',
'getClosureScopeClass' => '?ReflectionClass',
'getDocComment' => 'string|false',
'getEndLine' => 'int|false',
@@ -1102,6 +1103,8 @@ class TentativeTypes
'isDot' => 'bool',
'rewind' => 'void',
'valid' => 'bool',
'key' => 'mixed',
'current' => 'mixed',
'next' => 'void',
'seek' => 'void',
],
@@ -1148,8 +1151,8 @@ class TentativeTypes
'getFlags' => 'int',
'setMaxLineLen' => 'void',
'getMaxLineLen' => 'int',
'hasChildren' => 'bool',
'getChildren' => '?RecursiveIterator',
'hasChildren' => 'false',
'getChildren' => 'null',
'seek' => 'void',
'getCurrentLine' => 'string',
],
@@ -1238,7 +1241,7 @@ class TentativeTypes
'current' => 'never',
'next' => 'void',
'key' => 'never',
'valid' => 'bool',
'valid' => 'false',
'rewind' => 'void',
],
'CallbackFilterIterator' => [

View File

@@ -1,4 +1,4 @@
Copyright (c) 2019-2022 Fabien Potencier
Copyright (c) 2019-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,297 +1,285 @@
/* This file is based on WebProfilerBundle/Resources/views/Profiler/base_js.html.twig.
If you make any change in this file, verify the same change is needed in the other file. */
/*<![CDATA[*/
if (typeof Sfjs === 'undefined') {
Sfjs = (function() {
"use strict";
(function() {
"use strict";
if ('classList' in document.documentElement) {
var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); };
var removeClass = function(el, cssClass) { el.classList.remove(cssClass); };
var addClass = function(el, cssClass) { el.classList.add(cssClass); };
var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); };
} else {
var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); };
var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); };
var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } };
var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); };
if ('classList' in document.documentElement) {
var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); };
var removeClass = function(el, cssClass) { el.classList.remove(cssClass); };
var addClass = function(el, cssClass) { el.classList.add(cssClass); };
var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); };
} else {
var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); };
var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); };
var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } };
var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); };
}
var addEventListener;
var el = document.createElement('div');
if (!('addEventListener' in el)) {
addEventListener = function (element, eventName, callback) {
element.attachEvent('on' + eventName, callback);
};
} else {
addEventListener = function (element, eventName, callback) {
element.addEventListener(eventName, callback, false);
};
}
if (navigator.clipboard) {
document.querySelectorAll('[data-clipboard-text]').forEach(function(element) {
removeClass(element, 'hidden');
element.addEventListener('click', function() {
navigator.clipboard.writeText(element.getAttribute('data-clipboard-text'));
})
});
}
(function createTabs() {
var tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])');
/* create the tab navigation for each group of tabs */
for (var i = 0; i < tabGroups.length; i++) {
var tabs = tabGroups[i].querySelectorAll(':scope > .tab');
var tabNavigation = document.createElement('ul');
tabNavigation.className = 'tab-navigation';
var selectedTabId = 'tab-' + i + '-0'; /* select the first tab by default */
for (var j = 0; j < tabs.length; j++) {
var tabId = 'tab-' + i + '-' + j;
var tabTitle = tabs[j].querySelector('.tab-title').innerHTML;
var tabNavigationItem = document.createElement('li');
tabNavigationItem.setAttribute('data-tab-id', tabId);
if (hasClass(tabs[j], 'active')) { selectedTabId = tabId; }
if (hasClass(tabs[j], 'disabled')) { addClass(tabNavigationItem, 'disabled'); }
tabNavigationItem.innerHTML = tabTitle;
tabNavigation.appendChild(tabNavigationItem);
var tabContent = tabs[j].querySelector('.tab-content');
tabContent.parentElement.setAttribute('id', tabId);
}
tabGroups[i].insertBefore(tabNavigation, tabGroups[i].firstChild);
addClass(document.querySelector('[data-tab-id="' + selectedTabId + '"]'), 'active');
}
var addEventListener;
/* display the active tab and add the 'click' event listeners */
for (i = 0; i < tabGroups.length; i++) {
tabNavigation = tabGroups[i].querySelectorAll(':scope >.tab-navigation li');
var el = document.createElement('div');
if (!('addEventListener' in el)) {
addEventListener = function (element, eventName, callback) {
element.attachEvent('on' + eventName, callback);
};
} else {
addEventListener = function (element, eventName, callback) {
element.addEventListener(eventName, callback, false);
};
}
for (j = 0; j < tabNavigation.length; j++) {
tabId = tabNavigation[j].getAttribute('data-tab-id');
document.getElementById(tabId).querySelector('.tab-title').className = 'hidden';
if (navigator.clipboard) {
document.querySelectorAll('[data-clipboard-text]').forEach(function(element) {
removeClass(element, 'hidden');
element.addEventListener('click', function() {
navigator.clipboard.writeText(element.getAttribute('data-clipboard-text'));
})
});
}
return {
addEventListener: addEventListener,
createTabs: function() {
var tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])');
/* create the tab navigation for each group of tabs */
for (var i = 0; i < tabGroups.length; i++) {
var tabs = tabGroups[i].querySelectorAll(':scope > .tab');
var tabNavigation = document.createElement('ul');
tabNavigation.className = 'tab-navigation';
var selectedTabId = 'tab-' + i + '-0'; /* select the first tab by default */
for (var j = 0; j < tabs.length; j++) {
var tabId = 'tab-' + i + '-' + j;
var tabTitle = tabs[j].querySelector('.tab-title').innerHTML;
var tabNavigationItem = document.createElement('li');
tabNavigationItem.setAttribute('data-tab-id', tabId);
if (hasClass(tabs[j], 'active')) { selectedTabId = tabId; }
if (hasClass(tabs[j], 'disabled')) { addClass(tabNavigationItem, 'disabled'); }
tabNavigationItem.innerHTML = tabTitle;
tabNavigation.appendChild(tabNavigationItem);
var tabContent = tabs[j].querySelector('.tab-content');
tabContent.parentElement.setAttribute('id', tabId);
}
tabGroups[i].insertBefore(tabNavigation, tabGroups[i].firstChild);
addClass(document.querySelector('[data-tab-id="' + selectedTabId + '"]'), 'active');
if (hasClass(tabNavigation[j], 'active')) {
document.getElementById(tabId).className = 'block';
} else {
document.getElementById(tabId).className = 'hidden';
}
/* display the active tab and add the 'click' event listeners */
for (i = 0; i < tabGroups.length; i++) {
tabNavigation = tabGroups[i].querySelectorAll(':scope >.tab-navigation li');
tabNavigation[j].addEventListener('click', function(e) {
var activeTab = e.target || e.srcElement;
for (j = 0; j < tabNavigation.length; j++) {
tabId = tabNavigation[j].getAttribute('data-tab-id');
document.getElementById(tabId).querySelector('.tab-title').className = 'hidden';
if (hasClass(tabNavigation[j], 'active')) {
document.getElementById(tabId).className = 'block';
} else {
document.getElementById(tabId).className = 'hidden';
}
tabNavigation[j].addEventListener('click', function(e) {
var activeTab = e.target || e.srcElement;
/* needed because when the tab contains HTML contents, user can click */
/* on any of those elements instead of their parent '<li>' element */
while (activeTab.tagName.toLowerCase() !== 'li') {
activeTab = activeTab.parentNode;
}
/* get the full list of tabs through the parent of the active tab element */
var tabNavigation = activeTab.parentNode.children;
for (var k = 0; k < tabNavigation.length; k++) {
var tabId = tabNavigation[k].getAttribute('data-tab-id');
document.getElementById(tabId).className = 'hidden';
removeClass(tabNavigation[k], 'active');
}
addClass(activeTab, 'active');
var activeTabId = activeTab.getAttribute('data-tab-id');
document.getElementById(activeTabId).className = 'block';
});
/* needed because when the tab contains HTML contents, user can click */
/* on any of those elements instead of their parent '<li>' element */
while (activeTab.tagName.toLowerCase() !== 'li') {
activeTab = activeTab.parentNode;
}
tabGroups[i].setAttribute('data-processed', 'true');
}
},
createToggles: function() {
var toggles = document.querySelectorAll('.sf-toggle:not([data-processed=true])');
for (var i = 0; i < toggles.length; i++) {
var elementSelector = toggles[i].getAttribute('data-toggle-selector');
var element = document.querySelector(elementSelector);
addClass(element, 'sf-toggle-content');
if (toggles[i].hasAttribute('data-toggle-initial') && toggles[i].getAttribute('data-toggle-initial') == 'display') {
addClass(toggles[i], 'sf-toggle-on');
addClass(element, 'sf-toggle-visible');
} else {
addClass(toggles[i], 'sf-toggle-off');
addClass(element, 'sf-toggle-hidden');
/* get the full list of tabs through the parent of the active tab element */
var tabNavigation = activeTab.parentNode.children;
for (var k = 0; k < tabNavigation.length; k++) {
var tabId = tabNavigation[k].getAttribute('data-tab-id');
document.getElementById(tabId).className = 'hidden';
removeClass(tabNavigation[k], 'active');
}
addEventListener(toggles[i], 'click', function(e) {
e.preventDefault();
if ('' !== window.getSelection().toString()) {
/* Don't do anything on text selection */
return;
}
var toggle = e.target || e.srcElement;
/* needed because when the toggle contains HTML contents, user can click */
/* on any of those elements instead of their parent '.sf-toggle' element */
while (!hasClass(toggle, 'sf-toggle')) {
toggle = toggle.parentNode;
}
var element = document.querySelector(toggle.getAttribute('data-toggle-selector'));
toggleClass(toggle, 'sf-toggle-on');
toggleClass(toggle, 'sf-toggle-off');
toggleClass(element, 'sf-toggle-hidden');
toggleClass(element, 'sf-toggle-visible');
/* the toggle doesn't change its contents when clicking on it */
if (!toggle.hasAttribute('data-toggle-alt-content')) {
return;
}
if (!toggle.hasAttribute('data-toggle-original-content')) {
toggle.setAttribute('data-toggle-original-content', toggle.innerHTML);
}
var currentContent = toggle.innerHTML;
var originalContent = toggle.getAttribute('data-toggle-original-content');
var altContent = toggle.getAttribute('data-toggle-alt-content');
toggle.innerHTML = currentContent !== altContent ? altContent : originalContent;
});
/* Prevents from disallowing clicks on links inside toggles */
var toggleLinks = toggles[i].querySelectorAll('a');
for (var j = 0; j < toggleLinks.length; j++) {
addEventListener(toggleLinks[j], 'click', function(e) {
e.stopPropagation();
});
}
/* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */
var copyToClipboardElements = toggles[i].querySelectorAll('span[data-clipboard-text]');
for (var k = 0; k < copyToClipboardElements.length; k++) {
addEventListener(copyToClipboardElements[k], 'click', function(e) {
e.stopPropagation();
});
}
toggles[i].setAttribute('data-processed', 'true');
}
},
createFilters: function() {
document.querySelectorAll('[data-filters] [data-filter]').forEach(function (filter) {
var filters = filter.closest('[data-filters]'),
type = 'choice',
name = filter.dataset.filter,
ucName = name.charAt(0).toUpperCase()+name.slice(1),
list = document.createElement('ul'),
values = filters.dataset['filter'+ucName] || filters.querySelectorAll('[data-filter-'+name+']'),
labels = {},
defaults = null,
indexed = {},
processed = {};
if (typeof values === 'string') {
type = 'level';
labels = values.split(',');
values = values.toLowerCase().split(',');
defaults = values.length - 1;
}
addClass(list, 'filter-list');
addClass(list, 'filter-list-'+type);
values.forEach(function (value, i) {
if (value instanceof HTMLElement) {
value = value.dataset['filter'+ucName];
}
if (value in processed) {
return;
}
var option = document.createElement('li'),
label = i in labels ? labels[i] : value,
active = false,
matches;
if ('' === label) {
option.innerHTML = '<em>(none)</em>';
} else {
option.innerText = label;
}
option.dataset.filter = value;
option.setAttribute('title', 1 === (matches = filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').length) ? 'Matches 1 row' : 'Matches '+matches+' rows');
indexed[value] = i;
list.appendChild(option);
addEventListener(option, 'click', function () {
if ('choice' === type) {
filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
if (option.dataset.filter === row.dataset['filter'+ucName]) {
toggleClass(row, 'filter-hidden-'+name);
}
});
toggleClass(option, 'active');
} else if ('level' === type) {
if (i === this.parentNode.querySelectorAll('.active').length - 1) {
return;
}
this.parentNode.querySelectorAll('li').forEach(function (currentOption, j) {
if (j <= i) {
addClass(currentOption, 'active');
if (i === j) {
addClass(currentOption, 'last-active');
} else {
removeClass(currentOption, 'last-active');
}
} else {
removeClass(currentOption, 'active');
removeClass(currentOption, 'last-active');
}
});
filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
if (i < indexed[row.dataset['filter'+ucName]]) {
addClass(row, 'filter-hidden-'+name);
} else {
removeClass(row, 'filter-hidden-'+name);
}
});
}
});
if ('choice' === type) {
active = null === defaults || 0 <= defaults.indexOf(value);
} else if ('level' === type) {
active = i <= defaults;
if (active && i === defaults) {
addClass(option, 'last-active');
}
}
if (active) {
addClass(option, 'active');
} else {
filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').forEach(function (row) {
toggleClass(row, 'filter-hidden-'+name);
});
}
processed[value] = true;
});
if (1 < list.childNodes.length) {
filter.appendChild(list);
filter.dataset.filtered = '';
}
addClass(activeTab, 'active');
var activeTabId = activeTab.getAttribute('data-tab-id');
document.getElementById(activeTabId).className = 'block';
});
}
};
tabGroups[i].setAttribute('data-processed', 'true');
}
})();
Sfjs.addEventListener(document, 'DOMContentLoaded', function() {
Sfjs.createTabs();
Sfjs.createToggles();
Sfjs.createFilters();
});
}
(function createToggles() {
var toggles = document.querySelectorAll('.sf-toggle:not([data-processed=true])');
for (var i = 0; i < toggles.length; i++) {
var elementSelector = toggles[i].getAttribute('data-toggle-selector');
var element = document.querySelector(elementSelector);
addClass(element, 'sf-toggle-content');
if (toggles[i].hasAttribute('data-toggle-initial') && toggles[i].getAttribute('data-toggle-initial') == 'display') {
addClass(toggles[i], 'sf-toggle-on');
addClass(element, 'sf-toggle-visible');
} else {
addClass(toggles[i], 'sf-toggle-off');
addClass(element, 'sf-toggle-hidden');
}
addEventListener(toggles[i], 'click', function(e) {
e.preventDefault();
if ('' !== window.getSelection().toString()) {
/* Don't do anything on text selection */
return;
}
var toggle = e.target || e.srcElement;
/* needed because when the toggle contains HTML contents, user can click */
/* on any of those elements instead of their parent '.sf-toggle' element */
while (!hasClass(toggle, 'sf-toggle')) {
toggle = toggle.parentNode;
}
var element = document.querySelector(toggle.getAttribute('data-toggle-selector'));
toggleClass(toggle, 'sf-toggle-on');
toggleClass(toggle, 'sf-toggle-off');
toggleClass(element, 'sf-toggle-hidden');
toggleClass(element, 'sf-toggle-visible');
/* the toggle doesn't change its contents when clicking on it */
if (!toggle.hasAttribute('data-toggle-alt-content')) {
return;
}
if (!toggle.hasAttribute('data-toggle-original-content')) {
toggle.setAttribute('data-toggle-original-content', toggle.innerHTML);
}
var currentContent = toggle.innerHTML;
var originalContent = toggle.getAttribute('data-toggle-original-content');
var altContent = toggle.getAttribute('data-toggle-alt-content');
toggle.innerHTML = currentContent !== altContent ? altContent : originalContent;
});
/* Prevents from disallowing clicks on links inside toggles */
var toggleLinks = toggles[i].querySelectorAll('a');
for (var j = 0; j < toggleLinks.length; j++) {
addEventListener(toggleLinks[j], 'click', function(e) {
e.stopPropagation();
});
}
/* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */
var copyToClipboardElements = toggles[i].querySelectorAll('span[data-clipboard-text]');
for (var k = 0; k < copyToClipboardElements.length; k++) {
addEventListener(copyToClipboardElements[k], 'click', function(e) {
e.stopPropagation();
});
}
toggles[i].setAttribute('data-processed', 'true');
}
})();
(function createFilters() {
document.querySelectorAll('[data-filters] [data-filter]').forEach(function (filter) {
var filters = filter.closest('[data-filters]'),
type = 'choice',
name = filter.dataset.filter,
ucName = name.charAt(0).toUpperCase()+name.slice(1),
list = document.createElement('ul'),
values = filters.dataset['filter'+ucName] || filters.querySelectorAll('[data-filter-'+name+']'),
labels = {},
defaults = null,
indexed = {},
processed = {};
if (typeof values === 'string') {
type = 'level';
labels = values.split(',');
values = values.toLowerCase().split(',');
defaults = values.length - 1;
}
addClass(list, 'filter-list');
addClass(list, 'filter-list-'+type);
values.forEach(function (value, i) {
if (value instanceof HTMLElement) {
value = value.dataset['filter'+ucName];
}
if (value in processed) {
return;
}
var option = document.createElement('li'),
label = i in labels ? labels[i] : value,
active = false,
matches;
if ('' === label) {
option.innerHTML = '<em>(none)</em>';
} else {
option.innerText = label;
}
option.dataset.filter = value;
option.setAttribute('title', 1 === (matches = filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').length) ? 'Matches 1 row' : 'Matches '+matches+' rows');
indexed[value] = i;
list.appendChild(option);
addEventListener(option, 'click', function () {
if ('choice' === type) {
filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
if (option.dataset.filter === row.dataset['filter'+ucName]) {
toggleClass(row, 'filter-hidden-'+name);
}
});
toggleClass(option, 'active');
} else if ('level' === type) {
if (i === this.parentNode.querySelectorAll('.active').length - 1) {
return;
}
this.parentNode.querySelectorAll('li').forEach(function (currentOption, j) {
if (j <= i) {
addClass(currentOption, 'active');
if (i === j) {
addClass(currentOption, 'last-active');
} else {
removeClass(currentOption, 'last-active');
}
} else {
removeClass(currentOption, 'active');
removeClass(currentOption, 'last-active');
}
});
filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
if (i < indexed[row.dataset['filter'+ucName]]) {
addClass(row, 'filter-hidden-'+name);
} else {
removeClass(row, 'filter-hidden-'+name);
}
});
}
});
if ('choice' === type) {
active = null === defaults || 0 <= defaults.indexOf(value);
} else if ('level' === type) {
active = i <= defaults;
if (active && i === defaults) {
addClass(option, 'last-active');
}
}
if (active) {
addClass(option, 'active');
} else {
filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').forEach(function (row) {
toggleClass(row, 'filter-hidden-'+name);
});
}
processed[value] = true;
});
if (1 < list.childNodes.length) {
filter.appendChild(list);
filter.dataset.filtered = '';
}
});
})();
})();
/*]]>*/

View File

@@ -10,6 +10,10 @@
* file that was distributed with this source code.
*/
if ('cli' !== \PHP_SAPI) {
throw new Exception('This script must be run from the command line.');
}
// Run from the root of the php-src repository, this script generates
// a table with all the methods that have a tentative return type.
//

4
vendor/symfony/error-handler/Resources/bin/patch-type-declarations vendored Executable file → Normal file
View File

@@ -10,6 +10,10 @@
* file that was distributed with this source code.
*/
if ('cli' !== \PHP_SAPI) {
throw new Exception('This script must be run from the command line.');
}
if (\in_array('-h', $argv) || \in_array('--help', $argv)) {
echo implode(PHP_EOL, [
' Patches type declarations based on "@return" PHPDoc and triggers deprecations for',

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="<?= $this->charset; ?>" />
<meta name="robots" content="noindex,nofollow,noarchive" />

View File

@@ -12,7 +12,7 @@
$class = substr($exception['class'], $separator);
?>
<?php if ('' === $class) { ?>
</br>
<br>
<?php } else { ?>
<h3 class="trace-class">
<?php if ('' !== $namespace) { ?>

View File

@@ -73,7 +73,7 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
{
if (isset($this->wrappedListeners[$eventName])) {
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
if ($wrappedListener->getWrappedListener() === $listener) {
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
$listener = $wrappedListener;
unset($this->wrappedListeners[$eventName][$index]);
break;
@@ -108,8 +108,8 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
// we might have wrapped listeners for the event (if called while dispatching)
// in that case get the priority by wrapper
if (isset($this->wrappedListeners[$eventName])) {
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
if ($wrappedListener->getWrappedListener() === $listener) {
foreach ($this->wrappedListeners[$eventName] as $wrappedListener) {
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
}
}

View File

@@ -47,7 +47,7 @@ final class WrappedListener
$r = new \ReflectionFunction($listener);
if (str_contains($r->name, '{closure}')) {
$this->pretty = $this->name = 'closure';
} elseif ($class = $r->getClosureScopeClass()) {
} elseif ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
$this->name = $class->name;
$this->pretty = $this->name.'::'.$r->name;
} else {

View File

@@ -108,7 +108,7 @@ class EventDispatcher implements EventDispatcherInterface
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener) {
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
return $priority;
}
}
@@ -164,7 +164,7 @@ class EventDispatcher implements EventDispatcherInterface
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener) {
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-2023 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -19,7 +19,7 @@ class Comparator
private $target;
private $operator = '==';
public function __construct(string $target = null, string $operator = '==')
public function __construct(?string $target = null, string $operator = '==')
{
if (null === $target) {
trigger_deprecation('symfony/finder', '5.4', 'Constructing a "%s" without setting "$target" is deprecated.', __CLASS__);

View File

@@ -35,7 +35,7 @@ namespace Symfony\Component\Finder\Comparator;
class NumberComparator extends Comparator
{
/**
* @param string|int $test A comparison string or an integer
* @param string|null $test A comparison string or null
*
* @throws \InvalidArgumentException If the test is not understood
*/

View File

@@ -164,8 +164,8 @@ class Finder implements \IteratorAggregate, \Countable
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('/\.php$/')
* $finder->name('*.php') // same as above, without dot files
* $finder->name('test.php')
* $finder->name(['test.py', 'test.php'])
*

View File

@@ -17,6 +17,7 @@ namespace Symfony\Component\Finder\Iterator;
* @author Fabien Potencier <fabien@symfony.com>
*
* @extends \FilterIterator<string, \SplFileInfo>
*
* @implements \RecursiveIterator<string, \SplFileInfo>
*/
class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator

View File

@@ -29,7 +29,7 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
/**
* @var bool
*/
private $rewindable;
private $ignoreFirstRewind = true;
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
private $rootPath;
@@ -118,7 +118,6 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
// performance optimization to avoid redoing the same work in all children
$children->rewindable = &$this->rewindable;
$children->rootPath = $this->rootPath;
}
@@ -129,40 +128,30 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
}
/**
* Do nothing for non rewindable stream.
*
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
$this->ignoreFirstRewind = false;
parent::next();
}
/**
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
if (false === $this->isRewindable()) {
// some streams like FTP are not rewindable, ignore the first rewind after creation,
// as newly created DirectoryIterator does not need to be rewound
if ($this->ignoreFirstRewind) {
$this->ignoreFirstRewind = false;
return;
}
parent::rewind();
}
/**
* Checks if the stream is rewindable.
*
* @return bool
*/
public function isRewindable()
{
if (null !== $this->rewindable) {
return $this->rewindable;
}
if (false !== $stream = @opendir($this->getPath())) {
$infos = stream_get_meta_data($stream);
closedir($stream);
if ($infos['seekable']) {
return $this->rewindable = true;
}
}
return $this->rewindable = false;
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -34,6 +34,7 @@ class BinaryFileResponse extends Response
protected $offset = 0;
protected $maxlen = -1;
protected $deleteFileAfterSend = false;
protected $chunkSize = 16 * 1024;
/**
* @param \SplFileInfo|string $file The file to stream
@@ -44,7 +45,7 @@ class BinaryFileResponse extends Response
* @param bool $autoEtag Whether the ETag header should be automatically set
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
*/
public function __construct($file, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
public function __construct($file, int $status = 200, array $headers = [], bool $public = true, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
{
parent::__construct(null, $status, $headers);
@@ -68,7 +69,7 @@ class BinaryFileResponse extends Response
*
* @deprecated since Symfony 5.2, use __construct() instead.
*/
public static function create($file = null, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
public static function create($file = null, int $status = 200, array $headers = [], bool $public = true, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
{
trigger_deprecation('symfony/http-foundation', '5.2', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class);
@@ -84,7 +85,7 @@ class BinaryFileResponse extends Response
*
* @throws FileException
*/
public function setFile($file, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
public function setFile($file, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
{
if (!$file instanceof File) {
if ($file instanceof \SplFileInfo) {
@@ -125,6 +126,22 @@ class BinaryFileResponse extends Response
return $this->file;
}
/**
* Sets the response stream chunk size.
*
* @return $this
*/
public function setChunkSize(int $chunkSize): self
{
if ($chunkSize < 1 || $chunkSize > \PHP_INT_MAX) {
throw new \LogicException('The chunk size of a BinaryFileResponse cannot be less than 1 or greater than PHP_INT_MAX.');
}
$this->chunkSize = $chunkSize;
return $this;
}
/**
* Automatically sets the Last-Modified header according the file modification date.
*
@@ -189,15 +206,19 @@ class BinaryFileResponse extends Response
*/
public function prepare(Request $request)
{
if ($this->isInformational() || $this->isEmpty()) {
parent::prepare($request);
$this->maxlen = 0;
return $this;
}
if (!$this->headers->has('Content-Type')) {
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
}
if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
$this->setProtocolVersion('1.1');
}
$this->ensureIEOverSSLCompatibility($request);
parent::prepare($request);
$this->offset = 0;
$this->maxlen = -1;
@@ -205,6 +226,7 @@ class BinaryFileResponse extends Response
if (false === $fileSize = $this->file->getSize()) {
return $this;
}
$this->headers->remove('Transfer-Encoding');
$this->headers->set('Content-Length', $fileSize);
if (!$this->headers->has('Accept-Ranges')) {
@@ -245,7 +267,7 @@ class BinaryFileResponse extends Response
$range = $request->headers->get('Range');
if (str_starts_with($range, 'bytes=')) {
[$start, $end] = explode('-', substr($range, 6), 2) + [0];
[$start, $end] = explode('-', substr($range, 6), 2) + [1 => 0];
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
@@ -274,6 +296,10 @@ class BinaryFileResponse extends Response
}
}
if ($request->isMethod('HEAD')) {
$this->maxlen = 0;
}
return $this;
}
@@ -295,24 +321,49 @@ class BinaryFileResponse extends Response
*/
public function sendContent()
{
if (!$this->isSuccessful()) {
return parent::sendContent();
}
try {
if (!$this->isSuccessful()) {
return parent::sendContent();
}
if (0 === $this->maxlen) {
return $this;
}
if (0 === $this->maxlen) {
return $this;
}
$out = fopen('php://output', 'w');
$file = fopen($this->file->getPathname(), 'r');
$out = fopen('php://output', 'w');
$file = fopen($this->file->getPathname(), 'r');
stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
ignore_user_abort(true);
fclose($out);
fclose($file);
if (0 !== $this->offset) {
fseek($file, $this->offset);
}
if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
unlink($this->file->getPathname());
$length = $this->maxlen;
while ($length && !feof($file)) {
$read = $length > $this->chunkSize || 0 > $length ? $this->chunkSize : $length;
if (false === $data = fread($file, $read)) {
break;
}
while ('' !== $data) {
$read = fwrite($out, $data);
if (false === $read || connection_aborted()) {
break 2;
}
if (0 < $length) {
$length -= $read;
}
$data = substr($data, $read);
}
}
fclose($out);
fclose($file);
} finally {
if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
unlink($this->file->getPathname());
}
}
return $this;

View File

@@ -71,7 +71,7 @@ class Cookie
return new static($name, $value, $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
}
public static function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self
public static function create(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self
{
return new self($name, $value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite);
}
@@ -80,7 +80,7 @@ class Cookie
* @param string $name The name of the cookie
* @param string|null $value The value of the cookie
* @param int|string|\DateTimeInterface $expire The time the cookie expires
* @param string $path The path on the server in which the cookie will be available on
* @param string|null $path The path on the server in which the cookie will be available on
* @param string|null $domain The domain that the cookie is available to
* @param bool|null $secure Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
@@ -89,7 +89,7 @@ class Cookie
*
* @throws \InvalidArgumentException
*/
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = 'lax')
public function __construct(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = 'lax')
{
// from PHP source code
if ($raw && false !== strpbrk($name, self::RESERVED_CHARS_LIST)) {

View File

@@ -12,7 +12,7 @@
namespace Symfony\Component\HttpFoundation\Exception;
/**
* Raised when a session does not exists. This happens in the following cases:
* Raised when a session does not exist. This happens in the following cases:
* - the session is not enabled
* - attempt to read a session outside a request context (ie. cli script).
*
@@ -20,7 +20,7 @@ namespace Symfony\Component\HttpFoundation\Exception;
*/
class SessionNotFoundException extends \LogicException implements RequestExceptionInterface
{
public function __construct(string $message = 'There is currently no session available.', int $code = 0, \Throwable $previous = null)
public function __construct(string $message = 'There is currently no session available.', int $code = 0, ?\Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}

View File

@@ -88,7 +88,7 @@ class File extends \SplFileInfo
*
* @throws FileException if the target file could not be created
*/
public function move(string $directory, string $name = null)
public function move(string $directory, ?string $name = null)
{
$target = $this->getTargetFile($directory, $name);
@@ -121,7 +121,7 @@ class File extends \SplFileInfo
/**
* @return self
*/
protected function getTargetFile(string $directory, string $name = null)
protected function getTargetFile(string $directory, ?string $name = null)
{
if (!is_dir($directory)) {
if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {

View File

@@ -60,7 +60,7 @@ class UploadedFile extends File
* @throws FileException If file_uploads is disabled
* @throws FileNotFoundException If the file does not exist
*/
public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false)
public function __construct(string $path, string $originalName, ?string $mimeType = null, ?int $error = null, bool $test = false)
{
$this->originalName = $this->getName($originalName);
$this->mimeType = $mimeType ?: 'application/octet-stream';
@@ -74,7 +74,7 @@ class UploadedFile extends File
* Returns the original file name.
*
* It is extracted from the request from which the file has been uploaded.
* Then it should not be considered as a safe value.
* This should not be considered as a safe value to use for a file name on your servers.
*
* @return string
*/
@@ -87,7 +87,7 @@ class UploadedFile extends File
* Returns the original file extension.
*
* It is extracted from the original file name that was uploaded.
* Then it should not be considered as a safe value.
* This should not be considered as a safe value to use for a file name on your servers.
*
* @return string
*/
@@ -172,7 +172,7 @@ class UploadedFile extends File
*
* @throws FileException if, for any reason, the file could not have been moved
*/
public function move(string $directory, string $name = null)
public function move(string $directory, ?string $name = null)
{
if ($this->isValid()) {
if ($this->test) {
@@ -223,8 +223,8 @@ class UploadedFile extends File
*/
public static function getMaxFilesize()
{
$sizePostMax = self::parseFilesize(ini_get('post_max_size'));
$sizeUploadMax = self::parseFilesize(ini_get('upload_max_filesize'));
$sizePostMax = self::parseFilesize(\ini_get('post_max_size'));
$sizeUploadMax = self::parseFilesize(\ini_get('upload_max_filesize'));
return min($sizePostMax ?: \PHP_INT_MAX, $sizeUploadMax ?: \PHP_INT_MAX);
}
@@ -253,11 +253,11 @@ class UploadedFile extends File
switch (substr($size, -1)) {
case 't': $max *= 1024;
// no break
// no break
case 'g': $max *= 1024;
// no break
// no break
case 'm': $max *= 1024;
// no break
// no break
case 'k': $max *= 1024;
}

View File

@@ -67,7 +67,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*
* @return array<string, array<int, string|null>>|array<int, string|null>
*/
public function all(string $key = null)
public function all(?string $key = null)
{
if (null !== $key) {
return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? [];
@@ -110,7 +110,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*
* @return string|null
*/
public function get(string $key, string $default = null)
public function get(string $key, ?string $default = null)
{
$headers = $this->all($key);
@@ -197,7 +197,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*
* @throws \RuntimeException When the HTTP header is not parseable
*/
public function getDate(string $key, \DateTime $default = null)
public function getDate(string $key, ?\DateTime $default = null)
{
if (null === $value = $this->get($key)) {
return $default;

View File

@@ -33,17 +33,21 @@ class HeaderUtils
*
* Example:
*
* HeaderUtils::split("da, en-gb;q=0.8", ",;")
* HeaderUtils::split('da, en-gb;q=0.8', ',;')
* // => ['da'], ['en-gb', 'q=0.8']]
*
* @param string $separators List of characters to split on, ordered by
* precedence, e.g. ",", ";=", or ",;="
* precedence, e.g. ',', ';=', or ',;='
*
* @return array Nested array with as many levels as there are characters in
* $separators
*/
public static function split(string $header, string $separators): array
{
if ('' === $separators) {
throw new \InvalidArgumentException('At least one separator must be specified.');
}
$quotedSeparators = preg_quote($separators, '/');
preg_match_all('
@@ -77,8 +81,8 @@ class HeaderUtils
*
* Example:
*
* HeaderUtils::combine([["foo", "abc"], ["bar"]])
* // => ["foo" => "abc", "bar" => true]
* HeaderUtils::combine([['foo', 'abc'], ['bar']])
* // => ['foo' => 'abc', 'bar' => true]
*/
public static function combine(array $parts): array
{
@@ -95,13 +99,13 @@ class HeaderUtils
/**
* Joins an associative array into a string for use in an HTTP header.
*
* The key and value of each entry are joined with "=", and all entries
* The key and value of each entry are joined with '=', and all entries
* are joined with the specified separator and an additional space (for
* readability). Values are quoted if necessary.
*
* Example:
*
* HeaderUtils::toString(["foo" => "abc", "bar" => true, "baz" => "a b c"], ",")
* HeaderUtils::toString(['foo' => 'abc', 'bar' => true, 'baz' => 'a b c'], ',')
* // => 'foo=abc, bar, baz="a b c"'
*/
public static function toString(array $assoc, string $separator): string
@@ -138,7 +142,7 @@ class HeaderUtils
* Decodes a quoted string.
*
* If passed an unquoted string that matches the "token" construct (as
* defined in the HTTP specification), it is passed through verbatimly.
* defined in the HTTP specification), it is passed through verbatim.
*/
public static function unquote(string $s): string
{
@@ -252,40 +256,37 @@ class HeaderUtils
private static function groupParts(array $matches, string $separators, bool $first = true): array
{
$separator = $separators[0];
$partSeparators = substr($separators, 1);
$separators = substr($separators, 1) ?: '';
$i = 0;
if ('' === $separators && !$first) {
$parts = [''];
foreach ($matches as $match) {
if (!$i && isset($match['separator'])) {
$i = 1;
$parts[1] = '';
} else {
$parts[$i] .= self::unquote($match[0]);
}
}
return $parts;
}
$parts = [];
$partMatches = [];
$previousMatchWasSeparator = false;
foreach ($matches as $match) {
if (!$first && $previousMatchWasSeparator && isset($match['separator']) && $match['separator'] === $separator) {
$previousMatchWasSeparator = true;
$partMatches[$i][] = $match;
} elseif (isset($match['separator']) && $match['separator'] === $separator) {
$previousMatchWasSeparator = true;
if (($match['separator'] ?? null) === $separator) {
++$i;
} else {
$previousMatchWasSeparator = false;
$partMatches[$i][] = $match;
}
}
$parts = [];
if ($partSeparators) {
foreach ($partMatches as $matches) {
$parts[] = self::groupParts($matches, $partSeparators, false);
}
} else {
foreach ($partMatches as $matches) {
$parts[] = self::unquote($matches[0][0]);
}
if (!$first && 2 < \count($parts)) {
$parts = [
$parts[0],
implode($separator, \array_slice($parts, 1)),
];
}
foreach ($partMatches as $matches) {
$parts[] = '' === $separators ? self::unquote($matches[0][0]) : self::groupParts($matches, $separators, false);
}
return $parts;

View File

@@ -29,14 +29,14 @@ final class InputBag extends ParameterBag
*/
public function get(string $key, $default = null)
{
if (null !== $default && !is_scalar($default) && !(\is_object($default) && method_exists($default, '__toString'))) {
if (null !== $default && !\is_scalar($default) && !(\is_object($default) && method_exists($default, '__toString'))) {
trigger_deprecation('symfony/http-foundation', '5.1', 'Passing a non-scalar value as 2nd argument to "%s()" is deprecated, pass a scalar or null instead.', __METHOD__);
}
$value = parent::get($key, $this);
if (null !== $value && $this !== $value && !is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
trigger_deprecation('symfony/http-foundation', '5.1', 'Retrieving a non-string value from "%s()" is deprecated, and will throw a "%s" exception in Symfony 6.0, use "%s::all($key)" instead.', __METHOD__, BadRequestException::class, __CLASS__);
if (null !== $value && $this !== $value && !\is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
trigger_deprecation('symfony/http-foundation', '5.1', 'Retrieving a non-scalar value from "%s()" is deprecated, and will throw a "%s" exception in Symfony 6.0, use "%s::all($key)" instead.', __METHOD__, BadRequestException::class, __CLASS__);
}
return $this === $value ? $default : $value;
@@ -45,7 +45,7 @@ final class InputBag extends ParameterBag
/**
* {@inheritdoc}
*/
public function all(string $key = null): array
public function all(?string $key = null): array
{
return parent::all($key);
}
@@ -76,7 +76,7 @@ final class InputBag extends ParameterBag
*/
public function set(string $key, $value)
{
if (null !== $value && !is_scalar($value) && !\is_array($value) && !method_exists($value, '__toString')) {
if (null !== $value && !\is_scalar($value) && !\is_array($value) && !method_exists($value, '__toString')) {
trigger_deprecation('symfony/http-foundation', '5.1', 'Passing "%s" as a 2nd Argument to "%s()" is deprecated, pass a scalar, array, or null instead.', get_debug_type($value), __METHOD__);
}

View File

@@ -73,7 +73,7 @@ class IpUtils
return false;
}
$cacheKey = $requestIp.'-'.$ip;
$cacheKey = $requestIp.'-'.$ip.'-v4';
if (isset(self::$checkedIps[$cacheKey])) {
return self::$checkedIps[$cacheKey];
}
@@ -86,7 +86,7 @@ class IpUtils
[$address, $netmask] = explode('/', $ip, 2);
if ('0' === $netmask) {
return self::$checkedIps[$cacheKey] = filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
return self::$checkedIps[$cacheKey] = false !== filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
}
if ($netmask < 0 || $netmask > 32) {
@@ -126,7 +126,7 @@ class IpUtils
return false;
}
$cacheKey = $requestIp.'-'.$ip;
$cacheKey = $requestIp.'-'.$ip.'-v6';
if (isset(self::$checkedIps[$cacheKey])) {
return self::$checkedIps[$cacheKey];
}
@@ -135,9 +135,18 @@ class IpUtils
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
}
// Check to see if we were given a IP4 $requestIp or $ip by mistake
if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
return self::$checkedIps[$cacheKey] = false;
}
if (str_contains($ip, '/')) {
[$address, $netmask] = explode('/', $ip, 2);
if (!filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
return self::$checkedIps[$cacheKey] = false;
}
if ('0' === $netmask) {
return (bool) unpack('n*', @inet_pton($address));
}
@@ -146,6 +155,10 @@ class IpUtils
return self::$checkedIps[$cacheKey] = false;
}
} else {
if (!filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
return self::$checkedIps[$cacheKey] = false;
}
$address = $ip;
$netmask = 128;
}

View File

@@ -105,7 +105,7 @@ class JsonResponse extends Response
*
* @throws \InvalidArgumentException When the callback name is not valid
*/
public function setCallback(string $callback = null)
public function setCallback(?string $callback = null)
{
if (null !== $callback) {
// partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -39,7 +39,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @return array
*/
public function all(/*string $key = null*/)
public function all(/* ?string $key = null */)
{
$key = \func_num_args() > 0 ? func_get_arg(0) : null;

View File

@@ -35,9 +35,7 @@ abstract class AbstractRequestRateLimiter implements RequestRateLimiterInterface
foreach ($limiters as $limiter) {
$rateLimit = $limiter->consume(1);
if (null === $minimalRateLimit || $rateLimit->getRemainingTokens() < $minimalRateLimit->getRemainingTokens()) {
$minimalRateLimit = $rateLimit;
}
$minimalRateLimit = $minimalRateLimit ? self::getMinimalRateLimit($minimalRateLimit, $rateLimit) : $rateLimit;
}
return $minimalRateLimit;
@@ -54,4 +52,20 @@ abstract class AbstractRequestRateLimiter implements RequestRateLimiterInterface
* @return LimiterInterface[] a set of limiters using keys extracted from the request
*/
abstract protected function getLimiters(Request $request): array;
private static function getMinimalRateLimit(RateLimit $first, RateLimit $second): RateLimit
{
if ($first->isAccepted() !== $second->isAccepted()) {
return $first->isAccepted() ? $second : $first;
}
$firstRemainingTokens = $first->getRemainingTokens();
$secondRemainingTokens = $second->getRemainingTokens();
if ($firstRemainingTokens === $secondRemainingTokens) {
return $first->getRetryAfter() < $second->getRetryAfter() ? $second : $first;
}
return $firstRemainingTokens > $secondRemainingTokens ? $second : $first;
}
}

View File

@@ -103,6 +103,7 @@ class RedirectResponse extends Response
</html>', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
$this->headers->set('Location', $url);
$this->headers->set('Content-Type', 'text/html; charset=utf-8');
return $this;
}

View File

@@ -191,7 +191,7 @@ class Request
protected $session;
/**
* @var string
* @var string|null
*/
protected $locale;
@@ -246,6 +246,9 @@ class Request
self::HEADER_X_FORWARDED_PREFIX => 'X_FORWARDED_PREFIX',
];
/** @var bool */
private $isIisRewrite = false;
/**
* @param array $query The GET parameters
* @param array $request The POST parameters
@@ -439,16 +442,16 @@ class Request
/**
* Clones a request and overrides some of its parameters.
*
* @param array $query The GET parameters
* @param array $request The POST parameters
* @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
* @param array $cookies The COOKIE parameters
* @param array $files The FILES parameters
* @param array $server The SERVER parameters
* @param array|null $query The GET parameters
* @param array|null $request The POST parameters
* @param array|null $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
* @param array|null $cookies The COOKIE parameters
* @param array|null $files The FILES parameters
* @param array|null $server The SERVER parameters
*
* @return static
*/
public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
public function duplicate(?array $query = null, ?array $request = null, ?array $attributes = null, ?array $cookies = null, ?array $files = null, ?array $server = null)
{
$dup = clone $this;
if (null !== $query) {
@@ -522,10 +525,10 @@ class Request
$cookies = [];
foreach ($this->cookies as $k => $v) {
$cookies[] = $k.'='.$v;
$cookies[] = \is_array($v) ? http_build_query([$k => $v], '', '; ', \PHP_QUERY_RFC3986) : "$k=$v";
}
if (!empty($cookies)) {
if ($cookies) {
$cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
}
@@ -562,7 +565,7 @@ class Request
$request = ['g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE];
$requestOrder = ini_get('request_order') ?: ini_get('variables_order');
$requestOrder = \ini_get('request_order') ?: \ini_get('variables_order');
$requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
$_REQUEST = [[]];
@@ -1452,7 +1455,7 @@ class Request
*/
public function getLocale()
{
return null === $this->locale ? $this->defaultLocale : $this->locale;
return $this->locale ?? $this->defaultLocale;
}
/**
@@ -1573,9 +1576,9 @@ class Request
/**
* Gets the request body decoded as array, typically from a JSON payload.
*
* @throws JsonException When the body cannot be decoded to an array
*
* @return array
*
* @throws JsonException When the body cannot be decoded to an array
*/
public function toArray()
{
@@ -1648,7 +1651,7 @@ class Request
*
* @return string|null
*/
public function getPreferredLanguage(array $locales = null)
public function getPreferredLanguage(?array $locales = null)
{
$preferredLanguages = $this->getLanguages();
@@ -1689,7 +1692,8 @@ class Request
$languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
$this->languages = [];
foreach ($languages as $lang => $acceptHeaderItem) {
foreach ($languages as $acceptHeaderItem) {
$lang = $acceptHeaderItem->getValue();
if (str_contains($lang, '-')) {
$codes = explode('-', $lang);
if ('i' === $codes[0]) {
@@ -1727,7 +1731,7 @@ class Request
return $this->charsets;
}
return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
return $this->charsets = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()));
}
/**
@@ -1741,7 +1745,7 @@ class Request
return $this->encodings;
}
return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
return $this->encodings = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all()));
}
/**
@@ -1755,7 +1759,7 @@ class Request
return $this->acceptableContentTypes;
}
return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
return $this->acceptableContentTypes = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()));
}
/**
@@ -1804,11 +1808,10 @@ class Request
{
$requestUri = '';
if ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
if ($this->isIisRewrite() && '' != $this->server->get('UNENCODED_URL')) {
// IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
$requestUri = $this->server->get('UNENCODED_URL');
$this->server->remove('UNENCODED_URL');
$this->server->remove('IIS_WasUrlRewritten');
} elseif ($this->server->has('REQUEST_URI')) {
$requestUri = $this->server->get('REQUEST_URI');
@@ -2011,7 +2014,13 @@ class Request
*/
private function getUrlencodedPrefix(string $string, string $prefix): ?string
{
if (!str_starts_with(rawurldecode($string), $prefix)) {
if ($this->isIisRewrite()) {
// ISS with UrlRewriteModule might report SCRIPT_NAME/PHP_SELF with wrong case
// see https://github.com/php/php-src/issues/11981
if (0 !== stripos(rawurldecode($string), $prefix)) {
return null;
}
} elseif (!str_starts_with(rawurldecode($string), $prefix)) {
return null;
}
@@ -2052,7 +2061,7 @@ class Request
return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR', ''), self::$trustedProxies);
}
private function getTrustedValues(int $type, string $ip = null): array
private function getTrustedValues(int $type, ?string $ip = null): array
{
$clientValues = [];
$forwardedValues = [];
@@ -2144,4 +2153,20 @@ class Request
// Now the IP chain contains only untrusted proxies and the client IP
return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp];
}
/**
* Is this IIS with UrlRewriteModule?
*
* This method consumes, caches and removed the IIS_WasUrlRewritten env var,
* so we don't inherit it to sub-requests.
*/
private function isIisRewrite(): bool
{
if (1 === $this->server->getInt('IIS_WasUrlRewritten')) {
$this->isIisRewrite = true;
$this->server->remove('IIS_WasUrlRewritten');
}
return $this->isIisRewrite;
}
}

View File

@@ -58,7 +58,7 @@ class RequestMatcher implements RequestMatcherInterface
* @param string|string[]|null $ips
* @param string|string[]|null $schemes
*/
public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, int $port = null)
public function __construct(?string $path = null, ?string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, ?int $port = null)
{
$this->matchPath($path);
$this->matchHost($host);
@@ -91,7 +91,7 @@ class RequestMatcher implements RequestMatcherInterface
}
/**
* Adds a check for the the URL port.
* Adds a check for the URL port.
*
* @param int|null $port The port number to connect to
*/

View File

@@ -72,7 +72,7 @@ class Response
public const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
public const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
public const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
public const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
public const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; // RFC7725
public const HTTP_INTERNAL_SERVER_ERROR = 500;
public const HTTP_NOT_IMPLEMENTED = 501;
public const HTTP_BAD_GATEWAY = 502;
@@ -298,7 +298,7 @@ class Response
$charset = $this->charset ?: 'UTF-8';
if (!$headers->has('Content-Type')) {
$headers->set('Content-Type', 'text/html; charset='.$charset);
} elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
} elseif (0 === stripos($headers->get('Content-Type') ?? '', 'text/') && false === stripos($headers->get('Content-Type') ?? '', 'charset')) {
// add the charset
$headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
}
@@ -399,6 +399,7 @@ class Response
litespeed_finish_request();
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
static::closeOutputBuffers(0, true);
flush();
}
return $this;
@@ -462,7 +463,7 @@ class Response
*
* @final
*/
public function setStatusCode(int $code, string $text = null): object
public function setStatusCode(int $code, ?string $text = null): object
{
$this->statusCode = $code;
if ($this->isInvalid()) {
@@ -736,7 +737,7 @@ class Response
*
* @final
*/
public function setExpires(\DateTimeInterface $date = null): object
public function setExpires(?\DateTimeInterface $date = null): object
{
if (null === $date) {
$this->headers->remove('Expires');
@@ -773,8 +774,10 @@ class Response
return (int) $this->headers->getCacheControlDirective('max-age');
}
if (null !== $this->getExpires()) {
return (int) $this->getExpires()->format('U') - (int) $this->getDate()->format('U');
if (null !== $expires = $this->getExpires()) {
$maxAge = (int) $expires->format('U') - (int) $this->getDate()->format('U');
return max($maxAge, 0);
}
return null;
@@ -818,7 +821,7 @@ class Response
*
* It returns null when no freshness information is present in the response.
*
* When the responses TTL is <= 0, the response may not be served from cache without first
* When the response's TTL is 0, the response may not be served from cache without first
* revalidating with the origin.
*
* @final
@@ -827,7 +830,7 @@ class Response
{
$maxAge = $this->getMaxAge();
return null !== $maxAge ? $maxAge - $this->getAge() : null;
return null !== $maxAge ? max($maxAge - $this->getAge(), 0) : null;
}
/**
@@ -883,7 +886,7 @@ class Response
*
* @final
*/
public function setLastModified(\DateTimeInterface $date = null): object
public function setLastModified(?\DateTimeInterface $date = null): object
{
if (null === $date) {
$this->headers->remove('Last-Modified');
@@ -921,7 +924,7 @@ class Response
*
* @final
*/
public function setEtag(string $etag = null, bool $weak = false): object
public function setEtag(?string $etag = null, bool $weak = false): object
{
if (null === $etag) {
$this->headers->remove('Etag');
@@ -1214,7 +1217,7 @@ class Response
*
* @final
*/
public function isRedirect(string $location = null): bool
public function isRedirect(?string $location = null): bool
{
return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location'));
}

View File

@@ -88,7 +88,7 @@ class ResponseHeaderBag extends HeaderBag
/**
* {@inheritdoc}
*/
public function all(string $key = null)
public function all(?string $key = null)
{
$headers = parent::all();
@@ -186,7 +186,7 @@ class ResponseHeaderBag extends HeaderBag
/**
* Removes a cookie from the array, but does not unset it in the browser.
*/
public function removeCookie(string $name, ?string $path = '/', string $domain = null)
public function removeCookie(string $name, ?string $path = '/', ?string $domain = null)
{
if (null === $path) {
$path = '/';
@@ -239,7 +239,7 @@ class ResponseHeaderBag extends HeaderBag
/**
* Clears a cookie in the browser.
*/
public function clearCookie(string $name, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, string $sameSite = null)
public function clearCookie(string $name, ?string $path = '/', ?string $domain = null, bool $secure = false, bool $httpOnly = true, ?string $sameSite = null)
{
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, $sameSite));
}

View File

@@ -31,7 +31,7 @@ class ServerBag extends ParameterBag
foreach ($this->parameters as $key => $value) {
if (str_starts_with($key, 'HTTP_')) {
$headers[substr($key, 5)] = $value;
} elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
} elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true) && '' !== $value) {
$headers[$key] = $value;
}
}
@@ -51,7 +51,7 @@ class ServerBag extends ParameterBag
* RewriteCond %{HTTP:Authorization} .+
* RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
* RewriteCond %{REQUEST_FILENAME} !-f
* RewriteRule ^(.*)$ app.php [QSA,L]
* RewriteRule ^(.*)$ index.php [QSA,L]
*/
$authorizationHeader = null;

Some files were not shown because too many files have changed in this diff Show More