diff --git a/composer.json b/composer.json index 9874dc49e..4da00a804 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,10 @@ "require": { "php": "^7.3 || ^8.0", "ext-json": "*", + "composer-plugin-api": "~1.0 || ~2.0", "amphp/amp": "^2.4", "amphp/parallel": "^1.4", "amphp/parallel-functions": "^1.0", - "composer-plugin-api": "~1.0 || ~2.0", "doctrine/collections": "^1.6.7", "gitonomy/gitlib": "^1.0.3", "monolog/monolog": "~1.16 || ^2.0", diff --git a/grumphp.yml.dist b/grumphp.yml.dist index a333a3151..f2a490571 100644 --- a/grumphp.yml.dist +++ b/grumphp.yml.dist @@ -36,6 +36,7 @@ grumphp: functional: true psalm: show_info: true + no_cache: true testsuites: git_pre_commit: tasks: [phpcs, phpspec, phpunit, composer, composer_normalize, yamllint, phplint, phpparser, psalm] diff --git a/phive.xml b/phive.xml index d28752319..905d55981 100644 --- a/phive.xml +++ b/phive.xml @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/psalm.xml b/psalm.xml index 53275583b..1b82c729f 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,7 +1,7 @@ getIterator(), [$closure]); + $filter = new Iterator\CustomFilterIterator($this->getIterator(), [$p]); return new self(iterator_to_array($filter)); } @@ -221,11 +223,9 @@ public function ignoreSymlinks(): FilesCollection public function serialize(): string { return serialize($this->map(function (SplFileInfo $fileInfo): string { - return (string) ( - $fileInfo instanceof SymfonySplFileInfo - ? $fileInfo->getRelativePathname() - : $fileInfo->getPathname() - ); + return $fileInfo instanceof SymfonySplFileInfo + ? $fileInfo->getRelativePathname() + : $fileInfo->getPathname(); })->toArray()); } diff --git a/src/Configuration/Model/FixerConfig.php b/src/Configuration/Model/FixerConfig.php index b540ab856..f4d02fc64 100644 --- a/src/Configuration/Model/FixerConfig.php +++ b/src/Configuration/Model/FixerConfig.php @@ -33,8 +33,8 @@ public function __construct( public static function fromArray(array $config): self { return new self( - (bool) ($config['enabled'] ?? false), - (bool) ($config['fix_by_default'] ?? false) + ($config['enabled'] ?? false), + ($config['fix_by_default'] ?? false) ); } diff --git a/src/Configuration/Model/ParallelConfig.php b/src/Configuration/Model/ParallelConfig.php index a984e50b9..32a87ee3c 100644 --- a/src/Configuration/Model/ParallelConfig.php +++ b/src/Configuration/Model/ParallelConfig.php @@ -33,8 +33,8 @@ public function __construct( public static function fromArray(array $config): self { return new self( - (bool) ($config['enabled'] ?? false), - (int) ($config['max_workers'] ?? 1) + ($config['enabled'] ?? false), + ($config['max_workers'] ?? 1) ); } diff --git a/src/Console/Command/Git/InitCommand.php b/src/Console/Command/Git/InitCommand.php index e618a39fd..604d0759a 100644 --- a/src/Console/Command/Git/InitCommand.php +++ b/src/Console/Command/Git/InitCommand.php @@ -153,7 +153,10 @@ private function parseHookVariable(string $key, $value): string case 'ENV': return DotEnvSerializer::serialize($value); default: - /** @var string $value */ + /** + * @var string $value + * @psalm-suppress PossiblyInvalidCast, RedundantCastGivenDocblockType + */ return (string) $value; } } diff --git a/src/Event/Dispatcher/Bridge/SymfonyEventDispatcher.php b/src/Event/Dispatcher/Bridge/SymfonyEventDispatcher.php index 684c0761a..eeb8dd01e 100644 --- a/src/Event/Dispatcher/Bridge/SymfonyEventDispatcher.php +++ b/src/Event/Dispatcher/Bridge/SymfonyEventDispatcher.php @@ -24,7 +24,7 @@ public function __construct($eventDispatcher) $this->dispatcher = $eventDispatcher; } - public function dispatch(Event $event, string $eventName = null): void + public function dispatch(Event $event, string $name = null): void { $interfacesImplemented = class_implements($this->dispatcher); if (in_array(SymfonyEventDispatcherContract::class, $interfacesImplemented, true)) { @@ -32,7 +32,7 @@ public function dispatch(Event $event, string $eventName = null): void * @psalm-suppress InvalidArgument * @psalm-suppress TooManyArguments */ - $this->dispatcher->dispatch($event, $eventName); + $this->dispatcher->dispatch($event, $name); return; } @@ -40,6 +40,6 @@ public function dispatch(Event $event, string $eventName = null): void * @psalm-suppress InvalidArgument * @psalm-suppress TooManyArguments */ - $this->dispatcher->dispatch($eventName, $event); + $this->dispatcher->dispatch($name, $event); } } diff --git a/src/Fixer/FixerUpper.php b/src/Fixer/FixerUpper.php index 0b5e85e05..0321e27ff 100644 --- a/src/Fixer/FixerUpper.php +++ b/src/Fixer/FixerUpper.php @@ -57,8 +57,8 @@ static function (TaskResultInterface $result): bool { $result->ok() ? $this->IO->colorize(['✔'], 'green') : $this->IO->colorize(['✘'], 'red') ); - if ($this->IO->isVerbose() && $result->error()) { - $this->IO->writeError($this->IO->colorize([$result->error()->getMessage()], 'red')); + if ($this->IO->isVerbose() && $error = $result->error()) { + $this->IO->writeError($this->IO->colorize([$error->getMessage()], 'red')); } $count++; diff --git a/src/Fixer/Provider/FixableProcessResultProvider.php b/src/Fixer/Provider/FixableProcessResultProvider.php index e127f81f0..fcf08a53f 100644 --- a/src/Fixer/Provider/FixableProcessResultProvider.php +++ b/src/Fixer/Provider/FixableProcessResultProvider.php @@ -19,6 +19,7 @@ public static function provide( array $successExitCodes = [0] ): FixableTaskResult { $fixerProcess = $fixerProcessBuilder(); + /** @psalm-suppress RedundantConditionGivenDocblockType */ assert($fixerProcess instanceof Process); $fixerCommand = $fixerProcess->getCommandLine(); diff --git a/src/Formatter/GitBlacklistFormatter.php b/src/Formatter/GitBlacklistFormatter.php index 4c8ab45ed..9c6ad2f8c 100644 --- a/src/Formatter/GitBlacklistFormatter.php +++ b/src/Formatter/GitBlacklistFormatter.php @@ -45,7 +45,7 @@ private function formatOutput(string $output): string { $result = static::RESET_COLOR; foreach (array_filter(explode("\n", $output)) as $lineNumber => $line) { - $result .= preg_match('/^[0-9]+/', $line) ? $this->trimOutputLine($line, (int) $lineNumber) : $line; + $result .= preg_match('/^[0-9]+/', $line) ? $this->trimOutputLine($line, $lineNumber) : $line; $result .= PHP_EOL; } diff --git a/src/Process/ProcessFactory.php b/src/Process/ProcessFactory.php index ffd8d155d..b7a73708a 100644 --- a/src/Process/ProcessFactory.php +++ b/src/Process/ProcessFactory.php @@ -22,6 +22,6 @@ public static function fromArguments(ProcessArgumentsCollection $arguments): Pro */ public static function fromScalar($arguments): Process { - return is_array($arguments) ? new Process($arguments) : Process::fromShellCommandline((string) $arguments); + return is_array($arguments) ? new Process($arguments) : Process::fromShellCommandline($arguments); } } diff --git a/src/Process/TmpFileUsingProcessRunner.php b/src/Process/TmpFileUsingProcessRunner.php index 069607102..51618beb0 100644 --- a/src/Process/TmpFileUsingProcessRunner.php +++ b/src/Process/TmpFileUsingProcessRunner.php @@ -29,7 +29,7 @@ public static function run(callable $processBuilder, callable $writer): Process } foreach ($writer() as $entry) { - fwrite($tmp, (string) $entry); + fwrite($tmp, $entry); } fseek($tmp, 0); diff --git a/src/Runner/FixableTaskResult.php b/src/Runner/FixableTaskResult.php index 033c46b61..d94079735 100644 --- a/src/Runner/FixableTaskResult.php +++ b/src/Runner/FixableTaskResult.php @@ -69,10 +69,10 @@ public function getContext(): ContextInterface return $this->result->getContext(); } - public function withAppendedMessage(string $additionalMessage): TaskResultInterface + public function withAppendedMessage(string $message): TaskResultInterface { $new = clone $this; - $new->result = $this->result->withAppendedMessage($additionalMessage); + $new->result = $this->result->withAppendedMessage($message); return $new; } diff --git a/src/Runner/Middleware/HandleRunnerMiddleware.php b/src/Runner/Middleware/HandleRunnerMiddleware.php index fc3db12a3..30a4c3fe4 100644 --- a/src/Runner/Middleware/HandleRunnerMiddleware.php +++ b/src/Runner/Middleware/HandleRunnerMiddleware.php @@ -38,7 +38,7 @@ public function __construct(TaskHandler $taskHandler, RunnerConfig $config) public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection { return new TaskResultCollection( - (array) wait( + wait( /** * @return \Generator */ diff --git a/src/Runner/MiddlewareStack.php b/src/Runner/MiddlewareStack.php index 21f890871..f71e2f4f9 100644 --- a/src/Runner/MiddlewareStack.php +++ b/src/Runner/MiddlewareStack.php @@ -7,6 +7,9 @@ use GrumPHP\Collection\TaskResultCollection; use GrumPHP\Runner\Middleware\RunnerMiddlewareInterface; +/** + * @psalm-immutable + */ class MiddlewareStack { /** @@ -30,9 +33,6 @@ public static function fromIterable(iterable $middlewares): self ); } - /** - * @psalm-pure - */ public function handle(TaskRunnerContext $context): TaskResultCollection { return ($this->stack)($context); diff --git a/src/Runner/Promise/MultiPromise.php b/src/Runner/Promise/MultiPromise.php index 89dbdeebc..37a925639 100644 --- a/src/Runner/Promise/MultiPromise.php +++ b/src/Runner/Promise/MultiPromise.php @@ -20,7 +20,7 @@ class MultiPromise * @param array> $promises * @param callable(TValue):bool $shouldCancel * - * @return Promise + * @return Promise */ public static function cancelable(array $promises, callable $shouldCancel): Promise { @@ -50,26 +50,31 @@ static function (CancelledException $error) use ($deferred, $watcherId): void { }); }; - $promise->onResolve(static function ( - ?\Throwable $error, - $result - ) use ( - $deferred, - $cancel, - $shouldCancel - ): void { - if ($error instanceof \Throwable) { - $cancel($error); - $deferred->fail($error); - return; - } + $promise->onResolve( + /** + * @param TValue $result + */ + static function ( + ?\Throwable $error, + $result + ) use ( + $deferred, + $cancel, + $shouldCancel + ): void { + if ($error instanceof \Throwable) { + $cancel($error); + $deferred->fail($error); + return; + } - if ($result && $shouldCancel($result)) { - $cancel(); - } + if ($result && $shouldCancel($result)) { + $cancel(); + } - $deferred->resolve($result); - }); + $deferred->resolve($result); + } + ); } ); diff --git a/src/Runner/Reporting/RunnerReporter.php b/src/Runner/Reporting/RunnerReporter.php index 7996accb8..aac0c81d6 100644 --- a/src/Runner/Reporting/RunnerReporter.php +++ b/src/Runner/Reporting/RunnerReporter.php @@ -41,8 +41,8 @@ public function __construct( public function start(TaskRunnerContext $context): void { $this->IO->write($this->IO->colorize(['GrumPHP is sniffing your code!'], 'yellow')); - if ($context->getTestSuite()) { - $this->IO->style()->note('Running testsuite: '.$context->getTestSuite()->getName()); + if ($testSuite = $context->getTestSuite()) { + $this->IO->style()->note('Running testsuite: '.$testSuite->getName()); } } diff --git a/src/Runner/TaskHandler/Middleware/TaskHandlerMiddlewareInterface.php b/src/Runner/TaskHandler/Middleware/TaskHandlerMiddlewareInterface.php index e8f792bb6..8ee61884f 100644 --- a/src/Runner/TaskHandler/Middleware/TaskHandlerMiddlewareInterface.php +++ b/src/Runner/TaskHandler/Middleware/TaskHandlerMiddlewareInterface.php @@ -17,7 +17,7 @@ interface TaskHandlerMiddlewareInterface */ public function handle( TaskInterface $task, - TaskRunnerContext $runnercontext, + TaskRunnerContext $runnerContext, callable $next ): Promise; } diff --git a/src/Runner/TaskHandler/TaskHandler.php b/src/Runner/TaskHandler/TaskHandler.php index fcd30e678..c681177c3 100644 --- a/src/Runner/TaskHandler/TaskHandler.php +++ b/src/Runner/TaskHandler/TaskHandler.php @@ -12,6 +12,9 @@ use GrumPHP\Runner\TaskRunnerContext; use GrumPHP\Task\TaskInterface; +/** + * @psalm-immutable + */ class TaskHandler { /** @@ -37,7 +40,6 @@ public static function fromIterable(iterable $handlers): self } /** - * @psalm-pure * @return Promise */ public function handle(TaskInterface $task, TaskRunnerContext $runnerContext): Promise diff --git a/src/Runner/TaskResult.php b/src/Runner/TaskResult.php index d58c56ab6..35ccc8f94 100644 --- a/src/Runner/TaskResult.php +++ b/src/Runner/TaskResult.php @@ -95,10 +95,10 @@ public function getContext(): ContextInterface return $this->context; } - public function withAppendedMessage(string $additionalMessage): TaskResultInterface + public function withAppendedMessage(string $message): TaskResultInterface { $new = clone $this; - $new->message = $this->message . $additionalMessage; + $new->message = $this->message . $message; return $new; } diff --git a/src/Task/AbstractExternalTask.php b/src/Task/AbstractExternalTask.php index ed0d6928d..5aa8c8fb3 100644 --- a/src/Task/AbstractExternalTask.php +++ b/src/Task/AbstractExternalTask.php @@ -9,6 +9,9 @@ use GrumPHP\Task\Config\EmptyTaskConfig; use GrumPHP\Task\Config\TaskConfigInterface; +/** + * @template-covariant Formatter extends ProcessFormatterInterface + */ abstract class AbstractExternalTask implements TaskInterface { /** @@ -22,10 +25,13 @@ abstract class AbstractExternalTask implements TaskInterface protected $processBuilder; /** - * @var ProcessFormatterInterface + * @var Formatter */ protected $formatter; + /** + * @param Formatter $formatter + */ public function __construct(ProcessBuilder $processBuilder, ProcessFormatterInterface $formatter) { $this->config = new EmptyTaskConfig(); diff --git a/src/Task/AbstractLinterTask.php b/src/Task/AbstractLinterTask.php index 4091e7d2c..1bd1686ad 100644 --- a/src/Task/AbstractLinterTask.php +++ b/src/Task/AbstractLinterTask.php @@ -12,6 +12,9 @@ use GrumPHP\Task\Config\TaskConfigInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @template-covariant Linter extends LinterInterface + */ abstract class AbstractLinterTask implements TaskInterface { /** @@ -20,10 +23,13 @@ abstract class AbstractLinterTask implements TaskInterface protected $config; /** - * @var LinterInterface + * @var Linter */ protected $linter; + /** + * @param Linter $linter + */ public function __construct(LinterInterface $linter) { $this->linter = $linter; diff --git a/src/Task/AbstractParserTask.php b/src/Task/AbstractParserTask.php index ee9c82bff..3ef2409c2 100644 --- a/src/Task/AbstractParserTask.php +++ b/src/Task/AbstractParserTask.php @@ -12,6 +12,9 @@ use GrumPHP\Task\Config\TaskConfigInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @template-covariant Parser extends ParserInterface + */ abstract class AbstractParserTask implements TaskInterface { /** @@ -20,10 +23,13 @@ abstract class AbstractParserTask implements TaskInterface protected $configuration; /** - * @var ParserInterface + * @var Parser */ protected $parser; + /** + * @param Parser $parser + */ public function __construct(ParserInterface $parser) { $this->configuration = new EmptyTaskConfig(); diff --git a/src/Task/CloverCoverage.php b/src/Task/CloverCoverage.php index 01297f511..bbd9829b7 100644 --- a/src/Task/CloverCoverage.php +++ b/src/Task/CloverCoverage.php @@ -102,7 +102,7 @@ public function run(ContextInterface $context): TaskResultInterface $totalElements = (int) current($xml->xpath('/coverage/project/metrics/@elements')); $checkedElements = (int) current($xml->xpath('/coverage/project/metrics/@coveredelements')); - if (0 === (int) $totalElements) { + if (0 === $totalElements) { return TaskResult::createSkipped($this, $context); } diff --git a/src/Task/Git/CommitMessage.php b/src/Task/Git/CommitMessage.php index 192e99c84..ae31b387b 100644 --- a/src/Task/Git/CommitMessage.php +++ b/src/Task/Git/CommitMessage.php @@ -136,7 +136,7 @@ public function run(ContextInterface $context): TaskResultInterface } - if ((bool) $this->enforceTypeScopeConventions()) { + if ($this->enforceTypeScopeConventions()) { try { $this->checkTypeScopeConventions($context); } catch (RuntimeException $e) { @@ -287,7 +287,7 @@ private function subjectIsCapitalized(GitCommitMsgContext $context): bool return false; } - $firstLetter = (string) ($match[1] ?? ''); + $firstLetter = $match[1] ?? ''; return !(1 !== preg_match('/^(fixup|squash)!/u', $subject) && 1 !== preg_match('/[[:upper:]]/u', $firstLetter)); } @@ -317,13 +317,16 @@ function (): ?string { $lines = preg_split('/\R/u', $commitMessage); $everythingBelowWillBeIgnored = false; - return array_values(array_filter($lines, function ($line) use (&$everythingBelowWillBeIgnored, $commentChar) { - if (mb_stripos($line, $commentChar.' Everything below it will be ignored.') !== false) { - $everythingBelowWillBeIgnored = true; - return false; + return array_values(array_filter( + $lines, + function (string $line) use (&$everythingBelowWillBeIgnored, $commentChar) { + if (mb_stripos($line, $commentChar.' Everything below it will be ignored.') !== false) { + $everythingBelowWillBeIgnored = true; + return false; + } + return 0 !== strpos($line, $commentChar) && !$everythingBelowWillBeIgnored; } - return 0 !== strpos($line, $commentChar) && !$everythingBelowWillBeIgnored; - })); + )); } private function enforceTypeScopeConventions(): bool diff --git a/src/Task/JsonLint.php b/src/Task/JsonLint.php index 73d9b1f5c..8dea47440 100644 --- a/src/Task/JsonLint.php +++ b/src/Task/JsonLint.php @@ -13,6 +13,9 @@ use GrumPHP\Task\Context\RunContext; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractLinterTask + */ class JsonLint extends AbstractLinterTask { /** diff --git a/src/Task/PhpParser.php b/src/Task/PhpParser.php index 12aae4f6a..03765ec5d 100644 --- a/src/Task/PhpParser.php +++ b/src/Task/PhpParser.php @@ -11,6 +11,9 @@ use GrumPHP\Runner\TaskResultInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractParserTask + */ class PhpParser extends AbstractParserTask { const KIND_PHP5 = 'php5'; diff --git a/src/Task/XmlLint.php b/src/Task/XmlLint.php index 0ece6d6c6..d9620b4d6 100644 --- a/src/Task/XmlLint.php +++ b/src/Task/XmlLint.php @@ -14,7 +14,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; /** - * @property \GrumPHP\Linter\Xml\XmlLinter $linter + * @extends AbstractLinterTask */ class XmlLint extends AbstractLinterTask { diff --git a/src/Task/YamlLint.php b/src/Task/YamlLint.php index e139e60cb..75aa1422c 100644 --- a/src/Task/YamlLint.php +++ b/src/Task/YamlLint.php @@ -14,6 +14,9 @@ use GrumPHP\Task\Context\RunContext; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @extends AbstractLinterTask + */ class YamlLint extends AbstractLinterTask { diff --git a/src/Util/PhpVersion.php b/src/Util/PhpVersion.php index 4e711ca7e..372153843 100644 --- a/src/Util/PhpVersion.php +++ b/src/Util/PhpVersion.php @@ -21,7 +21,7 @@ public function isSupportedVersion(string $currentVersion): bool $now = new \DateTime(); foreach ($this->versions as $number => $eol) { $eol = new \DateTime($eol); - if ($now < $eol && (int) version_compare($currentVersion, $number) >= 0) { + if ($now < $eol && version_compare($currentVersion, $number) >= 0) { return true; } } @@ -31,6 +31,6 @@ public function isSupportedVersion(string $currentVersion): bool public function isSupportedProjectVersion(string $currentVersion, string $projectVersion): bool { - return (int) version_compare($currentVersion, $projectVersion) >= 0; + return version_compare($currentVersion, $projectVersion) >= 0; } } diff --git a/src/Util/Regex.php b/src/Util/Regex.php index ccfe4f2d5..6ef046341 100644 --- a/src/Util/Regex.php +++ b/src/Util/Regex.php @@ -57,6 +57,7 @@ private function toRegex(string $string): string public function addPatternModifier(string $modifier): void { + /** @psalm-suppress InvalidLiteralArgument */ if ('' === $modifier || false === strpos(self::ALLOWED_MODIFIERS, $modifier)) { throw new RuntimeException('Invalid regex modifier: '.$modifier); } diff --git a/stubs/Amp.php b/stubs/Amp.php index 32ff3e85c..0611e5bec 100644 --- a/stubs/Amp.php +++ b/stubs/Amp.php @@ -1,77 +1,113 @@ $gen - * - * @return callable():Promise - */ -function coroutine(callable $gen) : callable -{ -} - -/** - * @template TReturn - * - * @param callable():(\Generator|TReturn) $gen - * - * @return Promise - */ -function call(callable $gen) : Promise -{ -} - - -/** - * @template TReturn - */ -interface Promise -{ /** - * @param callable(?\Throwable, ?TReturn):void $onResolved + * @template TReturn * - * @return void + * @param callable():\Generator $gen + * + * @return callable():Promise */ - public function onResolve(callable $onResolved); -} + function coroutine(callable $gen) : callable + { + } -/** - * @template TReturn - */ -final class LazyPromise -{ /** - * @param callable(?\Throwable, ?TReturn):void $onResolved + * @template TReturn * - * @return void + * @param callable():(\Generator|TReturn) $gen + * + * @return Promise */ - public function onResolve(callable $onResolved) + function call(callable $gen) : Promise { } -} -/** - * @template TReturn - * - * @template-implements Promise - */ -class Success implements Promise -{ + /** - * @param TReturn|null $value + * @template TReturn */ - public function __construct($value = null) + interface Promise { + /** + * @param callable(?\Throwable, ?TReturn):void $onResolved + * + * @return void + */ + public function onResolve(callable $onResolved); } /** - * @param callable(?Throwable, ?TReturn):void $onResolved + * @template TReturn + */ + final class LazyPromise + { + /** + * @param callable(?\Throwable, ?TReturn):void $onResolved + * + * @return void + */ + public function onResolve(callable $onResolved) + { + } + } + + /** + * @template TReturn * - * @return void + * @template-implements Promise */ - public function onResolve(callable $onResolved) + class Success implements Promise { + /** + * @param TReturn|null $value + */ + public function __construct($value = null) + { + } + + /** + * @param callable(?Throwable, ?TReturn):void $onResolved + * + * @return void + */ + public function onResolve(callable $onResolved) + { + } } } + +namespace Amp\Promise { + use React\Promise\Promise as ReactPromise; + use Amp\Promise; + + /** + * @template TPromise + * @template T as Promise|ReactPromise + * + * @param Promise|ReactPromise $promise Promise to wait for. + * + * @return mixed Promise success value. + * + * @psalm-param T $promise + * @psalm-return (T is Promise ? TPromise : mixed) + * + * @throws \TypeError If $promise is not an instance of \Amp\Promise or \React\Promise\PromiseInterface. + * @throws \Error If the event loop stopped without the $promise being resolved. + * @throws \Throwable Promise failure reason. + */ + function wait($promise){} + + /** + * @template TValue + * + * @param Promise[]|\React\Promise\PromiseInterface[] $promises + * + * @return Promise + * + * @throws \Error If a non-Promise is in the array. + */ + function any(array $promises): Promise {} +} + + diff --git a/tools/composer-normalize b/tools/composer-normalize index 6a5c3ae83..dfa36f728 100755 Binary files a/tools/composer-normalize and b/tools/composer-normalize differ diff --git a/tools/phpcbf b/tools/phpcbf index 791da6988..dcf50adc3 100755 Binary files a/tools/phpcbf and b/tools/phpcbf differ diff --git a/tools/phpcs b/tools/phpcs index 1989255de..f49d1379e 100755 Binary files a/tools/phpcs and b/tools/phpcs differ diff --git a/tools/psalm b/tools/psalm index 81abe7e18..35b6dd494 100755 Binary files a/tools/psalm and b/tools/psalm differ