From a8bef6b0510e0613f28cfe306766f916dc3148a5 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 22 Apr 2024 16:32:15 +0400 Subject: [PATCH 001/106] Make the PORT parameter a list --- src/Command/Run.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Command/Run.php b/src/Command/Run.php index 4cbc91ba..97552474 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -32,7 +32,13 @@ final class Run extends Command implements SignalableCommandInterface public function configure(): void { - $this->addOption('port', 'p', InputOption::VALUE_OPTIONAL, 'Port to listen', 9912); + $this->addOption( + 'port', + 'p', + InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'Port to listen', + [9912], + ); $this->addOption( 'sender', 's', @@ -52,14 +58,25 @@ protected function execute( $output->writeln(\sprintf('%s v%s', Info::NAME, Info::VERSION)); $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); - $port = (int)$input->getOption('port') ?: 9912; + /** + * Prepare port listeners + * @var SocketServer[] $servers + */ + $servers = []; + foreach ($input->getOption('port') ?: [9912] as $port) { + \is_numeric($port) or throw new \InvalidArgumentException( + \sprintf('Invalid port `%s`. It must be a number.', $port), + ); + $servers[] = new SocketServer((int)$port); + } + /** @var non-empty-string[] $senders */ $senders = (array)$input->getOption('sender'); $registry = $this->createRegistry($output); $this->app = new Application( - [new SocketServer($port)], + $servers, new Logger($output), senders: $registry->getSenders($senders), withFrontend: $input->getOption('ui') !== false, From 8ff8358e2ffac266567fdb4bd6c3e8e669733e41 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 22 Apr 2024 16:51:22 +0400 Subject: [PATCH 002/106] Fix psalm issues, update docs --- README.md | 15 +++++++++++---- src/Command/Run.php | 13 ++++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 80e5e56e..f006d738 100644 --- a/README.md +++ b/README.md @@ -141,13 +141,20 @@ $responder->respond(trap($response)->return()); ### Default port -By default, the Trap server operates on port `9912`. However, if you wish to utilize a different port, you can easily -make this adjustment using the `-p` option. +Trap automatically recognizes the type of traffic. +Therefore, there is no need to open separate ports for different protocols. +By default, it operates on port `9912`. +However, if you wish to utilize a different port, you can easily make this adjustment using the `-p` option: -For example, to switch to port 8000, you would use the following command: +```bash +vendor/bin/trap -p8000 +``` + +Sometimes, it's convenient to run Trap on the same ports that [Buggregator](https://github.com/buggregator/server) +uses by default. Well, that's also possible: ```bash -vendor/bin/trap -p 8000 +vendor/bin/trap -p1025 -p9912 -p9913 -p8000 ``` ### Choosing Your Senders diff --git a/src/Command/Run.php b/src/Command/Run.php index 97552474..e10fe6eb 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -63,11 +63,18 @@ protected function execute( * @var SocketServer[] $servers */ $servers = []; - foreach ($input->getOption('port') ?: [9912] as $port) { + $ports = $input->getOption('port') ?: [9912]; + \assert(\is_array($ports)); + foreach ($ports as $port) { + \assert(\is_scalar($port)); \is_numeric($port) or throw new \InvalidArgumentException( - \sprintf('Invalid port `%s`. It must be a number.', $port), + \sprintf('Invalid port `%s`. It must be a number.', (string)$port), ); - $servers[] = new SocketServer((int)$port); + $port = (int)$port; + $port > 0 && $port < 65536 or throw new \InvalidArgumentException( + \sprintf('Invalid port `%s`. It must be in range 1-65535.', $port), + ); + $servers[] = new SocketServer($port); } /** @var non-empty-string[] $senders */ From 15bae7cb375608a1091334dc4787510c74088c8a Mon Sep 17 00:00:00 2001 From: l0gic Date: Mon, 29 Apr 2024 22:44:34 +0500 Subject: [PATCH 003/106] Cli startup joke --- src/Cli/StartupJokeSpeechBubble.php | 31 +++++++++++++++++++++++++++++ src/Command/Run.php | 7 ++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/Cli/StartupJokeSpeechBubble.php diff --git a/src/Cli/StartupJokeSpeechBubble.php b/src/Cli/StartupJokeSpeechBubble.php new file mode 100644 index 00000000..97acc919 --- /dev/null +++ b/src/Cli/StartupJokeSpeechBubble.php @@ -0,0 +1,31 @@ +writeln(\sprintf('%s v%s', Info::NAME, Info::VERSION)); - $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); + $output->write( + StartupJokeSpeechBubble::randomStartupJokeSpeechBubble() . Info::LOGO_CLI_COLOR . "\n", + true, + OutputInterface::OUTPUT_RAW + ); /** * Prepare port listeners From ca506518bf97d25d1ae9e4066b2ff4fa7d47d5a3 Mon Sep 17 00:00:00 2001 From: l0gic Date: Mon, 29 Apr 2024 23:57:58 +0500 Subject: [PATCH 004/106] Cli startup joke v2 --- src/Cli/CliStartupSpeechBubble.php | 70 +++++++++++++++++++++++++++++ src/Cli/StartupJokeSpeechBubble.php | 31 ------------- src/Command/Run.php | 4 +- 3 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 src/Cli/CliStartupSpeechBubble.php delete mode 100644 src/Cli/StartupJokeSpeechBubble.php diff --git a/src/Cli/CliStartupSpeechBubble.php b/src/Cli/CliStartupSpeechBubble.php new file mode 100644 index 00000000..c98a1be2 --- /dev/null +++ b/src/Cli/CliStartupSpeechBubble.php @@ -0,0 +1,70 @@ + + */ + private static function getJokeLines(): array + { + $joke = self::JOKES[mt_rand(0, \count(self::JOKES) - 1)]; + $jokeLen = max(\strlen($joke), 13); + $speechBubbleLeftSpace = str_repeat(' ', 14); + + $lines = []; + $lines[] = $speechBubbleLeftSpace.'⠘⡀⠀⠀'.str_repeat(' ', $jokeLen).'⠀⠀ ⡜'; + $lines[] = $speechBubbleLeftSpace.' ⠘⡀⠀'.str_pad($joke, $jokeLen, ' ', STR_PAD_BOTH).'⠀ ⡜'; + $lines[] = $speechBubbleLeftSpace.' ⠑⡀'.str_repeat(' ', $jokeLen).'⡔⠁'; + $lines[] = $speechBubbleLeftSpace.' ⢸ '.str_repeat('⣀', $jokeLen - 13).'⣀⣀⣀⣀⡀⠤⠄⠒⠈'; + $lines[] = $speechBubbleLeftSpace.' ⠘⣀⠄⠊⠁'; + + return $lines; + } +} diff --git a/src/Cli/StartupJokeSpeechBubble.php b/src/Cli/StartupJokeSpeechBubble.php deleted file mode 100644 index 97acc919..00000000 --- a/src/Cli/StartupJokeSpeechBubble.php +++ /dev/null @@ -1,31 +0,0 @@ -writeln(\sprintf('%s v%s', Info::NAME, Info::VERSION)); $output->write( - StartupJokeSpeechBubble::randomStartupJokeSpeechBubble() . Info::LOGO_CLI_COLOR . "\n", + "\n" . CliStartupSpeechBubble::getStartupSpeechBubble() . "\n", true, OutputInterface::OUTPUT_RAW ); From 9062e5022890ab19c57f6d7f912f952f5e7a82ab Mon Sep 17 00:00:00 2001 From: l0gic Date: Tue, 30 Apr 2024 00:08:12 +0500 Subject: [PATCH 005/106] FQN for functions from global scope --- src/Cli/CliStartupSpeechBubble.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Cli/CliStartupSpeechBubble.php b/src/Cli/CliStartupSpeechBubble.php index c98a1be2..f11495a2 100644 --- a/src/Cli/CliStartupSpeechBubble.php +++ b/src/Cli/CliStartupSpeechBubble.php @@ -32,17 +32,17 @@ public static function getStartupSpeechBubble(): string $result = ''; $jokeLines = self::getJokeLines(); - $maxJokeLineLen = max(array_map('mb_strlen', $jokeLines)) + 2; + $maxJokeLineLen = \max(\array_map('\mb_strlen', $jokeLines)) + 2; foreach ($jokeLines as $line) { - $rightSpaces = str_repeat(' ', $maxJokeLineLen - mb_strlen($line)); + $rightSpaces = \str_repeat(' ', $maxJokeLineLen - \mb_strlen($line)); $result .= "\e[44;97;1m" . $line . $rightSpaces . "\e[0m\n"; } - $logoLines = explode("\n", self::LOGO); + $logoLines = \explode("\n", self::LOGO); foreach ($logoLines as $line) { - $rightSpaces = str_repeat(' ', $maxJokeLineLen - mb_strlen($line)); + $rightSpaces = \str_repeat(' ', $maxJokeLineLen - \mb_strlen($line)); $result .= "\e[44;97;1m" . $line . $rightSpaces . "\e[0m\n"; } @@ -54,15 +54,15 @@ public static function getStartupSpeechBubble(): string */ private static function getJokeLines(): array { - $joke = self::JOKES[mt_rand(0, \count(self::JOKES) - 1)]; - $jokeLen = max(\strlen($joke), 13); - $speechBubbleLeftSpace = str_repeat(' ', 14); + $joke = self::JOKES[\mt_rand(0, \count(self::JOKES) - 1)]; + $jokeLen = \max(\strlen($joke), 13); + $speechBubbleLeftSpace = \str_repeat(' ', 14); $lines = []; - $lines[] = $speechBubbleLeftSpace.'⠘⡀⠀⠀'.str_repeat(' ', $jokeLen).'⠀⠀ ⡜'; - $lines[] = $speechBubbleLeftSpace.' ⠘⡀⠀'.str_pad($joke, $jokeLen, ' ', STR_PAD_BOTH).'⠀ ⡜'; - $lines[] = $speechBubbleLeftSpace.' ⠑⡀'.str_repeat(' ', $jokeLen).'⡔⠁'; - $lines[] = $speechBubbleLeftSpace.' ⢸ '.str_repeat('⣀', $jokeLen - 13).'⣀⣀⣀⣀⡀⠤⠄⠒⠈'; + $lines[] = $speechBubbleLeftSpace.'⠘⡀⠀⠀'.\str_repeat(' ', $jokeLen).'⠀⠀ ⡜'; + $lines[] = $speechBubbleLeftSpace.' ⠘⡀⠀'.\str_pad($joke, $jokeLen, ' ', STR_PAD_BOTH).'⠀ ⡜'; + $lines[] = $speechBubbleLeftSpace.' ⠑⡀'.\str_repeat(' ', $jokeLen).'⡔⠁'; + $lines[] = $speechBubbleLeftSpace.' ⢸ '.\str_repeat('⣀', $jokeLen - 13).'⣀⣀⣀⣀⡀⠤⠄⠒⠈'; $lines[] = $speechBubbleLeftSpace.' ⠘⣀⠄⠊⠁'; return $lines; From 38eb3281fbf68a8c9c6d5e6da2f5326b7d9c6bec Mon Sep 17 00:00:00 2001 From: l0gic Date: Tue, 30 Apr 2024 00:16:30 +0500 Subject: [PATCH 006/106] Add a console command to run a joke vendor/bin/trap joke --- bin/trap | 1 + src/Command/Joke.php | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/Command/Joke.php diff --git a/bin/trap b/bin/trap index cafcc726..981ba668 100644 --- a/bin/trap +++ b/bin/trap @@ -44,6 +44,7 @@ if ('cli' !== PHP_SAPI) { $application = new Application(); $application->setCommandLoader( new FactoryCommandLoader([ + Command\Joke::getDefaultName() => static fn() => new Command\Joke(), Command\Run::getDefaultName() => static fn() => new Command\Run(), Command\Test::getDefaultName() => static fn() => new Command\Test(), ]), diff --git a/src/Command/Joke.php b/src/Command/Joke.php new file mode 100644 index 00000000..fdb48fa9 --- /dev/null +++ b/src/Command/Joke.php @@ -0,0 +1,43 @@ +write( + "\n" . CliStartupSpeechBubble::getStartupSpeechBubble() . "\n", + true, + OutputInterface::OUTPUT_RAW + ); + + return Command::SUCCESS; + } +} From f8863d3bfd319e9c2f12a06c0e826a0c9a8ba93d Mon Sep 17 00:00:00 2001 From: l0gic Date: Tue, 30 Apr 2024 00:17:10 +0500 Subject: [PATCH 007/106] Remove unused Info::LOGO_CLI_COLOR --- src/Info.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Info.php b/src/Info.php index b5f9104b..3df0bc11 100644 --- a/src/Info.php +++ b/src/Info.php @@ -11,20 +11,5 @@ class Info { public const NAME = 'Buggregator Trap'; public const VERSION = '1.4.6'; - public const LOGO_CLI_COLOR = << Date: Tue, 30 Apr 2024 00:22:21 +0500 Subject: [PATCH 008/106] Buggregator related phrase --- src/Cli/CliStartupSpeechBubble.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cli/CliStartupSpeechBubble.php b/src/Cli/CliStartupSpeechBubble.php index f11495a2..b0a9a389 100644 --- a/src/Cli/CliStartupSpeechBubble.php +++ b/src/Cli/CliStartupSpeechBubble.php @@ -25,6 +25,7 @@ class CliStartupSpeechBubble 'Two hard things in computer science: cache invalidation, naming things and stack overflow.', 'Depressive programming style through dump and die.', 'PHP was dead 84 years ago right?', + 'Submit a pull request to help us improve the Buggregator Trap codebase', ]; public static function getStartupSpeechBubble(): string From 018d28de47f729c9dc43a28e548c9992370eaee9 Mon Sep 17 00:00:00 2001 From: l0gic Date: Tue, 30 Apr 2024 08:10:21 +0500 Subject: [PATCH 009/106] Revert "Remove unused Info::LOGO_CLI_COLOR" This reverts commit f8863d3bfd319e9c2f12a06c0e826a0c9a8ba93d. --- src/Info.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Info.php b/src/Info.php index 3df0bc11..b5f9104b 100644 --- a/src/Info.php +++ b/src/Info.php @@ -11,5 +11,20 @@ class Info { public const NAME = 'Buggregator Trap'; public const VERSION = '1.4.6'; + public const LOGO_CLI_COLOR = << Date: Tue, 30 Apr 2024 08:23:44 +0500 Subject: [PATCH 010/106] remove speech bubble --- src/Cli/CliStartupSpeechBubble.php | 71 ------------------------------ src/Command/Joke.php | 13 +----- src/Command/Run.php | 10 ++--- src/Info.php | 7 +++ 4 files changed, 11 insertions(+), 90 deletions(-) delete mode 100644 src/Cli/CliStartupSpeechBubble.php diff --git a/src/Cli/CliStartupSpeechBubble.php b/src/Cli/CliStartupSpeechBubble.php deleted file mode 100644 index b0a9a389..00000000 --- a/src/Cli/CliStartupSpeechBubble.php +++ /dev/null @@ -1,71 +0,0 @@ - - */ - private static function getJokeLines(): array - { - $joke = self::JOKES[\mt_rand(0, \count(self::JOKES) - 1)]; - $jokeLen = \max(\strlen($joke), 13); - $speechBubbleLeftSpace = \str_repeat(' ', 14); - - $lines = []; - $lines[] = $speechBubbleLeftSpace.'⠘⡀⠀⠀'.\str_repeat(' ', $jokeLen).'⠀⠀ ⡜'; - $lines[] = $speechBubbleLeftSpace.' ⠘⡀⠀'.\str_pad($joke, $jokeLen, ' ', STR_PAD_BOTH).'⠀ ⡜'; - $lines[] = $speechBubbleLeftSpace.' ⠑⡀'.\str_repeat(' ', $jokeLen).'⡔⠁'; - $lines[] = $speechBubbleLeftSpace.' ⢸ '.\str_repeat('⣀', $jokeLen - 13).'⣀⣀⣀⣀⡀⠤⠄⠒⠈'; - $lines[] = $speechBubbleLeftSpace.' ⠘⣀⠄⠊⠁'; - - return $lines; - } -} diff --git a/src/Command/Joke.php b/src/Command/Joke.php index fdb48fa9..57c9f33a 100644 --- a/src/Command/Joke.php +++ b/src/Command/Joke.php @@ -4,17 +4,10 @@ namespace Buggregator\Trap\Command; -use Buggregator\Trap\Application; -use Buggregator\Trap\Config\SocketServer; -use Buggregator\Trap\Cli\CliStartupSpeechBubble; use Buggregator\Trap\Info; -use Buggregator\Trap\Logger; -use Buggregator\Trap\Sender; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Command\SignalableCommandInterface; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -32,11 +25,7 @@ protected function execute( InputInterface $input, OutputInterface $output, ): int { - $output->write( - "\n" . CliStartupSpeechBubble::getStartupSpeechBubble() . "\n", - true, - OutputInterface::OUTPUT_RAW - ); + trap(Info::JOKES[\array_rand(Info::JOKES)]); return Command::SUCCESS; } diff --git a/src/Command/Run.php b/src/Command/Run.php index 8f06b61c..0ca8d11e 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -6,7 +6,6 @@ use Buggregator\Trap\Application; use Buggregator\Trap\Config\SocketServer; -use Buggregator\Trap\Cli\CliStartupSpeechBubble; use Buggregator\Trap\Info; use Buggregator\Trap\Logger; use Buggregator\Trap\Sender; @@ -56,12 +55,9 @@ protected function execute( ): int { try { // Print intro - $output->writeln(\sprintf('%s v%s', Info::NAME, Info::VERSION)); - $output->write( - "\n" . CliStartupSpeechBubble::getStartupSpeechBubble() . "\n", - true, - OutputInterface::OUTPUT_RAW - ); + $output->writeln(\sprintf('%s v%s:', Info::NAME, Info::VERSION)); + trap(Info::JOKES[\array_rand(Info::JOKES)]); + $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); /** * Prepare port listeners diff --git a/src/Info.php b/src/Info.php index b5f9104b..e4ad1e9e 100644 --- a/src/Info.php +++ b/src/Info.php @@ -27,4 +27,11 @@ class Info CONSOLE; public const TRAP_ROOT = __DIR__ . '/..'; + public const JOKES = [ + 'Why do programmers always mix up Halloween and Christmas? Because Oct 31 == Dec 25.', + 'Two hard things in computer science: cache invalidation, naming things and stack overflow.', + 'Depressive programming style through dump and die.', + 'PHP was dead 84 years ago right?', + 'Submit a pull request to help us improve the Buggregator Trap codebase', + ]; } From bb3bf61f79ea99213bdb28b8528cf58a006dc060 Mon Sep 17 00:00:00 2001 From: Dima Jolkin Date: Tue, 30 Apr 2024 12:52:37 +0300 Subject: [PATCH 011/106] Fix psalm, clear psalm-baseline.yaml --- psalm-baseline.xml | 515 +----------------- psalm.xml | 6 + src/Sender/Console/Renderer/Smtp.php | 4 +- .../Console/Renderer/TemplateRenderer.php | 1 + src/Sender/Console/Renderer/VarDumper.php | 4 +- src/Sender/Console/Support/Common.php | 8 +- src/Sender/Console/Support/Files.php | 6 +- src/Sender/Console/Support/Tables.php | 2 +- src/Sender/ConsoleSender.php | 2 + src/Socket/Client.php | 5 + src/Socket/Server.php | 1 + src/Support/StreamHelper.php | 2 +- src/Traffic/Dispatcher/Http.php | 1 + src/Traffic/Dispatcher/Smtp.php | 3 +- src/Traffic/Message/Headers.php | 25 +- src/Traffic/Message/Multipart/Field.php | 6 +- src/Traffic/Message/Multipart/File.php | 34 +- src/Traffic/Message/Multipart/Part.php | 27 +- src/Traffic/Message/Smtp.php | 27 +- src/Traffic/Parser/Http.php | 22 +- src/Traffic/Parser/Smtp.php | 2 +- src/functions.php | 4 + 22 files changed, 174 insertions(+), 533 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 7f3ec765..a5770984 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,99 +1,49 @@ - + - $data + - - - $port - - - - $output::OUTPUT_RAW - $output::OUTPUT_RAW - - $logger + - \is_resource($fp) + - $buf === null + - $time + - - $response - - - $time - - $response + - - - $time - $time - - - $time - - - - - $payload - getAttribute('begin_at', null)]]> - getAttribute('begin_at', null)]]> - - - $payload - - - $offset - $currentPos + - - $headers - $itemHeader - - - - - - - $headers - $itemHeader - $type - - TReturn + - - method]]> - {$this->method}(...$arguments)]]> - $values - $values - $values - $values + + + + @@ -101,69 +51,14 @@ stream->getSize()]]> - int + - - request->getBody()->getSize() + \array_reduce( - $this->request->getUploadedFiles(), - static fn(int $carry, array $file): int => $carry + $file['size'], - 0 - )]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - $payload - - - int - - - - - - - request->getUploadedFiles()]]> - array + - - int - - - request->getBody()->getSize()]]> - - - - - \json_decode($payload, true, JSON_THROW_ON_ERROR) - @@ -173,47 +68,18 @@ default => throw new \InvalidArgumentException('Unknown Sentry frame type.'), }]]> - - $data - $data - - - - - - - $data - - static + - - - - $item - $item - - - $item - - - public function __construct( + - - - $payload - - - $payload - - - $payload + @@ -221,369 +87,46 @@ - Files::normalizeSize($size) + - $frame + getSize()]]> getCookieParams()]]> getQueryParams()]]> - - - - - - - $file - $file - $function + + + $output->writeln(]]> - - - - - - $row - $stacktrace - - $file - $file - $function + + + - renderMessageHeader + - - - - $message - $meta - $tags + + - - - - - - - - - payload]]> - $type - - - payload['exceptions']]]> - - - $type - - - $type - - - - - getHeaders()]]> - getHeaders()]]> - - - getClientFilename()]]> - getClientMediaType()]]> - - - - - render - - - - - - - - - - - $context - $controller - $data - $fileLink - - - - - - - $meta - - - $context - - $data - - - $controller - $describer - $fileLink - - - - - $payload - [$data, $context] - - - describe - - - dumper->dump($controller, true)]]> - dumper->dump($data, true)]]> - - - - - $item - - - - \array_keys($data) - - - $item - - - - - $size - - - - - - - - $key - - - $value - - - - - Termwind::renderUsing($output) - new HtmlRenderer() - - - Termwind::renderUsing($output) - - - - - new SplQueue() - - - - - payloadSize]]> - - - \Closure::bind($callable(...), $this) - \Closure::bind($callable(...), $this) - - - - - socket]]> - socket]]> - - - - - new \SplQueue() - - - - - getClass()]]> - $value - $value - - - getName()]]]> - getName()]]]> - - - $value - getName()]]]> - - - getDescriptorByClassName - - - - - - - - - - $value - $values - - - headers[$header]]]> - - - headers[$header]]]> - headers[$new->headerNames[$normalized]]]]> - headers[$header]]]> - headers[$header]]]> - headers[$header]]]> - - - headers[$header]]]> - - - $header - $header - $header - - - string[] - - - - - FieldDataArray - $this->value, - ]]]> - - - - - FileDataArray - $this->fileName, - 'size' => $this->getSize(), - ]]]> - - - - - new File($headers, $name, $fileName), - false => new Field($headers, $name), - }]]> - - - $headers - $headers - $headers - - - static - - - - - $addrs - protocol['BCC'] ?? []]]> - - - protocol]]> - - - $attach - - - $message - - - $attach - $message - - - >]]> - - - $attachments - $messages - - - - - $boundary - $headersBlock - read($blockEnd - $pos + 2)]]> - - - $parts - $result - - - >]]> - array{0: non-empty-string, 1: non-empty-string, 2: non-empty-string} - - - getSize()]]> - - - $value - $value - - - - - $boundary - $headerBlock - - - - - AbstractCloner::$defaultCasters[EnumValue::class] - AbstractCloner::$defaultCasters[MapField::class] - AbstractCloner::$defaultCasters[Message::class] - AbstractCloner::$defaultCasters[RepeatedField::class] - - - AbstractCloner::$defaultCasters[EnumValue::class] - AbstractCloner::$defaultCasters[MapField::class] - AbstractCloner::$defaultCasters[Message::class] - AbstractCloner::$defaultCasters[RepeatedField::class] - - - $value - AbstractCloner::$defaultCasters[EnumValue::class] - AbstractCloner::$defaultCasters[MapField::class] - AbstractCloner::$defaultCasters[Message::class] - AbstractCloner::$defaultCasters[RepeatedField::class] - diff --git a/psalm.xml b/psalm.xml index 1e24d848..14f9f935 100644 --- a/psalm.xml +++ b/psalm.xml @@ -11,6 +11,12 @@ + + + + + + diff --git a/src/Sender/Console/Renderer/Smtp.php b/src/Sender/Console/Renderer/Smtp.php index 9f48a8ea..5ef8eb0b 100644 --- a/src/Sender/Console/Renderer/Smtp.php +++ b/src/Sender/Console/Renderer/Smtp.php @@ -58,9 +58,9 @@ public function render(OutputInterface $output, Frame $frame): void foreach ($message->getAttachments() as $attach) { Files::renderFile( $output, - $attach->getClientFilename(), + $attach->getClientFilename() ?? 'undefined', $attach->getSize(), - $attach->getClientMediaType(), + $attach->getClientMediaType() ?? 'undefined', ); } $output->writeln(''); diff --git a/src/Sender/Console/Renderer/TemplateRenderer.php b/src/Sender/Console/Renderer/TemplateRenderer.php index 63b0800e..d0b98bff 100644 --- a/src/Sender/Console/Renderer/TemplateRenderer.php +++ b/src/Sender/Console/Renderer/TemplateRenderer.php @@ -20,6 +20,7 @@ public function __construct( public function render(string $template, array $data = []): void { + /** @psalm-suppress InternalMethod */ $this->renderer->render( $this->templateEngine->render($template, $data), 0 diff --git a/src/Sender/Console/Renderer/VarDumper.php b/src/Sender/Console/Renderer/VarDumper.php index 868e07ae..07f8f512 100644 --- a/src/Sender/Console/Renderer/VarDumper.php +++ b/src/Sender/Console/Renderer/VarDumper.php @@ -94,7 +94,7 @@ public function describe(OutputInterface $output, Data $data, array $context, in empty($request['method'] ?? '') or $meta['Method'] = $request['method']; empty($request['uri'] ?? '') or $meta['URI'] = $request['uri']; if ($controller = $request['controller']) { - $meta['Controller'] = rtrim($this->dumper->dump($controller, true), "\n"); + $meta['Controller'] = rtrim((string) $this->dumper->dump($controller, true), "\n"); } } elseif (isset($context['cli'])) { $meta['Command'] = $context['cli']['command_line']; @@ -107,7 +107,7 @@ public function describe(OutputInterface $output, Data $data, array $context, in Common::renderMetadata($output, $meta); $output->writeln(''); - $output->write($this->dumper->dump($data, true), true, OutputInterface::OUTPUT_RAW); + $output->write((string) $this->dumper->dump($data, true), true, OutputInterface::OUTPUT_RAW); } }; } diff --git a/src/Sender/Console/Support/Common.php b/src/Sender/Console/Support/Common.php index 21695e7d..c0355608 100644 --- a/src/Sender/Console/Support/Common.php +++ b/src/Sender/Console/Support/Common.php @@ -26,12 +26,12 @@ public static function renderHeader1(OutputInterface $output, string $title, ?st $output->writeln(['', \implode('', $parts), '']); } - public static function renderHeader2(OutputInterface $output, string $title, string ...$sub): void + public static function renderHeader2(OutputInterface $output, string $title, ?string ...$sub): void { $parts = ["# $title "]; foreach ($sub as $color => $value) { $color = \is_string($color) ? $color : 'gray'; - $parts[] = \sprintf(' %s ', $color, $value); + $parts[] = \sprintf(' %s ', $color, $value ?? 'NULL'); } $output->writeln(['', \implode('', $parts), '']); @@ -53,13 +53,13 @@ public static function renderHighlightedLine(OutputInterface $output, string $li } /** - * @param array $data + * @param array $data */ public static function renderMetadata(OutputInterface $output, array $data): void { + /** @psalm-suppress ArgumentTypeCoercion */ $maxHeaderLength = \max(\array_map('strlen', \array_keys($data))); - /** @var mixed $value */ foreach ($data as $head => $value) { // Align headers to the right self::renderHeader( diff --git a/src/Sender/Console/Support/Files.php b/src/Sender/Console/Support/Files.php index 387e5376..180c38cd 100644 --- a/src/Sender/Console/Support/Files.php +++ b/src/Sender/Console/Support/Files.php @@ -13,6 +13,9 @@ final class Files { /** + * + * @param non-negative-int|null $size + * * Render file info. Example: * ┌───┐ logo.ico * │ico│ 20.06 KiB @@ -24,6 +27,7 @@ final class Files * │ │ any value <= positional argument * │ico│ unknown size * └───┘ image/x-icon + * */ public static function renderFile( OutputInterface $output, @@ -33,7 +37,7 @@ public static function renderFile( string ...$additional ): void { // File extension - $ex = \substr($fileName, \strrpos($fileName, '.') + 1); + $ex = \substr($fileName, (int) \strrpos($fileName, '.') + 1); $ex = \strlen($ex) > 3 ? ' ' : \str_pad($ex, 3, ' ', \STR_PAD_BOTH); // File size diff --git a/src/Sender/Console/Support/Tables.php b/src/Sender/Console/Support/Tables.php index 80f99f7c..987bd5ed 100644 --- a/src/Sender/Console/Support/Tables.php +++ b/src/Sender/Console/Support/Tables.php @@ -23,7 +23,7 @@ public static function renderKeyValueTable(OutputInterface $output, string $titl return; } - $keyLength = \max(\array_map(static fn($key) => \strlen($key), \array_keys($data))); + $keyLength = \max(\array_map(static fn($key) => \strlen((string) $key), \array_keys($data))); $valueLength = \max(1, (new Terminal())->getWidth() - 7 - $keyLength); $table->setRows([...(static function (array $data) use ($valueLength): iterable { diff --git a/src/Sender/ConsoleSender.php b/src/Sender/ConsoleSender.php index 1d626a1a..58cdb975 100644 --- a/src/Sender/ConsoleSender.php +++ b/src/Sender/ConsoleSender.php @@ -21,8 +21,10 @@ final class ConsoleSender implements Sender { public static function create(OutputInterface $output): self { + /** @psalm-suppress InternalMethod, InternalClass */ Termwind::renderUsing($output); + /** @psalm-suppress InternalClass */ $templateRenderer = new TemplateRenderer( new HtmlRenderer(), new TemplateEngine(Info::TRAP_ROOT . '/resources/templates') diff --git a/src/Socket/Client.php b/src/Socket/Client.php index 2594393a..721e47a8 100644 --- a/src/Socket/Client.php +++ b/src/Socket/Client.php @@ -26,6 +26,9 @@ final class Client implements Destroyable private \Closure $onPayload; private \Closure $onClose; + /** + * @param positive-int $payloadSize + */ private function __construct( private readonly \Socket $socket, private readonly int $payloadSize, @@ -117,6 +120,7 @@ protected function onInit(): void */ public function setOnPayload(callable $callable): void { + /** @psalm-suppress PossiblyNullPropertyAssignmentValue */ $this->onPayload = \Closure::bind($callable(...), $this); } @@ -126,6 +130,7 @@ public function setOnPayload(callable $callable): void */ public function setOnClose(callable $callable): void { + /** @psalm-suppress PossiblyNullPropertyAssignmentValue */ $this->onClose = \Closure::bind($callable(...), $this); } diff --git a/src/Socket/Server.php b/src/Socket/Server.php index 73c1ae50..f69276e6 100644 --- a/src/Socket/Server.php +++ b/src/Socket/Server.php @@ -92,6 +92,7 @@ public static function init( public function process(): void { + /** @psalm-suppress PossiblyInvalidArgument */ while (!$this->cancelled and false !== ($socket = \socket_accept($this->socket))) { $client = null; try { diff --git a/src/Support/StreamHelper.php b/src/Support/StreamHelper.php index e6190296..ae06e1c5 100644 --- a/src/Support/StreamHelper.php +++ b/src/Support/StreamHelper.php @@ -67,7 +67,7 @@ public static function strpos(StreamInterface $stream, string $substr): int|fals * * @param non-empty-string $boundary * - * @return int Bytes written + * @return non-negative-int Bytes written */ public static function writeStreamUntil(StreamInterface $from, StreamInterface $to, string $boundary): int { diff --git a/src/Traffic/Dispatcher/Http.php b/src/Traffic/Dispatcher/Http.php index 8e7fa06a..3e0fa6f9 100644 --- a/src/Traffic/Dispatcher/Http.php +++ b/src/Traffic/Dispatcher/Http.php @@ -47,6 +47,7 @@ public function __construct( $handlers[] = new Fallback($middlewares); // Build pipeline of handlers. + /** @psalm-suppress MixedPropertyTypeCoercion */ $this->pipeline = Pipeline::build( $handlers, /** @see RequestHandler::handle() */ diff --git a/src/Traffic/Dispatcher/Smtp.php b/src/Traffic/Dispatcher/Smtp.php index f3525c8d..7dbe286c 100644 --- a/src/Traffic/Dispatcher/Smtp.php +++ b/src/Traffic/Dispatcher/Smtp.php @@ -31,7 +31,6 @@ public function __construct( public function dispatch(StreamClient $stream): iterable { $stream->sendData($this->createResponse(self::READY, 'mailamie')); - $protocol = []; $message = null; @@ -40,9 +39,11 @@ public function dispatch(StreamClient $stream): iterable if (\preg_match('/^(?:EHLO|HELO)/', $response)) { $stream->sendData($this->createResponse(self::OK)); } elseif (\preg_match('/^MAIL FROM:\s*<(.*)>/', $response, $matches)) { + /** @var array{1: non-empty-string} $matches */ $protocol['FROM'][] = $matches[1]; $stream->sendData($this->createResponse(self::OK)); } elseif (\preg_match('/^RCPT TO:\s*<(.*)>/', $response, $matches)) { + /** @var array{1: non-empty-string} $matches */ $protocol['BCC'][] = $matches[1]; $stream->sendData($this->createResponse(self::OK)); } elseif (\str_starts_with($response, 'QUIT')) { diff --git a/src/Traffic/Message/Headers.php b/src/Traffic/Message/Headers.php index 3be3a37f..a906baed 100644 --- a/src/Traffic/Message/Headers.php +++ b/src/Traffic/Message/Headers.php @@ -9,12 +9,15 @@ */ trait Headers { - /** @var array Map of all registered headers, as original name => array of values */ + /** @var array> Map of all registered headers, as original name => array of values */ private array $headers = []; - /** @var array Map of lowercase header name => original name at registration */ + /** @var array Map of lowercase header name => original name at registration */ private array $headerNames = []; + /** + * @return array> + */ public function getHeaders(): array { return $this->headers; @@ -26,7 +29,7 @@ public function hasHeader(string $header): bool } /** - * @return string[] + * @return list */ public function getHeader(string $header): array { @@ -45,9 +48,10 @@ public function getHeaderLine(string $header): string return \implode(', ', $this->getHeader($header)); } - public function withHeader(string $header, $value): static + public function withHeader(string $header, mixed $value): static { $value = $this->validateAndTrimHeader($header, $value); + /** @var non-empty-string $normalized */ $normalized = \strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); $new = clone $this; @@ -87,7 +91,7 @@ public function withoutHeader(string $header): static } /** - * @param array> $headers + * @param array> $headers */ private function setHeaders(array $headers): void { @@ -98,6 +102,7 @@ private function setHeaders(array $headers): void $header = (string)$header; } $value = $this->validateAndTrimHeader($header, $value); + /** @var non-empty-string $normalized */ $normalized = \strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); if (isset($this->headerNames[$normalized])) { $header = $this->headerNames[$normalized]; @@ -126,8 +131,12 @@ private function setHeaders(array $headers): void * field-value = *( ( %x21-7E / %x80-FF ) [ 1*( SP / HTAB ) ( %x21-7E / %x80-FF ) ] ) * * @see https://tools.ietf.org/html/rfc7230#section-3.2.4 + * + * @psalm-assert non-empty-string $header + * + * @return non-empty-list */ - private function validateAndTrimHeader(string $header, $values): array + private function validateAndTrimHeader(string $header, mixed $values): array { if (1 !== \preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@D", $header)) { throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string'); @@ -170,10 +179,10 @@ private function validateAndTrimHeader(string $header, $values): array /** * List of header values. * - * @param array> $headers + * @param array> $headers * @param non-empty-string $header * - * @return list + * @return list */ private static function findHeader(array $headers, string $header): array { diff --git a/src/Traffic/Message/Multipart/Field.php b/src/Traffic/Message/Multipart/Field.php index 3ceb14fd..ce13a111 100644 --- a/src/Traffic/Message/Multipart/Field.php +++ b/src/Traffic/Message/Multipart/Field.php @@ -6,7 +6,7 @@ /** * @psalm-type FieldDataArray = array{ - * headers: array>, + * headers: array>, * name?: string, * value: string * } @@ -15,6 +15,9 @@ */ final class Field extends Part { + /** + * @param array> $headers + */ public function __construct(array $headers, ?string $name = null, private string $value = '') { parent::__construct($headers, $name); @@ -30,6 +33,7 @@ public static function fromArray(array $data): self /** * @return FieldDataArray + * @psalm-suppress ImplementedReturnTypeMismatch */ public function jsonSerialize(): array { diff --git a/src/Traffic/Message/Multipart/File.php b/src/Traffic/Message/Multipart/File.php index 890a71f4..21f15036 100644 --- a/src/Traffic/Message/Multipart/File.php +++ b/src/Traffic/Message/Multipart/File.php @@ -10,10 +10,10 @@ /** * @psalm-type FileDataArray = array{ - * headers: array>, + * headers: array>, * name?: string, - * fileName: string, - * size?: int + * fileName?: string, + * size?: non-negative-int * } * * @internal @@ -21,8 +21,12 @@ final class File extends Part implements UploadedFileInterface { private ?UploadedFileInterface $uploadedFile = null; + /** @var non-negative-int|null */ private ?int $fileSize = null; + /** + * @param array> $headers + */ public function __construct(array $headers, ?string $name = null, private ?string $fileName = null) { parent::__construct($headers, $name); @@ -33,24 +37,35 @@ public function __construct(array $headers, ?string $name = null, private ?strin */ public static function fromArray(array $data): self { - $self = new self($data['headers'], $data['name'] ?? null, $data['fileName']); + $self = new self($data['headers'], $data['name'] ?? null, $data['fileName'] ?? null); $self->fileSize = $data['size'] ?? null; return $self; } /** * @return FileDataArray + * + * @psalm-suppress ImplementedReturnTypeMismatch */ public function jsonSerialize(): array { - return parent::jsonSerialize() + [ - 'fileName' => $this->fileName, - 'size' => $this->getSize(), - ]; + $data = parent::jsonSerialize(); + if ($this->fileName !== null) { + $data['fileName'] = $this->fileName; + } + if ($this->fileSize !== null) { + $data['size'] = $this->fileSize; + } + + return $data; } + /** + * @param non-negative-int|null $size + */ public function setStream(StreamInterface $stream, ?int $size = null, int $code = \UPLOAD_ERR_OK): void { + /** @psalm-suppress PropertyTypeCoercion */ $this->fileSize = $size ?? $stream->getSize() ?? null; $this->uploadedFile = new UploadedFile( $stream, @@ -83,6 +98,9 @@ public function moveTo(string $targetPath): void $this->getUploadedFile()->moveTo($targetPath); } + /** + * @return non-negative-int|null + */ public function getSize(): ?int { return $this->fileSize; diff --git a/src/Traffic/Message/Multipart/Part.php b/src/Traffic/Message/Multipart/Part.php index 8db1fb02..dd9b9d9d 100644 --- a/src/Traffic/Message/Multipart/Part.php +++ b/src/Traffic/Message/Multipart/Part.php @@ -6,6 +6,7 @@ use Buggregator\Trap\Traffic\Message\Headers; use JsonSerializable; +use RuntimeExceptio; use RuntimeException; /** @@ -15,6 +16,9 @@ abstract class Part implements JsonSerializable { use Headers; + /** + * @param array> $headers + */ protected function __construct( array $headers, protected ?string $name, @@ -22,7 +26,10 @@ protected function __construct( $this->setHeaders($headers); } - public static function create(array $headers): static + /** + * @param array> $headers + */ + public static function create(array $headers): Part { /** * Check Content-Disposition header @@ -31,6 +38,9 @@ public static function create(array $headers): static */ $contentDisposition = self::findHeader($headers, 'Content-Disposition')[0] ?? throw new RuntimeException('Missing Content-Disposition header.'); + if ($contentDisposition === '') { + throw new RuntimeException('Missing Content-Disposition header, can\'t be empty'); + } // Get field name and file name $name = \preg_match('/\bname=(?:(?[^" ;,]++)|"(?[^"]++)")/', $contentDisposition, $matches) === 1 @@ -63,11 +73,22 @@ public function withName(?string $name): static return $clone; } + /** + * @return array{ + * headers: array>, + * name?: string, + * } + */ public function jsonSerialize(): array { - return [ - 'name' => $this->name, + $data = [ 'headers' => $this->headers, ]; + + if ($this->name !== null) { + $data['name'] = $this->name; + } + + return $data; } } diff --git a/src/Traffic/Message/Smtp.php b/src/Traffic/Message/Smtp.php index 12d9c3e5..00bcf09e 100644 --- a/src/Traffic/Message/Smtp.php +++ b/src/Traffic/Message/Smtp.php @@ -6,6 +6,7 @@ use Buggregator\Trap\Traffic\Message\Multipart\Field; use Buggregator\Trap\Traffic\Message\Multipart\File; +use Buggregator\Trap\Traffic\Message\Multipart\Part; use Buggregator\Trap\Traffic\Message\Smtp\Contact; use Buggregator\Trap\Traffic\Message\Smtp\MessageFormat; use JsonSerializable; @@ -31,8 +32,8 @@ final class Smtp implements JsonSerializable private array $attachments = []; /** - * @param array> $protocol - * @param array> $headers + * @param array> $protocol + * @param array> $headers */ private function __construct( private readonly array $protocol, @@ -42,14 +43,22 @@ private function __construct( } /** - * @param array> $protocol - * @param array> $headers + * @param array> $protocol + * @param array> $headers */ public static function create(array $protocol, array $headers): self { return new self($protocol, $headers); } + /** + * @param array{ + * protocol: array>, + * headers: array>, + * messages: list, + * attachments: list, + * } $data + */ public static function fromArray(array $data): self { $self = new self($data['protocol'], $data['headers']); @@ -74,7 +83,7 @@ public function jsonSerialize(): array } /** - * @return Field[] + * @return list */ public function getMessages(): array { @@ -82,7 +91,7 @@ public function getMessages(): array } /** - * @return File[] + * @return list */ public function getAttachments(): array { @@ -90,7 +99,7 @@ public function getAttachments(): array } /** - * @param Field[] $messages + * @param list $messages */ public function withMessages(array $messages): self { @@ -100,7 +109,7 @@ public function withMessages(array $messages): self } /** - * @param File[] $attachments + * @param list $attachments */ public function withAttachments(array $attachments): self { @@ -110,7 +119,7 @@ public function withAttachments(array $attachments): self } /** - * @return array> + * @return array> */ public function getProtocol(): array { diff --git a/src/Traffic/Parser/Http.php b/src/Traffic/Parser/Http.php index 147f4afb..2bec87a1 100644 --- a/src/Traffic/Parser/Http.php +++ b/src/Traffic/Parser/Http.php @@ -64,8 +64,13 @@ public function parseStream(StreamClient $stream): ServerRequestInterface $rawCookies = \explode(';', $request->getHeaderLine('Cookie')); $cookies = []; foreach ($rawCookies as $cookie) { - [$name, $value] = \explode('=', \trim($cookie), 2); - $cookies[$name] = $value; + if (!str_contains($cookie, '=')) { + /** @psalm-suppress PossiblyUndefinedArrayOffset */ + [$name, $value] = \explode('=', \trim($cookie), 2); + $cookies[$name] = $value; + } else { + throw new RuntimeException('Invalid cookie: ' . $cookie); + } } $request = $request->withCookieParams($cookies); @@ -77,7 +82,7 @@ public function parseStream(StreamClient $stream): ServerRequestInterface /** * @param string $line * - * @return array{0: non-empty-string, 1: non-empty-string, 2: non-empty-string} + * @return list{non-empty-string, non-empty-string, non-empty-string} */ private function parseFirstLine(string $line): array { @@ -85,7 +90,11 @@ private function parseFirstLine(string $line): array if (\count($parts) !== 3) { throw new \InvalidArgumentException('Invalid first line.'); } + $parts[2] = \explode('/', $parts[2], 2)[1] ?? $parts[2]; + if ($parts[0] === '' || $parts[1] === '' || $parts[2] === '') { + throw new \InvalidArgumentException('Invalid first line.'); + } return $parts; } @@ -143,8 +152,11 @@ private function processMultipartForm(ServerRequestInterface $request): ServerRe if (\preg_match('/boundary="?([^"\\s;]++)"?/', $request->getHeaderLine('Content-Type'), $matches) !== 1) { return $request; } - $boundary = $matches[1]; + if ($boundary === '') { + throw new \Exception('Boundary can\'t be empty'); + } + $parts = self::parseMultipartBody($request->getBody(), $boundary); $uploadedFiles = $parsedBody = []; foreach ($parts as $part) { @@ -159,7 +171,7 @@ private function processMultipartForm(ServerRequestInterface $request): ServerRe if ($part instanceof File) { $uploadedFiles[$name][] = new UploadedFile( $part->getStream(), - $part->getSize(), + (int) $part->getSize(), $part->getError(), $part->getClientFilename(), $part->getClientMediaType(), diff --git a/src/Traffic/Parser/Smtp.php b/src/Traffic/Parser/Smtp.php index 5a915c79..55673453 100644 --- a/src/Traffic/Parser/Smtp.php +++ b/src/Traffic/Parser/Smtp.php @@ -18,7 +18,7 @@ final class Smtp { /** - * @param array> $protocol + * @param array> $protocol */ public function parseStream(array $protocol, StreamClient $stream): Message\Smtp { diff --git a/src/functions.php b/src/functions.php index 713171b9..0566d2f5 100644 --- a/src/functions.php +++ b/src/functions.php @@ -30,9 +30,13 @@ function trap(mixed ...$values): TrapHandle * Register the var-dump caster for protobuf messages */ if (\class_exists(AbstractCloner::class)) { + /** @psalm-suppress MixedAssignment */ AbstractCloner::$defaultCasters[Message::class] ??= [ProtobufCaster::class, 'cast']; + /** @psalm-suppress MixedAssignment */ AbstractCloner::$defaultCasters[RepeatedField::class] ??= [ProtobufCaster::class, 'castRepeated']; + /** @psalm-suppress MixedAssignment */ AbstractCloner::$defaultCasters[MapField::class] ??= [ProtobufCaster::class, 'castMap']; + /** @psalm-suppress MixedAssignment */ AbstractCloner::$defaultCasters[EnumValue::class] ??= [ProtobufCaster::class, 'castEnum']; } From 442709505f0d0ee308c144c2414bd4215b135532 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Tue, 30 Apr 2024 19:02:20 +0400 Subject: [PATCH 012/106] Fix few psalm types --- src/Proto/Frame/Smtp.php | 2 ++ .../Console/Renderer/SentryEnvelope.php | 2 +- src/Sender/Console/Renderer/Smtp.php | 4 +-- src/Sender/Console/Renderer/VarDumper.php | 4 +-- src/Sender/Console/Support/Common.php | 16 ++++++--- src/Sender/Console/Support/Files.php | 8 ++--- src/Socket/Client.php | 10 +++--- src/Socket/Server.php | 11 +++---- src/Support/StreamHelper.php | 2 +- src/Traffic/Dispatcher/Http.php | 3 +- src/Traffic/Dispatcher/Smtp.php | 4 +-- src/Traffic/Message/Headers.php | 5 +-- src/Traffic/Message/Multipart/Field.php | 2 +- src/Traffic/Message/Multipart/File.php | 12 +++---- src/Traffic/Message/Multipart/Part.php | 6 ++-- src/Traffic/Message/Smtp.php | 24 +++++++------- src/Traffic/Parser/Http.php | 12 +++---- src/Traffic/Parser/Smtp.php | 2 +- tests/Unit/Traffic/Parser/HttpParserTest.php | 33 +++++++++++++++++++ 19 files changed, 96 insertions(+), 66 deletions(-) diff --git a/src/Proto/Frame/Smtp.php b/src/Proto/Frame/Smtp.php index ae4a2229..b460f24f 100644 --- a/src/Proto/Frame/Smtp.php +++ b/src/Proto/Frame/Smtp.php @@ -14,6 +14,7 @@ /** * @internal * @psalm-internal Buggregator + * @psalm-import-type TArrayData from Message\Smtp */ final class Smtp extends Frame implements FilesCarrier { @@ -34,6 +35,7 @@ public function __toString(): string public static function fromString(string $payload, DateTimeImmutable $time): static { + /** @var TArrayData $payload */ $payload = \json_decode($payload, true, \JSON_THROW_ON_ERROR); $message = Message\Smtp::fromArray($payload); diff --git a/src/Sender/Console/Renderer/SentryEnvelope.php b/src/Sender/Console/Renderer/SentryEnvelope.php index 68b4783b..5b7c169a 100644 --- a/src/Sender/Console/Renderer/SentryEnvelope.php +++ b/src/Sender/Console/Renderer/SentryEnvelope.php @@ -36,7 +36,7 @@ public function render(OutputInterface $output, Frame $frame): void ++$i; try { $type = $item->headers['type'] ?? null; - Common::renderHeader2($output, "Item $i", green: $type); + Common::renderHeader2($output, "Item $i", green: (string)$type); Header::renderMessageHeader($output, $item->payload); $this->renderItem($output, $item); diff --git a/src/Sender/Console/Renderer/Smtp.php b/src/Sender/Console/Renderer/Smtp.php index 5ef8eb0b..430d9519 100644 --- a/src/Sender/Console/Renderer/Smtp.php +++ b/src/Sender/Console/Renderer/Smtp.php @@ -58,9 +58,9 @@ public function render(OutputInterface $output, Frame $frame): void foreach ($message->getAttachments() as $attach) { Files::renderFile( $output, - $attach->getClientFilename() ?? 'undefined', + $attach->getClientFilename() ?? '', $attach->getSize(), - $attach->getClientMediaType() ?? 'undefined', + $attach->getClientMediaType() ?? '', ); } $output->writeln(''); diff --git a/src/Sender/Console/Renderer/VarDumper.php b/src/Sender/Console/Renderer/VarDumper.php index 07f8f512..11edf05a 100644 --- a/src/Sender/Console/Renderer/VarDumper.php +++ b/src/Sender/Console/Renderer/VarDumper.php @@ -58,7 +58,7 @@ public function __construct( } /** - * @psalm-suppress RiskyTruthyFalsyComparison + * @psalm-suppress RiskyTruthyFalsyComparison, MixedArrayAccess, MixedArgument */ public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void { @@ -94,7 +94,7 @@ public function describe(OutputInterface $output, Data $data, array $context, in empty($request['method'] ?? '') or $meta['Method'] = $request['method']; empty($request['uri'] ?? '') or $meta['URI'] = $request['uri']; if ($controller = $request['controller']) { - $meta['Controller'] = rtrim((string) $this->dumper->dump($controller, true), "\n"); + $meta['Controller'] = \rtrim((string) $this->dumper->dump($controller, true), "\n"); } } elseif (isset($context['cli'])) { $meta['Command'] = $context['cli']['command_line']; diff --git a/src/Sender/Console/Support/Common.php b/src/Sender/Console/Support/Common.php index c0355608..83adf92c 100644 --- a/src/Sender/Console/Support/Common.php +++ b/src/Sender/Console/Support/Common.php @@ -26,12 +26,16 @@ public static function renderHeader1(OutputInterface $output, string $title, ?st $output->writeln(['', \implode('', $parts), '']); } - public static function renderHeader2(OutputInterface $output, string $title, ?string ...$sub): void + public static function renderHeader2(OutputInterface $output, string $title, string ...$sub): void { $parts = ["# $title "]; foreach ($sub as $color => $value) { + if ($value === '') { + continue; + } + $color = \is_string($color) ? $color : 'gray'; - $parts[] = \sprintf(' %s ', $color, $value ?? 'NULL'); + $parts[] = \sprintf(' %s ', $color, $value); } $output->writeln(['', \implode('', $parts), '']); @@ -53,12 +57,14 @@ public static function renderHighlightedLine(OutputInterface $output, string $li } /** - * @param array $data + * @param array $data */ public static function renderMetadata(OutputInterface $output, array $data): void { - /** @psalm-suppress ArgumentTypeCoercion */ - $maxHeaderLength = \max(\array_map('strlen', \array_keys($data))); + $maxHeaderLength = \max(0, ...\array_map( + static fn(string|int $key): int => \strlen((string) $key), + \array_keys($data)), + ); foreach ($data as $head => $value) { // Align headers to the right diff --git a/src/Sender/Console/Support/Files.php b/src/Sender/Console/Support/Files.php index 180c38cd..800d791b 100644 --- a/src/Sender/Console/Support/Files.php +++ b/src/Sender/Console/Support/Files.php @@ -13,7 +13,6 @@ final class Files { /** - * * @param non-negative-int|null $size * * Render file info. Example: @@ -27,7 +26,6 @@ final class Files * │ │ any value <= positional argument * │ico│ unknown size * └───┘ image/x-icon - * */ public static function renderFile( OutputInterface $output, @@ -37,8 +35,10 @@ public static function renderFile( string ...$additional ): void { // File extension - $ex = \substr($fileName, (int) \strrpos($fileName, '.') + 1); - $ex = \strlen($ex) > 3 ? ' ' : \str_pad($ex, 3, ' ', \STR_PAD_BOTH); + $dotPos = \strrpos($fileName, '.'); + $ex = $dotPos === false || \strlen($fileName) - $dotPos > 4 + ? ' ' + : \str_pad(\substr($fileName, $dotPos + 1), 3, ' ', \STR_PAD_BOTH); // File size $sizeStr = self::normalizeSize($size) ?? 'unknown size'; diff --git a/src/Socket/Client.php b/src/Socket/Client.php index 721e47a8..494eba8c 100644 --- a/src/Socket/Client.php +++ b/src/Socket/Client.php @@ -115,23 +115,21 @@ protected function onInit(): void } /** - * @param callable(string): void $callable Non-static callable. + * @param callable(string): void $callable If non-static callable, it will be bound to the current instance. * @psalm-assert callable(string): void $callable */ public function setOnPayload(callable $callable): void { - /** @psalm-suppress PossiblyNullPropertyAssignmentValue */ - $this->onPayload = \Closure::bind($callable(...), $this); + $this->onPayload = \Closure::bind($callable(...), $this) ?? $callable(...); } /** - * @param callable(): void $callable Non-static callable. + * @param callable(): void $callable If non-static callable, it will be bound to the current instance. * @psalm-assert callable(): void $callable */ public function setOnClose(callable $callable): void { - /** @psalm-suppress PossiblyNullPropertyAssignmentValue */ - $this->onClose = \Closure::bind($callable(...), $this); + $this->onClose = \Closure::bind($callable(...), $this) ?? $callable(...); } public function send(string $payload): void diff --git a/src/Socket/Server.php b/src/Socket/Server.php index f69276e6..994c5113 100644 --- a/src/Socket/Server.php +++ b/src/Socket/Server.php @@ -20,8 +20,7 @@ */ final class Server implements Processable, Cancellable, Destroyable { - /** @var false|resource|Socket */ - private $socket; + private Socket $socket; /** @var array */ private array $clients = []; @@ -41,13 +40,11 @@ private function __construct( private readonly ?Closure $clientInflector, private readonly Logger $logger, ) { - $this->socket = @\socket_create_listen($port); + $this->socket = @\socket_create_listen($port) ?: throw new \RuntimeException('Socket create failed.'); + /** @link https://github.com/buggregator/trap/pull/14 */ // \socket_set_option($this->socket, \SOL_SOCKET, \SO_LINGER, ['l_linger' => 0, 'l_onoff' => 1]); - if ($this->socket === false) { - throw new \RuntimeException('Socket create failed.'); - } \socket_set_nonblock($this->socket); $logger->status('Application', 'Server started on 127.0.0.1:%s', $port); @@ -92,7 +89,7 @@ public static function init( public function process(): void { - /** @psalm-suppress PossiblyInvalidArgument */ + // /** @psalm-suppress PossiblyInvalidArgument */ while (!$this->cancelled and false !== ($socket = \socket_accept($this->socket))) { $client = null; try { diff --git a/src/Support/StreamHelper.php b/src/Support/StreamHelper.php index ae06e1c5..e937a24d 100644 --- a/src/Support/StreamHelper.php +++ b/src/Support/StreamHelper.php @@ -67,7 +67,7 @@ public static function strpos(StreamInterface $stream, string $substr): int|fals * * @param non-empty-string $boundary * - * @return non-negative-int Bytes written + * @return int<0, max> Bytes written */ public static function writeStreamUntil(StreamInterface $from, StreamInterface $to, string $boundary): int { diff --git a/src/Traffic/Dispatcher/Http.php b/src/Traffic/Dispatcher/Http.php index 3e0fa6f9..d06361f0 100644 --- a/src/Traffic/Dispatcher/Http.php +++ b/src/Traffic/Dispatcher/Http.php @@ -47,13 +47,12 @@ public function __construct( $handlers[] = new Fallback($middlewares); // Build pipeline of handlers. - /** @psalm-suppress MixedPropertyTypeCoercion */ $this->pipeline = Pipeline::build( $handlers, /** @see RequestHandler::handle() */ 'handle', static function (): never { throw new \LogicException('No handler found for request.'); }, - Generator::class, + 'never', ); } diff --git a/src/Traffic/Dispatcher/Smtp.php b/src/Traffic/Dispatcher/Smtp.php index 7dbe286c..f670a3ad 100644 --- a/src/Traffic/Dispatcher/Smtp.php +++ b/src/Traffic/Dispatcher/Smtp.php @@ -39,11 +39,11 @@ public function dispatch(StreamClient $stream): iterable if (\preg_match('/^(?:EHLO|HELO)/', $response)) { $stream->sendData($this->createResponse(self::OK)); } elseif (\preg_match('/^MAIL FROM:\s*<(.*)>/', $response, $matches)) { - /** @var array{1: non-empty-string} $matches */ + /** @var array{0: non-empty-string, 1: string} $matches */ $protocol['FROM'][] = $matches[1]; $stream->sendData($this->createResponse(self::OK)); } elseif (\preg_match('/^RCPT TO:\s*<(.*)>/', $response, $matches)) { - /** @var array{1: non-empty-string} $matches */ + /** @var array{0: non-empty-string, 1: string} $matches */ $protocol['BCC'][] = $matches[1]; $stream->sendData($this->createResponse(self::OK)); } elseif (\str_starts_with($response, 'QUIT')) { diff --git a/src/Traffic/Message/Headers.php b/src/Traffic/Message/Headers.php index a906baed..4fc09473 100644 --- a/src/Traffic/Message/Headers.php +++ b/src/Traffic/Message/Headers.php @@ -9,7 +9,7 @@ */ trait Headers { - /** @var array> Map of all registered headers, as original name => array of values */ + /** @var array> Map of all registered headers */ private array $headers = []; /** @var array Map of lowercase header name => original name at registration */ @@ -91,7 +91,7 @@ public function withoutHeader(string $header): static } /** - * @param array> $headers + * @param array> $headers */ private function setHeaders(array $headers): void { @@ -101,6 +101,7 @@ private function setHeaders(array $headers): void // We must cast it back to a string in order to comply with validation. $header = (string)$header; } + $value = $this->validateAndTrimHeader($header, $value); /** @var non-empty-string $normalized */ $normalized = \strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); diff --git a/src/Traffic/Message/Multipart/Field.php b/src/Traffic/Message/Multipart/Field.php index ce13a111..e85f2bcc 100644 --- a/src/Traffic/Message/Multipart/Field.php +++ b/src/Traffic/Message/Multipart/Field.php @@ -16,7 +16,7 @@ final class Field extends Part { /** - * @param array> $headers + * @param array> $headers */ public function __construct(array $headers, ?string $name = null, private string $value = '') { diff --git a/src/Traffic/Message/Multipart/File.php b/src/Traffic/Message/Multipart/File.php index 21f15036..c0f8cb02 100644 --- a/src/Traffic/Message/Multipart/File.php +++ b/src/Traffic/Message/Multipart/File.php @@ -10,7 +10,7 @@ /** * @psalm-type FileDataArray = array{ - * headers: array>, + * headers: array>, * name?: string, * fileName?: string, * size?: non-negative-int @@ -25,7 +25,7 @@ final class File extends Part implements UploadedFileInterface private ?int $fileSize = null; /** - * @param array> $headers + * @param array> $headers */ public function __construct(array $headers, ?string $name = null, private ?string $fileName = null) { @@ -50,12 +50,8 @@ public static function fromArray(array $data): self public function jsonSerialize(): array { $data = parent::jsonSerialize(); - if ($this->fileName !== null) { - $data['fileName'] = $this->fileName; - } - if ($this->fileSize !== null) { - $data['size'] = $this->fileSize; - } + $this->fileName === null or $data['fileName'] = $this->fileName; + $this->fileSize === null or $data['size'] = $this->fileSize; return $data; } diff --git a/src/Traffic/Message/Multipart/Part.php b/src/Traffic/Message/Multipart/Part.php index dd9b9d9d..4b831367 100644 --- a/src/Traffic/Message/Multipart/Part.php +++ b/src/Traffic/Message/Multipart/Part.php @@ -17,7 +17,7 @@ abstract class Part implements JsonSerializable use Headers; /** - * @param array> $headers + * @param array> $headers */ protected function __construct( array $headers, @@ -27,7 +27,7 @@ protected function __construct( } /** - * @param array> $headers + * @param array> $headers */ public static function create(array $headers): Part { @@ -75,7 +75,7 @@ public function withName(?string $name): static /** * @return array{ - * headers: array>, + * headers: array>, * name?: string, * } */ diff --git a/src/Traffic/Message/Smtp.php b/src/Traffic/Message/Smtp.php index 00bcf09e..896b3bad 100644 --- a/src/Traffic/Message/Smtp.php +++ b/src/Traffic/Message/Smtp.php @@ -18,6 +18,13 @@ * @psalm-import-type FieldDataArray from Field * @psalm-import-type FileDataArray from File * + * @psalm-type TArrayData = array{ + * protocol: array>, + * headers: array>, + * messages: list, + * attachments: list, + * } + * * @internal */ final class Smtp implements JsonSerializable @@ -32,8 +39,8 @@ final class Smtp implements JsonSerializable private array $attachments = []; /** - * @param array> $protocol - * @param array> $headers + * @param array> $protocol + * @param array> $headers */ private function __construct( private readonly array $protocol, @@ -43,8 +50,8 @@ private function __construct( } /** - * @param array> $protocol - * @param array> $headers + * @param array> $protocol + * @param array> $headers */ public static function create(array $protocol, array $headers): self { @@ -52,12 +59,7 @@ public static function create(array $protocol, array $headers): self } /** - * @param array{ - * protocol: array>, - * headers: array>, - * messages: list, - * attachments: list, - * } $data + * @param TArrayData $data */ public static function fromArray(array $data): self { @@ -119,7 +121,7 @@ public function withAttachments(array $attachments): self } /** - * @return array> + * @return array> */ public function getProtocol(): array { diff --git a/src/Traffic/Parser/Http.php b/src/Traffic/Parser/Http.php index 2bec87a1..dff70ed2 100644 --- a/src/Traffic/Parser/Http.php +++ b/src/Traffic/Parser/Http.php @@ -64,12 +64,10 @@ public function parseStream(StreamClient $stream): ServerRequestInterface $rawCookies = \explode(';', $request->getHeaderLine('Cookie')); $cookies = []; foreach ($rawCookies as $cookie) { - if (!str_contains($cookie, '=')) { + if (\str_contains($cookie, '=')) { /** @psalm-suppress PossiblyUndefinedArrayOffset */ [$name, $value] = \explode('=', \trim($cookie), 2); $cookies[$name] = $value; - } else { - throw new RuntimeException('Invalid cookie: ' . $cookie); } } @@ -82,7 +80,7 @@ public function parseStream(StreamClient $stream): ServerRequestInterface /** * @param string $line * - * @return list{non-empty-string, non-empty-string, non-empty-string} + * @return array{non-empty-string, non-empty-string, non-empty-string} */ private function parseFirstLine(string $line): array { @@ -152,10 +150,8 @@ private function processMultipartForm(ServerRequestInterface $request): ServerRe if (\preg_match('/boundary="?([^"\\s;]++)"?/', $request->getHeaderLine('Content-Type'), $matches) !== 1) { return $request; } + /** @var non-empty-string $boundary */ $boundary = $matches[1]; - if ($boundary === '') { - throw new \Exception('Boundary can\'t be empty'); - } $parts = self::parseMultipartBody($request->getBody(), $boundary); $uploadedFiles = $parsedBody = []; @@ -213,7 +209,7 @@ private function createBody(StreamClient $stream, ?int $limit): StreamInterface } /** - * @return array> + * @return array> */ public static function parseHeaders(string $headersBlock): array { diff --git a/src/Traffic/Parser/Smtp.php b/src/Traffic/Parser/Smtp.php index 55673453..8d9abcdb 100644 --- a/src/Traffic/Parser/Smtp.php +++ b/src/Traffic/Parser/Smtp.php @@ -18,7 +18,7 @@ final class Smtp { /** - * @param array> $protocol + * @param array> $protocol */ public function parseStream(array $protocol, StreamClient $stream): Message\Smtp { diff --git a/tests/Unit/Traffic/Parser/HttpParserTest.php b/tests/Unit/Traffic/Parser/HttpParserTest.php index f4c8f2d7..b505cb07 100644 --- a/tests/Unit/Traffic/Parser/HttpParserTest.php +++ b/tests/Unit/Traffic/Parser/HttpParserTest.php @@ -52,6 +52,39 @@ public function testSimpleGet(): void ], $request->getCookieParams()); } + /**\ + * Parer doesn't fail on wrong cookies + */ + public function testWrongCookie(): void + { + $body = \str_split( + <<parseStream($body); + + $this->assertSame('GET', $request->getMethod()); + $this->assertSame('/foo/bar', $request->getUri()->getPath()); + $this->assertSame('1.1', $request->getProtocolVersion()); + $this->assertSame(['127.0.0.1:9912'], $request->getHeader('host')); + $this->assertSame(['ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3'], $request->getHeader('accept-language')); + $this->assertSame([ + 'csrf-token.sig' => 'X0fR', + ], $request->getCookieParams()); + } + public function testPostUrlEncoded(): void { $body = \str_split( From 38ce3fc87687f28c1ba817a4af8c9eeb4089677b Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Tue, 30 Apr 2024 19:26:03 +0400 Subject: [PATCH 013/106] Fix few psalm types: make headers key as array-key --- src/Traffic/Message/Headers.php | 4 ++-- src/Traffic/Message/Multipart/Field.php | 4 ++-- src/Traffic/Message/Multipart/File.php | 4 ++-- src/Traffic/Message/Multipart/Part.php | 8 ++++---- src/Traffic/Parser/Http.php | 2 +- src/Traffic/Parser/Smtp.php | 1 + 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Traffic/Message/Headers.php b/src/Traffic/Message/Headers.php index 4fc09473..5c25892a 100644 --- a/src/Traffic/Message/Headers.php +++ b/src/Traffic/Message/Headers.php @@ -180,7 +180,7 @@ private function validateAndTrimHeader(string $header, mixed $values): array /** * List of header values. * - * @param array> $headers + * @param array> $headers * @param non-empty-string $header * * @return list @@ -190,7 +190,7 @@ private static function findHeader(array $headers, string $header): array $header = \strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); $result = []; foreach ($headers as $name => $values) { - if (\strtr($name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') === $header) { + if (\strtr((string) $name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') === $header) { $result = [...$result, ...$values]; } } diff --git a/src/Traffic/Message/Multipart/Field.php b/src/Traffic/Message/Multipart/Field.php index e85f2bcc..80c74563 100644 --- a/src/Traffic/Message/Multipart/Field.php +++ b/src/Traffic/Message/Multipart/Field.php @@ -6,7 +6,7 @@ /** * @psalm-type FieldDataArray = array{ - * headers: array>, + * headers: array>, * name?: string, * value: string * } @@ -16,7 +16,7 @@ final class Field extends Part { /** - * @param array> $headers + * @param array> $headers */ public function __construct(array $headers, ?string $name = null, private string $value = '') { diff --git a/src/Traffic/Message/Multipart/File.php b/src/Traffic/Message/Multipart/File.php index c0f8cb02..3ce9738c 100644 --- a/src/Traffic/Message/Multipart/File.php +++ b/src/Traffic/Message/Multipart/File.php @@ -10,7 +10,7 @@ /** * @psalm-type FileDataArray = array{ - * headers: array>, + * headers: array>, * name?: string, * fileName?: string, * size?: non-negative-int @@ -25,7 +25,7 @@ final class File extends Part implements UploadedFileInterface private ?int $fileSize = null; /** - * @param array> $headers + * @param array> $headers */ public function __construct(array $headers, ?string $name = null, private ?string $fileName = null) { diff --git a/src/Traffic/Message/Multipart/Part.php b/src/Traffic/Message/Multipart/Part.php index 4b831367..0e958b5d 100644 --- a/src/Traffic/Message/Multipart/Part.php +++ b/src/Traffic/Message/Multipart/Part.php @@ -17,7 +17,7 @@ abstract class Part implements JsonSerializable use Headers; /** - * @param array> $headers + * @param array> $headers */ protected function __construct( array $headers, @@ -27,7 +27,7 @@ protected function __construct( } /** - * @param array> $headers + * @param array> $headers */ public static function create(array $headers): Part { @@ -75,8 +75,8 @@ public function withName(?string $name): static /** * @return array{ - * headers: array>, - * name?: string, + * headers: array>, + * name?: string * } */ public function jsonSerialize(): array diff --git a/src/Traffic/Parser/Http.php b/src/Traffic/Parser/Http.php index dff70ed2..e541946e 100644 --- a/src/Traffic/Parser/Http.php +++ b/src/Traffic/Parser/Http.php @@ -41,7 +41,7 @@ public function parseStream(StreamClient $stream): ServerRequestInterface $request = $this->factory->createServerRequest($method, $uri, []) ->withProtocolVersion($protocol); foreach ($headers as $name => $value) { - $request = $request->withHeader($name, $value); + $request = $request->withHeader((string) $name, $value); } // Todo refactor: diff --git a/src/Traffic/Parser/Smtp.php b/src/Traffic/Parser/Smtp.php index 8d9abcdb..50310489 100644 --- a/src/Traffic/Parser/Smtp.php +++ b/src/Traffic/Parser/Smtp.php @@ -99,6 +99,7 @@ private function processSingleBody(Message\Smtp $message, StreamInterface $strea { $content = \preg_replace(["/^\.([^\r])/m", "/(\r\n\\.\r\n)$/D"], ['$1', ''], $stream->getContents()); + /** @psalm-suppress InvalidArgument */ $body = new Field( headers: \array_intersect_key($message->getHeaders(), ['Content-Type' => true]), value: $content, From cc3c4be6adb2cce73389a4174168df38cb1e06fe Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 30 Apr 2024 18:33:57 +0300 Subject: [PATCH 014/106] Changed permission flags for bin/trap to 755 (#58) --- bin/trap | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/trap diff --git a/bin/trap b/bin/trap old mode 100644 new mode 100755 From fe18ffe4fbb163b52e17ef517a7b94c376f95f72 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 1 May 2024 13:11:28 +0400 Subject: [PATCH 015/106] Add Config Loader that may fetch values from XML using XPath --- composer.json | 1 + src/Service/Config/ConfigAttribute.php | 12 +++ src/Service/Config/ConfigLoader.php | 87 +++++++++++++++++++ src/Service/Config/XPath.php | 17 ++++ .../Unit/Service/Config/ConfigLoaderTest.php | 44 ++++++++++ trap.xml | 6 ++ 6 files changed, 167 insertions(+) create mode 100644 src/Service/Config/ConfigAttribute.php create mode 100644 src/Service/Config/ConfigLoader.php create mode 100644 src/Service/Config/XPath.php create mode 100644 tests/Unit/Service/Config/ConfigLoaderTest.php create mode 100644 trap.xml diff --git a/composer.json b/composer.json index 5fe7c04a..7c7ab370 100644 --- a/composer.json +++ b/composer.json @@ -71,6 +71,7 @@ "vimeo/psalm": "^5.11" }, "suggest": { + "ext-simplexml": "To load trap.xml", "roxblnfk/unpoly": "If you want to remove unnecessary PHP polyfills depend on PHP version." } } diff --git a/src/Service/Config/ConfigAttribute.php b/src/Service/Config/ConfigAttribute.php new file mode 100644 index 00000000..435be89f --- /dev/null +++ b/src/Service/Config/ConfigAttribute.php @@ -0,0 +1,12 @@ +xml = \is_string($xml) ? \simplexml_load_string($xml) : null; + } + + public function hidrate(object $config): void + { + // Read class properties + $reflection = new \ReflectionObject($config); + foreach ($reflection->getProperties() as $property) { + $attributes = $property->getAttributes(ConfigAttribute::class, \ReflectionAttribute::IS_INSTANCEOF); + if (\count($attributes) === 0) { + continue; + } + + $this->injectValue($config, $property, $attributes); + } + } + + /** + * @param \ReflectionProperty $property + * @param array<\ReflectionAttribute> $attributes + */ + private function injectValue(object $config, \ReflectionProperty $property, array $attributes): void + { + foreach ($attributes as $attribute) { + $attribute = $attribute->newInstance(); + + $value = match (true) { + $attribute instanceof XPath => $this->xml?->xpath($attribute->path), + default => null, + }; + + if ($value === null) { + continue; + } + + // Cast value to the property type + $type = $property->getType(); + $result = match (true) { + $type === null => $value[0], + $type->allowsNull() && $value[0] === '' => null, + $type->isBuiltin() => match ($type->getName()) { + 'int' => (int) $value[0], + 'float' => (float) $value[0], + 'bool' => \filter_var($value[0], FILTER_VALIDATE_BOOLEAN), + default => $value[0], + }, + default => $value[0], + }; + + // Set the property value + $property->setValue($config, $result); + } + } +} diff --git a/src/Service/Config/XPath.php b/src/Service/Config/XPath.php new file mode 100644 index 00000000..120289c3 --- /dev/null +++ b/src/Service/Config/XPath.php @@ -0,0 +1,17 @@ + + + + + + + + XML; + + $loader = new ConfigLoader(fn() => $xml); + $loader->hidrate($dto); + + self::assertTrue($dto->myBool); + self::assertSame(200, $dto->myInt); + self::assertSame('foo-bar', $dto->myString); + self::assertSame(42.0, $dto->myFloat); + } +} diff --git a/trap.xml b/trap.xml new file mode 100644 index 00000000..e7eea718 --- /dev/null +++ b/trap.xml @@ -0,0 +1,6 @@ + + + + + + From af4fb27781fb6da556926960a6add652ae3fab61 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 1 May 2024 13:37:18 +0400 Subject: [PATCH 016/106] Normalize Frontend configs --- src/Config/Frontend.php | 17 ++++++++++++++ src/Config/Frontend/Buffer.php | 20 ----------------- src/Config/Frontend/EventStorage.php | 22 +++++++++++++++++++ .../{EventsStorage.php => EventStorage.php} | 6 ++--- src/Sender/Frontend/FrameHandler.php | 2 +- src/Sender/Frontend/Http/EventAssets.php | 4 ++-- src/Sender/Frontend/Http/Router.php | 4 ++-- src/Sender/Frontend/RPC.php | 2 +- src/Sender/Frontend/Service.php | 2 +- src/Sender/FrontendSender.php | 10 ++++----- .../Sender/Frontend/EventsStorageTest.php | 8 +++---- trap.xml | 2 +- 12 files changed, 59 insertions(+), 40 deletions(-) create mode 100644 src/Config/Frontend.php delete mode 100644 src/Config/Frontend/Buffer.php create mode 100644 src/Config/Frontend/EventStorage.php rename src/Sender/Frontend/{EventsStorage.php => EventStorage.php} (91%) diff --git a/src/Config/Frontend.php b/src/Config/Frontend.php new file mode 100644 index 00000000..b8df12fd --- /dev/null +++ b/src/Config/Frontend.php @@ -0,0 +1,17 @@ + */ + #[XPath('/trap/frontend@port')] + public int $port = 8000; +} diff --git a/src/Config/Frontend/Buffer.php b/src/Config/Frontend/Buffer.php deleted file mode 100644 index 394353b4..00000000 --- a/src/Config/Frontend/Buffer.php +++ /dev/null @@ -1,20 +0,0 @@ - $maxSize The maximum number of events that can be stored in the buffer. - */ - public function __construct( - public readonly int $maxSize = 200, - ) { - } -} diff --git a/src/Config/Frontend/EventStorage.php b/src/Config/Frontend/EventStorage.php new file mode 100644 index 00000000..af9190e5 --- /dev/null +++ b/src/Config/Frontend/EventStorage.php @@ -0,0 +1,22 @@ + + */ + #[XPath('/trap/frontend/EventStorage@maxEvents')] + public int $maxEvents = 200; +} diff --git a/src/Sender/Frontend/EventsStorage.php b/src/Sender/Frontend/EventStorage.php similarity index 91% rename from src/Sender/Frontend/EventsStorage.php rename to src/Sender/Frontend/EventStorage.php index 4e46d149..d3023e1a 100644 --- a/src/Sender/Frontend/EventsStorage.php +++ b/src/Sender/Frontend/EventStorage.php @@ -4,7 +4,7 @@ namespace Buggregator\Trap\Sender\Frontend; -use Buggregator\Trap\Config\Frontend\Buffer as Config; +use Buggregator\Trap\Config\Frontend\EventStorage as Config; use Countable; use IteratorAggregate; @@ -12,7 +12,7 @@ * @internal * @implements IteratorAggregate */ -final class EventsStorage implements IteratorAggregate, Countable +final class EventStorage implements IteratorAggregate, Countable { /** @@ -32,7 +32,7 @@ public function add(Event $event): void $this->events[$event->uuid] = $event; $this->sorted = false; - if (\count($this->events) > $this->config->maxSize) { + if (\count($this->events) > $this->config->maxEvents) { // find most old event and remove it $k = $event->uuid; $t = $event->timestamp; diff --git a/src/Sender/Frontend/FrameHandler.php b/src/Sender/Frontend/FrameHandler.php index 51edcf05..fd30f267 100644 --- a/src/Sender/Frontend/FrameHandler.php +++ b/src/Sender/Frontend/FrameHandler.php @@ -20,7 +20,7 @@ final class FrameHandler implements HandlerInterface public function __construct( private readonly Logger $logger, private readonly ConnectionPool $connectionPool, - private readonly EventsStorage $eventsStorage, + private readonly EventStorage $eventsStorage, ) { $this->frameMapper = new FrameMapper(); } diff --git a/src/Sender/Frontend/Http/EventAssets.php b/src/Sender/Frontend/Http/EventAssets.php index 35771fe5..442d5114 100644 --- a/src/Sender/Frontend/Http/EventAssets.php +++ b/src/Sender/Frontend/Http/EventAssets.php @@ -12,7 +12,7 @@ use Buggregator\Trap\Handler\Router\Router as CommonRouter; use Buggregator\Trap\Logger; use Buggregator\Trap\Sender\Frontend\Event\AttachedFile; -use Buggregator\Trap\Sender\Frontend\EventsStorage; +use Buggregator\Trap\Sender\Frontend\EventStorage; use Nyholm\Psr7\Response; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -27,7 +27,7 @@ final class EventAssets implements Middleware public function __construct( private readonly Logger $logger, - private readonly EventsStorage $eventsStorage, + private readonly EventStorage $eventsStorage, ) { $this->router = CommonRouter::new($this); } diff --git a/src/Sender/Frontend/Http/Router.php b/src/Sender/Frontend/Http/Router.php index 13e1656a..c5872c6a 100644 --- a/src/Sender/Frontend/Http/Router.php +++ b/src/Sender/Frontend/Http/Router.php @@ -8,7 +8,7 @@ use Buggregator\Trap\Handler\Router\Method; use Buggregator\Trap\Handler\Router\Router as CommonRouter; use Buggregator\Trap\Logger; -use Buggregator\Trap\Sender\Frontend\EventsStorage; +use Buggregator\Trap\Sender\Frontend\EventStorage; use Buggregator\Trap\Sender\Frontend\Service; use Buggregator\Trap\Support\Json; use Nyholm\Psr7\Response; @@ -25,7 +25,7 @@ final class Router implements Middleware public function __construct( private readonly Logger $logger, - EventsStorage $eventsStorage, + EventStorage $eventsStorage, ) { $service = new Service($logger, $eventsStorage); $this->router = CommonRouter::new($service); diff --git a/src/Sender/Frontend/RPC.php b/src/Sender/Frontend/RPC.php index be5b82f6..55749f47 100644 --- a/src/Sender/Frontend/RPC.php +++ b/src/Sender/Frontend/RPC.php @@ -17,7 +17,7 @@ final class RPC public function __construct( private readonly Logger $logger, - EventsStorage $eventsStorage, + EventStorage $eventsStorage, ) { $this->router = Router::new(new Service($logger, $eventsStorage)); } diff --git a/src/Sender/Frontend/Service.php b/src/Sender/Frontend/Service.php index 1fba7abc..39c1afb8 100644 --- a/src/Sender/Frontend/Service.php +++ b/src/Sender/Frontend/Service.php @@ -22,7 +22,7 @@ final class Service { public function __construct( private readonly Logger $logger, - private readonly EventsStorage $eventsStorage, + private readonly EventStorage $eventsStorage, ) { } diff --git a/src/Sender/FrontendSender.php b/src/Sender/FrontendSender.php index 079774c5..ff41bb25 100644 --- a/src/Sender/FrontendSender.php +++ b/src/Sender/FrontendSender.php @@ -17,9 +17,9 @@ final class FrontendSender implements \Buggregator\Trap\Sender, Processable public static function create( Logger $logger, ?Frontend\ConnectionPool $connectionPool = null, - ?Frontend\EventsStorage $eventStorage = null, + ?Frontend\EventStorage $eventStorage = null, ): self { - $eventStorage ??= new Frontend\EventsStorage(); + $eventStorage ??= new Frontend\EventStorage(); $connectionPool ??= new Frontend\ConnectionPool($logger, new Frontend\RPC($logger, $eventStorage)); return new self( $connectionPool, @@ -30,7 +30,7 @@ public static function create( private function __construct( private readonly ConnectionPool $connectionPool, - private readonly Frontend\EventsStorage $framesStorage, + private readonly Frontend\EventStorage $framesStorage, private readonly FrameHandler $handler, ) { } @@ -52,9 +52,9 @@ public function getConnectionPool(): ConnectionPool } /** - * @return Frontend\EventsStorage + * @return Frontend\EventStorage */ - public function getEventStorage(): Frontend\EventsStorage + public function getEventStorage(): Frontend\EventStorage { return $this->framesStorage; } diff --git a/tests/Unit/Sender/Frontend/EventsStorageTest.php b/tests/Unit/Sender/Frontend/EventsStorageTest.php index eddd77c4..c2addb34 100644 --- a/tests/Unit/Sender/Frontend/EventsStorageTest.php +++ b/tests/Unit/Sender/Frontend/EventsStorageTest.php @@ -4,9 +4,9 @@ namespace Buggregator\Trap\Tests\Unit\Sender\Frontend; -use Buggregator\Trap\Config\Frontend\Buffer as Config; +use Buggregator\Trap\Config\Frontend\EventStorage as Config; use Buggregator\Trap\Sender\Frontend\Event; -use Buggregator\Trap\Sender\Frontend\EventsStorage; +use Buggregator\Trap\Sender\Frontend\EventStorage; use Buggregator\Trap\Support\Uuid; use PHPUnit\Framework\TestCase; @@ -15,7 +15,7 @@ class EventsStorageTest extends TestCase public function testMaxEventsLimit(): void { $config = new Config(2); - $storage = new EventsStorage($config); + $storage = new EventStorage($config); $storage->add($e1 = $this->createEvent()); $storage->add($e2 = $this->createEvent()); @@ -31,7 +31,7 @@ public function testMaxEventsLimit(): void public function testMaxEventsLimitWithSort(): void { $config = new Config(2); - $storage = new EventsStorage($config); + $storage = new EventStorage($config); $storage->add($e1 = $this->createEvent()); $storage->add($e2 = $this->createEvent()); diff --git a/trap.xml b/trap.xml index e7eea718..818bf5de 100644 --- a/trap.xml +++ b/trap.xml @@ -1,6 +1,6 @@ - + From ed02006bb2bc15a0c7d0f3417c5ebab2d97c98fe Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 1 May 2024 14:49:00 +0400 Subject: [PATCH 017/106] Add Container --- composer.json | 4 +- src/Application.php | 7 +- src/Command/Run.php | 17 +-- src/Config/{ => Server}/Frontend.php | 2 +- .../{ => Server}/Frontend/EventStorage.php | 2 +- src/Config/{ => Server}/SocketServer.php | 2 +- src/Info.php | 2 +- src/Sender/Frontend/EventStorage.php | 2 +- src/Service/Container.php | 115 ++++++++++++++++++ .../Sender/Frontend/EventsStorageTest.php | 2 +- 10 files changed, 139 insertions(+), 16 deletions(-) rename src/Config/{ => Server}/Frontend.php (83%) rename src/Config/{ => Server}/Frontend/EventStorage.php (88%) rename src/Config/{ => Server}/SocketServer.php (89%) create mode 100644 src/Service/Container.php diff --git a/composer.json b/composer.json index 7c7ab370..a01be715 100644 --- a/composer.json +++ b/composer.json @@ -59,9 +59,11 @@ "nunomaduro/termwind": "^1.15 || ^2", "nyholm/psr7": "^1.8", "php-http/message": "^1.15", + "psr/container": "^1.1 || ^2.0", "psr/http-message": "^1.1 || ^2", "symfony/console": "^6.4 || ^7", - "symfony/var-dumper": "^6.3 || ^7" + "symfony/var-dumper": "^6.3 || ^7", + "yiisoft/injector": "^1.2" }, "require-dev": { "dereuromark/composer-prefer-lowest": "^0.1.10", diff --git a/src/Application.php b/src/Application.php index d59a9a5a..7c2ccba0 100644 --- a/src/Application.php +++ b/src/Application.php @@ -4,10 +4,11 @@ namespace Buggregator\Trap; -use Buggregator\Trap\Config\SocketServer; +use Buggregator\Trap\Config\Server\SocketServer; use Buggregator\Trap\Handler\Http\Handler\Websocket; use Buggregator\Trap\Handler\Http\Middleware; use Buggregator\Trap\Proto\Buffer; +use Buggregator\Trap\Service\Container; use Buggregator\Trap\Socket\Client; use Buggregator\Trap\Socket\Server; use Buggregator\Trap\Socket\SocketStream; @@ -31,17 +32,19 @@ final class Application implements Processable, Cancellable, Destroyable private readonly Buffer $buffer; private bool $cancelled = false; + private readonly Logger $logger; /** * @param SocketServer[] $map * @param Sender[] $senders */ public function __construct( + private readonly Container $container, array $map = [], - private readonly Logger $logger = new Logger(), private array $senders = [], bool $withFrontend = true, ) { + $this->logger = $this->container->get(Logger::class); $this->buffer = new Buffer(bufferSize: 10485760, timer: 0.1); $inspector = new Inspector( diff --git a/src/Command/Run.php b/src/Command/Run.php index e10fe6eb..b2138f16 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -5,10 +5,11 @@ namespace Buggregator\Trap\Command; use Buggregator\Trap\Application; -use Buggregator\Trap\Config\SocketServer; +use Buggregator\Trap\Config\Server\SocketServer; use Buggregator\Trap\Info; use Buggregator\Trap\Logger; use Buggregator\Trap\Sender; +use Buggregator\Trap\Service\Container; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\SignalableCommandInterface; @@ -82,13 +83,15 @@ protected function execute( $registry = $this->createRegistry($output); - $this->app = new Application( - $servers, - new Logger($output), - senders: $registry->getSenders($senders), - withFrontend: $input->getOption('ui') !== false, - ); + $container = new Container(); + $container->set(new Logger($output)); + $container->set($container->make(Application::class, [ + 'map' => $servers, + 'senders' => $registry->getSenders($senders), + 'withFrontend' => $input->getOption('ui') !== false, + ])); + $this->app = $container->get(Application::class); $this->app->run(); } catch (\Throwable $e) { if ($output->isVerbose()) { diff --git a/src/Config/Frontend.php b/src/Config/Server/Frontend.php similarity index 83% rename from src/Config/Frontend.php rename to src/Config/Server/Frontend.php index b8df12fd..0d18199f 100644 --- a/src/Config/Frontend.php +++ b/src/Config/Server/Frontend.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Buggregator\Trap\Config; +namespace Buggregator\Trap\Config\Server; use Buggregator\Trap\Service\Config\XPath; diff --git a/src/Config/Frontend/EventStorage.php b/src/Config/Server/Frontend/EventStorage.php similarity index 88% rename from src/Config/Frontend/EventStorage.php rename to src/Config/Server/Frontend/EventStorage.php index af9190e5..a902ae14 100644 --- a/src/Config/Frontend/EventStorage.php +++ b/src/Config/Server/Frontend/EventStorage.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Buggregator\Trap\Config\Frontend; +namespace Buggregator\Trap\Config\Server\Frontend; use Buggregator\Trap\Service\Config\XPath; diff --git a/src/Config/SocketServer.php b/src/Config/Server/SocketServer.php similarity index 89% rename from src/Config/SocketServer.php rename to src/Config/Server/SocketServer.php index 9609f693..18aa1df2 100644 --- a/src/Config/SocketServer.php +++ b/src/Config/Server/SocketServer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Buggregator\Trap\Config; +namespace Buggregator\Trap\Config\Server; /** * @internal diff --git a/src/Info.php b/src/Info.php index b5f9104b..e6d3cc60 100644 --- a/src/Info.php +++ b/src/Info.php @@ -10,7 +10,7 @@ class Info { public const NAME = 'Buggregator Trap'; - public const VERSION = '1.4.6'; + public const VERSION = '1.6.0'; public const LOGO_CLI_COLOR = << */ + private array $cache = []; + + /** @var array */ + private array $factory = []; + + private readonly Injector $injector; + + /** + * @psalm-suppress PropertyTypeCoercion + */ + public function __construct() + { + $this->injector = (new Injector($this))->withCacheReflections(false); + $this->cache[Injector::class] = $this->injector; + $this->cache[self::class] = $this; + $this->cache[ContainerInterface::class] = $this; + } + + /** + * @template T of object + * @param class-string $id + * @return T + * + * @psalm-suppress MoreSpecificImplementedParamType + */ + public function get(string $id): object + { + /** @psalm-suppress InvalidReturnStatement */ + return $this->cache[$id] ??= $this->make($id); + } + + /** + * @param class-string $id + * + * @psalm-suppress MoreSpecificImplementedParamType + */ + public function has(string $id): bool + { + return \array_key_exists($id, $this->cache) || \array_key_exists($id, $this->factory); + } + + /** + * @template T of object + * @param T $service + * @param class-string|null $id + */ + public function set(object $service, ?string $id = null): void + { + \assert($id === null || $service instanceof $id, "Service must be instance of {$id}."); + $this->cache[$id ?? \get_class($service)] = $service; + } + + /** + * Create an object of the specified class without caching. + * + * @template T + * @param class-string $class + * @return T + */ + public function make(string $class, array $arguments = []): object + { + $result = \array_key_exists($class, $this->factory) + ? ($this->factory[$class])($this) + : $this->injector->make($class, $arguments); + + \assert($result instanceof $class, "Created object must be instance of {$class}."); + + // Detect Trap related types + + // Configs + if (\str_starts_with($class, 'Buggregator\\Trap\\Config\\')) { + // Hydrate config + $configLoader = $this->get(ConfigLoader::class); + $configLoader->hidrate($result); + } + + return $result; + } + + /** + * Declare a factory for the specified class. + * + * @template T of object + * @param class-string $id + * @param (callable(Container): T) $callback + */ + public function bind(string $id, callable $callback): void + { + $this->factory[$id] = $callback; + } + + public function destroy(): void + { + unset($this->cache, $this->factory, $this->injector); + } +} diff --git a/tests/Unit/Sender/Frontend/EventsStorageTest.php b/tests/Unit/Sender/Frontend/EventsStorageTest.php index c2addb34..815d2d67 100644 --- a/tests/Unit/Sender/Frontend/EventsStorageTest.php +++ b/tests/Unit/Sender/Frontend/EventsStorageTest.php @@ -4,7 +4,7 @@ namespace Buggregator\Trap\Tests\Unit\Sender\Frontend; -use Buggregator\Trap\Config\Frontend\EventStorage as Config; +use Buggregator\Trap\Config\Server\Frontend\EventStorage as Config; use Buggregator\Trap\Sender\Frontend\Event; use Buggregator\Trap\Sender\Frontend\EventStorage; use Buggregator\Trap\Support\Uuid; From f37be4ba54fb99f29bf0a6294c659587519e47e5 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 1 May 2024 17:27:53 +0400 Subject: [PATCH 018/106] Support autoinjectable ENVs and CLI params in configs --- src/Application.php | 20 +++--- src/Command/Run.php | 8 ++- src/Config/Server/Frontend.php | 6 +- src/Proto/Buffer.php | 3 + src/Service/Config/CliOption.php | 17 +++++ src/Service/Config/ConfigLoader.php | 66 ++++++++++++------- src/Service/Config/Env.php | 17 +++++ src/Service/Config/XPath.php | 1 + src/Service/Container.php | 25 ++++--- .../Sender/Frontend/EventsStorageTest.php | 6 +- 10 files changed, 117 insertions(+), 52 deletions(-) create mode 100644 src/Service/Config/CliOption.php create mode 100644 src/Service/Config/Env.php diff --git a/src/Application.php b/src/Application.php index 7c2ccba0..7b0b29fc 100644 --- a/src/Application.php +++ b/src/Application.php @@ -4,6 +4,7 @@ namespace Buggregator\Trap; +use Buggregator\Trap\Config\Server\Frontend as FrontendConfig; use Buggregator\Trap\Config\Server\SocketServer; use Buggregator\Trap\Handler\Http\Handler\Websocket; use Buggregator\Trap\Handler\Http\Middleware; @@ -46,10 +47,9 @@ public function __construct( ) { $this->logger = $this->container->get(Logger::class); $this->buffer = new Buffer(bufferSize: 10485760, timer: 0.1); + $this->container->set($this->buffer); - $inspector = new Inspector( - $this->buffer, - $this->logger, + $inspector = $container->make(Inspector::class, [ // new Traffic\Dispatcher\WebSocket(), new Traffic\Dispatcher\VarDumper(), new Traffic\Dispatcher\Http( @@ -63,7 +63,7 @@ public function __construct( ), new Traffic\Dispatcher\Smtp(), new Traffic\Dispatcher\Monolog(), - ); + ]); $this->processors[] = $inspector; $withFrontend and $this->configureFrontend(8000); @@ -202,9 +202,7 @@ public function configureFrontend(int $port): void { $this->senders[] = $wsSender = Sender\FrontendSender::create($this->logger); - $inspector = new Inspector( - $this->buffer, - $this->logger, + $inspector = $this->container->make(Inspector::class, [ new Traffic\Dispatcher\Http( [ new Sender\Frontend\Http\StaticFiles(), @@ -213,11 +211,11 @@ public function configureFrontend(int $port): void ], [new Sender\Frontend\Http\RequestHandler($wsSender->getConnectionPool())], silentMode: true, - ), - ); + ) + ]); $this->processors[] = $inspector; - $this->processors[] = $wsSender; - $this->prepareServerFiber(new SocketServer(port: $port), $inspector, $this->logger); + $config = $this->container->get(FrontendConfig::class); + $this->prepareServerFiber(new SocketServer(port: $config->port), $inspector, $this->logger); } } diff --git a/src/Command/Run.php b/src/Command/Run.php index b2138f16..a86c5f29 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -84,14 +84,16 @@ protected function execute( $registry = $this->createRegistry($output); $container = new Container(); + $container->set($registry); + $container->set($input, InputInterface::class); $container->set(new Logger($output)); - $container->set($container->make(Application::class, [ + $this->app = $container->get(Application::class, [ 'map' => $servers, 'senders' => $registry->getSenders($senders), 'withFrontend' => $input->getOption('ui') !== false, - ])); + ]); + - $this->app = $container->get(Application::class); $this->app->run(); } catch (\Throwable $e) { if ($output->isVerbose()) { diff --git a/src/Config/Server/Frontend.php b/src/Config/Server/Frontend.php index 0d18199f..77e0aa8d 100644 --- a/src/Config/Server/Frontend.php +++ b/src/Config/Server/Frontend.php @@ -4,6 +4,8 @@ namespace Buggregator\Trap\Config\Server; +use Buggregator\Trap\Service\Config\CliOption; +use Buggregator\Trap\Service\Config\Env; use Buggregator\Trap\Service\Config\XPath; /** @@ -12,6 +14,8 @@ final class Frontend { /** @var int<1, max> */ - #[XPath('/trap/frontend@port')] + #[Env('TRAP_FRONTEND_PORT')] + #[CliOption('ui')] + #[XPath('/trap/frontend/@port')] public int $port = 8000; } diff --git a/src/Proto/Buffer.php b/src/Proto/Buffer.php index 66998202..8610121f 100644 --- a/src/Proto/Buffer.php +++ b/src/Proto/Buffer.php @@ -7,6 +7,9 @@ use Buggregator\Trap\Support\Timer; /** + * A buffer that accumulates events and notifies when it is full. + * Overflow can occur due to a configured timeout or number of events. + * * @internal */ final class Buffer diff --git a/src/Service/Config/CliOption.php b/src/Service/Config/CliOption.php new file mode 100644 index 00000000..b631062a --- /dev/null +++ b/src/Service/Config/CliOption.php @@ -0,0 +1,17 @@ +xml = \is_string($xml) ? \simplexml_load_string($xml) : null; + $this->xml = \is_string($xml) + ? (\simplexml_load_string($xml, options: \LIBXML_NOERROR) ?: null) + : null; } public function hidrate(object $config): void @@ -55,33 +62,42 @@ public function hidrate(object $config): void private function injectValue(object $config, \ReflectionProperty $property, array $attributes): void { foreach ($attributes as $attribute) { - $attribute = $attribute->newInstance(); + try { + $attribute = $attribute->newInstance(); - $value = match (true) { - $attribute instanceof XPath => $this->xml?->xpath($attribute->path), - default => null, - }; + $value = match (true) { + $attribute instanceof XPath => $this->xml?->xpath($attribute->path)[$attribute->key], + $attribute instanceof Env => \getenv($attribute->name) === false ? null : \getenv($attribute->name), + $attribute instanceof CliOption => $this->cliInput?->getOption($attribute->name), + default => null, + }; - if ($value === null) { - continue; - } + if ($value === null) { + continue; + } - // Cast value to the property type - $type = $property->getType(); - $result = match (true) { - $type === null => $value[0], - $type->allowsNull() && $value[0] === '' => null, - $type->isBuiltin() => match ($type->getName()) { - 'int' => (int) $value[0], - 'float' => (float) $value[0], - 'bool' => \filter_var($value[0], FILTER_VALIDATE_BOOLEAN), - default => $value[0], - }, - default => $value[0], - }; + // Cast value to the property type + $type = $property->getType(); + $result = match (true) { + $type === null => $value, + $type->allowsNull() && $value === '' => null, + $type->isBuiltin() => match ($type->getName()) { + 'int' => (int) $value, + 'float' => (float) $value, + 'bool' => \filter_var($value, FILTER_VALIDATE_BOOLEAN), + default => $value, + }, + default => $value, + }; - // Set the property value - $property->setValue($config, $result); + // todo Validation + + // Set the property value + $property->setValue($config, $result); + return; + } catch (\Throwable $e) { + $this->logger->exception($e, important: true); + } } } } diff --git a/src/Service/Config/Env.php b/src/Service/Config/Env.php new file mode 100644 index 00000000..5ce1d1d3 --- /dev/null +++ b/src/Service/Config/Env.php @@ -0,0 +1,17 @@ + */ private array $cache = []; - /** @var array */ + /** @var array */ private array $factory = []; private readonly Injector $injector; @@ -38,14 +38,15 @@ public function __construct() /** * @template T of object * @param class-string $id + * @param array $arguments Will be used if the object is created for the first time. * @return T * * @psalm-suppress MoreSpecificImplementedParamType */ - public function get(string $id): object + public function get(string $id, array $arguments = []): object { /** @psalm-suppress InvalidReturnStatement */ - return $this->cache[$id] ??= $this->make($id); + return $this->cache[$id] ??= $this->make($id, $arguments); } /** @@ -78,9 +79,13 @@ public function set(object $service, ?string $id = null): void */ public function make(string $class, array $arguments = []): object { - $result = \array_key_exists($class, $this->factory) - ? ($this->factory[$class])($this) - : $this->injector->make($class, $arguments); + $binding = $this->factory[$class] ?? null; + + $result = match(true) { + $binding === null => $this->injector->make($class, $arguments), + \is_array($binding) => $this->injector->make($class, \array_merge($binding, $arguments)), + default => ($this->factory[$class])($this), + }; \assert($result instanceof $class, "Created object must be instance of {$class}."); @@ -97,15 +102,15 @@ public function make(string $class, array $arguments = []): object } /** - * Declare a factory for the specified class. + * Declare a factory or predefined arguments for the specified class. * * @template T of object * @param class-string $id - * @param (callable(Container): T) $callback + * @param array|\Closure(Container): T $binding */ - public function bind(string $id, callable $callback): void + public function bind(string $id, \Closure|array $binding): void { - $this->factory[$id] = $callback; + $this->factory[$id] = $binding; } public function destroy(): void diff --git a/tests/Unit/Sender/Frontend/EventsStorageTest.php b/tests/Unit/Sender/Frontend/EventsStorageTest.php index 815d2d67..5858c3cf 100644 --- a/tests/Unit/Sender/Frontend/EventsStorageTest.php +++ b/tests/Unit/Sender/Frontend/EventsStorageTest.php @@ -14,7 +14,8 @@ class EventsStorageTest extends TestCase { public function testMaxEventsLimit(): void { - $config = new Config(2); + $config = new Config(); + $config->maxEvents = 2; $storage = new EventStorage($config); $storage->add($e1 = $this->createEvent()); @@ -30,7 +31,8 @@ public function testMaxEventsLimit(): void public function testMaxEventsLimitWithSort(): void { - $config = new Config(2); + $config = new Config(); + $config->maxEvents = 2; $storage = new EventStorage($config); $storage->add($e1 = $this->createEvent()); From e2c9998a082b8ba0b0e36c60c3bb63073f0a6718 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 1 May 2024 17:39:38 +0400 Subject: [PATCH 019/106] Fix tests --- src/Config/Server/Frontend.php | 2 +- src/Service/Config/ConfigLoader.php | 8 ++++++-- src/Service/Container.php | 2 +- tests/Unit/Service/Config/ConfigLoaderTest.php | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Config/Server/Frontend.php b/src/Config/Server/Frontend.php index 77e0aa8d..76ad55bb 100644 --- a/src/Config/Server/Frontend.php +++ b/src/Config/Server/Frontend.php @@ -13,7 +13,7 @@ */ final class Frontend { - /** @var int<1, max> */ + /** @var int<1, 65535> */ #[Env('TRAP_FRONTEND_PORT')] #[CliOption('ui')] #[XPath('/trap/frontend/@port')] diff --git a/src/Service/Config/ConfigLoader.php b/src/Service/Config/ConfigLoader.php index 2a8bb03d..5b6b1196 100644 --- a/src/Service/Config/ConfigLoader.php +++ b/src/Service/Config/ConfigLoader.php @@ -16,6 +16,7 @@ final class ConfigLoader /** * @param null|callable(): non-empty-string $xmlProvider + * @psalm-suppress RiskyTruthyFalsyComparison */ public function __construct( private Logger $logger, @@ -57,7 +58,7 @@ public function hidrate(object $config): void /** * @param \ReflectionProperty $property - * @param array<\ReflectionAttribute> $attributes + * @param list<\ReflectionAttribute> $attributes */ private function injectValue(object $config, \ReflectionProperty $property, array $attributes): void { @@ -65,6 +66,7 @@ private function injectValue(object $config, \ReflectionProperty $property, arra try { $attribute = $attribute->newInstance(); + /** @var mixed $value */ $value = match (true) { $attribute instanceof XPath => $this->xml?->xpath($attribute->path)[$attribute->key], $attribute instanceof Env => \getenv($attribute->name) === false ? null : \getenv($attribute->name), @@ -78,8 +80,10 @@ private function injectValue(object $config, \ReflectionProperty $property, arra // Cast value to the property type $type = $property->getType(); + + /** @var mixed $result */ $result = match (true) { - $type === null => $value, + !$type instanceof \ReflectionNamedType => $value, $type->allowsNull() && $value === '' => null, $type->isBuiltin() => match ($type->getName()) { 'int' => (int) $value, diff --git a/src/Service/Container.php b/src/Service/Container.php index cbb1370c..90508388 100644 --- a/src/Service/Container.php +++ b/src/Service/Container.php @@ -41,7 +41,7 @@ public function __construct() * @param array $arguments Will be used if the object is created for the first time. * @return T * - * @psalm-suppress MoreSpecificImplementedParamType + * @psalm-suppress MoreSpecificImplementedParamType, InvalidReturnType */ public function get(string $id, array $arguments = []): object { diff --git a/tests/Unit/Service/Config/ConfigLoaderTest.php b/tests/Unit/Service/Config/ConfigLoaderTest.php index 97f025fa..b33ed0e4 100644 --- a/tests/Unit/Service/Config/ConfigLoaderTest.php +++ b/tests/Unit/Service/Config/ConfigLoaderTest.php @@ -4,6 +4,7 @@ namespace Buggregator\Trap\Tests\Unit\Service\Config; +use Buggregator\Trap\Logger; use Buggregator\Trap\Service\Config\ConfigLoader; use Buggregator\Trap\Service\Config\XPath; use PHPUnit\Framework\TestCase; @@ -33,7 +34,7 @@ public function testSimpleHydration(): void XML; - $loader = new ConfigLoader(fn() => $xml); + $loader = new ConfigLoader(new Logger(), null, fn() => $xml); $loader->hidrate($dto); self::assertTrue($dto->myBool); From 1fc81b179902fec63a5b1e77c31d0b29bf0c4dc1 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 1 May 2024 23:13:55 +0400 Subject: [PATCH 020/106] Add bootstrap and tests --- src/Bootstrap.php | 75 +++++++++++++++++++ src/Command/Run.php | 9 ++- src/Config/Server/Frontend.php | 4 +- src/Service/Config/ConfigLoader.php | 40 ++++------ src/Service/Config/InputArgument.php | 17 +++++ .../Config/{CliOption.php => InputOption.php} | 2 +- src/Service/Container.php | 16 ++-- .../Unit/Service/Config/ConfigLoaderTest.php | 58 +++++++++++++- 8 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 src/Bootstrap.php create mode 100644 src/Service/Config/InputArgument.php rename src/Service/Config/{CliOption.php => InputOption.php} (81%) diff --git a/src/Bootstrap.php b/src/Bootstrap.php new file mode 100644 index 00000000..079bc7d8 --- /dev/null +++ b/src/Bootstrap.php @@ -0,0 +1,75 @@ +container; + unset($this->container); + + return $c; + } + + /** + * @param non-empty-string|null $xml File or XML content + */ + public function withConfig( + ?string $xml = null, + array $inputOptions = [], + array $inputArguments = [], + array $environment = [], + ): self { + $args = [ + 'env' => $environment, + 'inputArguments' => $inputArguments, + 'inputOptions' => $inputOptions, + ]; + + // XML config file + $xml === null or $args['xml'] = $this->readXml($xml); + + // Register bindings + $this->container->bind(ConfigLoader::class, $args); + + return $this; + } + + private function readXml(string $fileOrContent): string + { + // Load content + if (\str_starts_with($fileOrContent, 'createRegistry($output); - $container = new Container(); + $container = Bootstrap::init()->withConfig( + xml: \dirname(__DIR__, 2) . '/trap.xml', + inputOptions: $input->getOptions(), + inputArguments: $input->getArguments(), + environment: \getenv(), + )->finish(); $container->set($registry); $container->set($input, InputInterface::class); $container->set(new Logger($output)); diff --git a/src/Config/Server/Frontend.php b/src/Config/Server/Frontend.php index 76ad55bb..8ad3bb60 100644 --- a/src/Config/Server/Frontend.php +++ b/src/Config/Server/Frontend.php @@ -4,7 +4,7 @@ namespace Buggregator\Trap\Config\Server; -use Buggregator\Trap\Service\Config\CliOption; +use Buggregator\Trap\Service\Config\InputOption; use Buggregator\Trap\Service\Config\Env; use Buggregator\Trap\Service\Config\XPath; @@ -15,7 +15,7 @@ final class Frontend { /** @var int<1, 65535> */ #[Env('TRAP_FRONTEND_PORT')] - #[CliOption('ui')] + #[InputOption('ui')] #[XPath('/trap/frontend/@port')] public int $port = 8000; } diff --git a/src/Service/Config/ConfigLoader.php b/src/Service/Config/ConfigLoader.php index 5b6b1196..625c6bc5 100644 --- a/src/Service/Config/ConfigLoader.php +++ b/src/Service/Config/ConfigLoader.php @@ -5,7 +5,6 @@ namespace Buggregator\Trap\Service\Config; use Buggregator\Trap\Logger; -use Symfony\Component\Console\Input\InputInterface; /** * @internal @@ -15,31 +14,23 @@ final class ConfigLoader private \SimpleXMLElement|null $xml = null; /** - * @param null|callable(): non-empty-string $xmlProvider * @psalm-suppress RiskyTruthyFalsyComparison */ public function __construct( - private Logger $logger, - private ?InputInterface $cliInput, - ?callable $xmlProvider = null, - ) - { - // Check SimpleXML extension - if (!\extension_loaded('simplexml')) { - return; - } - - try { - $xml = $xmlProvider === null - ? \file_get_contents(\dirname(__DIR__, 3) . '/trap.xml') - : $xmlProvider(); - } catch (\Throwable) { - return; + private readonly Logger $logger, + private readonly array $env = [], + private readonly array $inputArguments = [], + private readonly array $inputOptions = [], + ?string $xml = null, + ) { + if (\is_string($xml)) { + // Check SimpleXML extension + if (!\extension_loaded('simplexml')) { + $logger->info('SimpleXML extension is not loaded.'); + } else { + $this->xml = \simplexml_load_string($xml, options: \LIBXML_NOERROR) ?: null; + } } - - $this->xml = \is_string($xml) - ? (\simplexml_load_string($xml, options: \LIBXML_NOERROR) ?: null) - : null; } public function hidrate(object $config): void @@ -69,8 +60,9 @@ private function injectValue(object $config, \ReflectionProperty $property, arra /** @var mixed $value */ $value = match (true) { $attribute instanceof XPath => $this->xml?->xpath($attribute->path)[$attribute->key], - $attribute instanceof Env => \getenv($attribute->name) === false ? null : \getenv($attribute->name), - $attribute instanceof CliOption => $this->cliInput?->getOption($attribute->name), + $attribute instanceof Env => $this->env[$attribute->name] ?? null, + $attribute instanceof InputOption => $this->inputOptions[$attribute->name] ?? null, + $attribute instanceof InputArgument => $this->inputArguments[$attribute->name] ?? null, default => null, }; diff --git a/src/Service/Config/InputArgument.php b/src/Service/Config/InputArgument.php new file mode 100644 index 00000000..ff863541 --- /dev/null +++ b/src/Service/Config/InputArgument.php @@ -0,0 +1,17 @@ +factory[$class] ?? null; - $result = match(true) { - $binding === null => $this->injector->make($class, $arguments), - \is_array($binding) => $this->injector->make($class, \array_merge($binding, $arguments)), - default => ($this->factory[$class])($this), - }; + if ($binding instanceof \Closure) { + $result = $binding($this); + } else { + try { + $result = $this->injector->make($class, \array_merge((array) $binding, $arguments)); + } catch (\Throwable $e) { + throw new class(previous: $e) extends \RuntimeException implements NotFoundExceptionInterface {}; + } + } \assert($result instanceof $class, "Created object must be instance of {$class}."); // Detect Trap related types - // Configs if (\str_starts_with($class, 'Buggregator\\Trap\\Config\\')) { // Hydrate config diff --git a/tests/Unit/Service/Config/ConfigLoaderTest.php b/tests/Unit/Service/Config/ConfigLoaderTest.php index b33ed0e4..781ceb07 100644 --- a/tests/Unit/Service/Config/ConfigLoaderTest.php +++ b/tests/Unit/Service/Config/ConfigLoaderTest.php @@ -4,8 +4,11 @@ namespace Buggregator\Trap\Tests\Unit\Service\Config; -use Buggregator\Trap\Logger; +use Buggregator\Trap\Bootstrap; use Buggregator\Trap\Service\Config\ConfigLoader; +use Buggregator\Trap\Service\Config\Env; +use Buggregator\Trap\Service\Config\InputArgument; +use Buggregator\Trap\Service\Config\InputOption; use Buggregator\Trap\Service\Config\XPath; use PHPUnit\Framework\TestCase; @@ -22,8 +25,9 @@ public function testSimpleHydration(): void public string $myString; #[XPath('/trap/container/MyFloat/@value')] public float $myFloat; + #[XPath('/trap/container/Nothing/@value')] + public float $none = 3.14; }; - $xml = <<<'XML' @@ -34,12 +38,58 @@ public function testSimpleHydration(): void XML; - $loader = new ConfigLoader(new Logger(), null, fn() => $xml); - $loader->hidrate($dto); + $this->createConfigLoader(xml: $xml)->hidrate($dto); self::assertTrue($dto->myBool); self::assertSame(200, $dto->myInt); self::assertSame('foo-bar', $dto->myString); self::assertSame(42.0, $dto->myFloat); + self::assertSame(3.14, $dto->none); + } + + public function testAttributesOrder(): void + { + $dto = new class() { + #[XPath('/test/@foo')] + #[InputArgument('test')] + #[InputOption('test')] + #[Env('test')] + public int $int1; + #[Env('test')] + #[InputArgument('test')] + #[XPath('/test/@foo')] + #[InputOption('test')] + public int $int2; + #[InputArgument('test')] + #[Env('test')] + #[XPath('/test/@foo')] + #[InputOption('test')] + public int $int3; + }; + $xml = <<<'XML' + + + + XML; + + $this + ->createConfigLoader(xml: $xml, opts: ['test' => 13], args: ['test' => 69], env: ['test' => 0]) + ->hidrate($dto); + + self::assertSame(42, $dto->int1); + self::assertSame(0, $dto->int2); + self::assertSame(69, $dto->int3); + } + + private function createConfigLoader( + ?string $xml = null, + array $opts = [], + array $args = [], + array $env = [], + ): ConfigLoader { + return Bootstrap::init() + ->withConfig($xml, $opts, $args, $env) + ->finish() + ->get(ConfigLoader::class); } } From 2060195c9261926e454882825b9a97b36feb5fef Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 1 May 2024 23:58:59 +0400 Subject: [PATCH 021/106] Support arrays in config loader; add env `TRAP_TCP_PORTS` and `TRAP_TCP_HOST` --- src/Command/Run.php | 49 ++++++++++++++++------------- src/Config/Server/TcpPorts.php | 36 +++++++++++++++++++++ src/Service/Config/ConfigLoader.php | 7 ++++- 3 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 src/Config/Server/TcpPorts.php diff --git a/src/Command/Run.php b/src/Command/Run.php index 1ca6547a..26f36128 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -7,9 +7,11 @@ use Buggregator\Trap\Application; use Buggregator\Trap\Bootstrap; use Buggregator\Trap\Config\Server\SocketServer; +use Buggregator\Trap\Config\Server\TcpPorts; use Buggregator\Trap\Info; use Buggregator\Trap\Logger; use Buggregator\Trap\Sender; +use Buggregator\Trap\Service\Container; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\SignalableCommandInterface; @@ -38,18 +40,40 @@ public function configure(): void 'p', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Port to listen', - [9912], ); $this->addOption( 'sender', 's', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Senders', - ['console'] ); $this->addOption('ui', null, InputOption::VALUE_OPTIONAL, 'Enable WEB UI (experimental)', false); } + /** + * Prepare port listeners + * @return SocketServer[] + */ + public function getServers(Container $container): array + { + $config = $container->get(TcpPorts::class); + + $servers = []; + $ports = $config->ports ?: [9912]; + /** @var scalar $port */ + foreach ($ports as $port) { + \is_numeric($port) or throw new \InvalidArgumentException( + \sprintf('Invalid port `%s`. It must be a number.', (string)$port), + ); + $port = (int)$port; + $port > 0 && $port < 65536 or throw new \InvalidArgumentException( + \sprintf('Invalid port `%s`. It must be in range 1-65535.', $port), + ); + $servers[] = new SocketServer($port, $config->host, $config->type); + } + return $servers; + } + protected function execute( InputInterface $input, OutputInterface $output, @@ -59,25 +83,6 @@ protected function execute( $output->writeln(\sprintf('%s v%s', Info::NAME, Info::VERSION)); $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); - /** - * Prepare port listeners - * @var SocketServer[] $servers - */ - $servers = []; - $ports = $input->getOption('port') ?: [9912]; - \assert(\is_array($ports)); - foreach ($ports as $port) { - \assert(\is_scalar($port)); - \is_numeric($port) or throw new \InvalidArgumentException( - \sprintf('Invalid port `%s`. It must be a number.', (string)$port), - ); - $port = (int)$port; - $port > 0 && $port < 65536 or throw new \InvalidArgumentException( - \sprintf('Invalid port `%s`. It must be in range 1-65535.', $port), - ); - $servers[] = new SocketServer($port); - } - /** @var non-empty-string[] $senders */ $senders = (array)$input->getOption('sender'); @@ -93,7 +98,7 @@ protected function execute( $container->set($input, InputInterface::class); $container->set(new Logger($output)); $this->app = $container->get(Application::class, [ - 'map' => $servers, + 'map' => $this->getServers($container), 'senders' => $registry->getSenders($senders), 'withFrontend' => $input->getOption('ui') !== false, ]); diff --git a/src/Config/Server/TcpPorts.php b/src/Config/Server/TcpPorts.php new file mode 100644 index 00000000..7a537c69 --- /dev/null +++ b/src/Config/Server/TcpPorts.php @@ -0,0 +1,36 @@ +> + */ + #[Env('TRAP_TCP_PORTS')] + #[InputOption('port')] + public array $ports = [9912, 9913]; + + /** + * Host to listen + * + * @var non-empty-string + */ + #[Env('TRAP_TCP_HOST')] + public string $host = '127.0.0.1'; + + public string $type = 'tcp'; +} diff --git a/src/Service/Config/ConfigLoader.php b/src/Service/Config/ConfigLoader.php index 625c6bc5..8bb1c736 100644 --- a/src/Service/Config/ConfigLoader.php +++ b/src/Service/Config/ConfigLoader.php @@ -66,7 +66,7 @@ private function injectValue(object $config, \ReflectionProperty $property, arra default => null, }; - if ($value === null) { + if (\in_array($value, [null, []], true)) { continue; } @@ -81,6 +81,11 @@ private function injectValue(object $config, \ReflectionProperty $property, arra 'int' => (int) $value, 'float' => (float) $value, 'bool' => \filter_var($value, FILTER_VALIDATE_BOOLEAN), + 'array' => match (true) { + \is_array($value) => $value, + \is_string($value) => explode(',', $value), + default => [$value], + }, default => $value, }, default => $value, From 9165cf3643ffff5cd4643957ef799e4ea4b7c18c Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 2 May 2024 00:22:16 +0400 Subject: [PATCH 022/106] Fix warning when value not found by XPath --- src/Service/Config/ConfigLoader.php | 2 +- .../Unit/Service/Config/ConfigLoaderTest.php | 28 +++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Service/Config/ConfigLoader.php b/src/Service/Config/ConfigLoader.php index 8bb1c736..d19992c1 100644 --- a/src/Service/Config/ConfigLoader.php +++ b/src/Service/Config/ConfigLoader.php @@ -59,7 +59,7 @@ private function injectValue(object $config, \ReflectionProperty $property, arra /** @var mixed $value */ $value = match (true) { - $attribute instanceof XPath => $this->xml?->xpath($attribute->path)[$attribute->key], + $attribute instanceof XPath => @$this->xml?->xpath($attribute->path)[$attribute->key], $attribute instanceof Env => $this->env[$attribute->name] ?? null, $attribute instanceof InputOption => $this->inputOptions[$attribute->name] ?? null, $attribute instanceof InputArgument => $this->inputArguments[$attribute->name] ?? null, diff --git a/tests/Unit/Service/Config/ConfigLoaderTest.php b/tests/Unit/Service/Config/ConfigLoaderTest.php index 781ceb07..79a22fae 100644 --- a/tests/Unit/Service/Config/ConfigLoaderTest.php +++ b/tests/Unit/Service/Config/ConfigLoaderTest.php @@ -25,8 +25,6 @@ public function testSimpleHydration(): void public string $myString; #[XPath('/trap/container/MyFloat/@value')] public float $myFloat; - #[XPath('/trap/container/Nothing/@value')] - public float $none = 3.14; }; $xml = <<<'XML' @@ -44,7 +42,31 @@ public function testSimpleHydration(): void self::assertSame(200, $dto->myInt); self::assertSame('foo-bar', $dto->myString); self::assertSame(42.0, $dto->myFloat); - self::assertSame(3.14, $dto->none); + } + + public function testNonExistingOptions(): void + { + $dto = new class() { + #[XPath('/trap/container/Nothing/@value')] + public float $none1 = 3.14; + #[Env('f--o--o')] + public float $none2 = 3.14; + #[InputOption('f--o--o')] + public float $none3 = 3.14; + #[InputArgument('f--o--o')] + public float $none4 = 3.14; + }; + $xml = <<<'XML' + + + XML; + + $this->createConfigLoader(xml: $xml)->hidrate($dto); + + self::assertSame(3.14, $dto->none1); + self::assertSame(3.14, $dto->none2); + self::assertSame(3.14, $dto->none3); + self::assertSame(3.14, $dto->none4); } public function testAttributesOrder(): void From 876a11cd99c72159430a0bb8d8d8b302bbcdf318 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 2 May 2024 00:39:33 +0400 Subject: [PATCH 023/106] Update readme --- README.md | 8 +++++++- src/Config/Server/Frontend.php | 2 +- src/Config/Server/TcpPorts.php | 2 +- src/Service/Config/Env.php | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f006d738..019bad02 100644 --- a/README.md +++ b/README.md @@ -154,9 +154,15 @@ Sometimes, it's convenient to run Trap on the same ports that [Buggregator](http uses by default. Well, that's also possible: ```bash -vendor/bin/trap -p1025 -p9912 -p9913 -p8000 +vendor/bin/trap -p1025 -p9912 -p9913 -p8000 --ui=8080 ``` +Environment variables can also be used to set endpoints: +- `TRAP_TCP_PORTS` - for TCP traffic: `9912,9913,1025,8000` +- `TRAP_TCP_HOST` - for the TCP host (default: `127.0.0.1`) +- `TRAP_UI_PORT` - for the web interface: `8080` + + ### Choosing Your Senders Buggregator Trap provides a variety of "senders" that dictate where the dumps will be sent. Currently, the available diff --git a/src/Config/Server/Frontend.php b/src/Config/Server/Frontend.php index 8ad3bb60..115073c1 100644 --- a/src/Config/Server/Frontend.php +++ b/src/Config/Server/Frontend.php @@ -14,7 +14,7 @@ final class Frontend { /** @var int<1, 65535> */ - #[Env('TRAP_FRONTEND_PORT')] + #[Env('TRAP_UI_PORT')] #[InputOption('ui')] #[XPath('/trap/frontend/@port')] public int $port = 8000; diff --git a/src/Config/Server/TcpPorts.php b/src/Config/Server/TcpPorts.php index 7a537c69..513a2955 100644 --- a/src/Config/Server/TcpPorts.php +++ b/src/Config/Server/TcpPorts.php @@ -22,7 +22,7 @@ final class TcpPorts */ #[Env('TRAP_TCP_PORTS')] #[InputOption('port')] - public array $ports = [9912, 9913]; + public array $ports = [9912]; /** * Host to listen diff --git a/src/Service/Config/Env.php b/src/Service/Config/Env.php index 5ce1d1d3..a8633324 100644 --- a/src/Service/Config/Env.php +++ b/src/Service/Config/Env.php @@ -7,7 +7,7 @@ /** * @internal */ -#[\Attribute(\Attribute::TARGET_PROPERTY)] +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)] final class Env implements ConfigAttribute { public function __construct( From 743d5d63342578846fd627285c9b91849ecb31c5 Mon Sep 17 00:00:00 2001 From: Dima Jolkin Date: Thu, 2 May 2024 11:17:22 +0300 Subject: [PATCH 024/106] recover psalm.xml and update psalm-baseline.xml --- psalm-baseline.xml | 188 +++++++++++++++++++++++++++++++++++++++++++++ psalm.xml | 6 -- 2 files changed, 188 insertions(+), 6 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index a5770984..e303f9ce 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -6,6 +6,10 @@ + + + + @@ -21,19 +25,56 @@ + + + + + + + + + + + + + + + getAttribute('begin_at', null)]]> + getAttribute('begin_at', null)]]> + + + + + + + + + + + + + + + + + + + + method]]> + {$this->method}(...$arguments)]]> @@ -55,11 +96,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + request->getUploadedFiles()]]> + + + + + throw new \InvalidArgumentException('Unknown Sentry frame type.'), }]]> + + + + + + + + + + + + + + + + + + + + + @@ -99,6 +194,12 @@ getQueryParams()]]> + + + + + + @@ -108,6 +209,14 @@ $output->writeln(]]> + + + + + + + + @@ -128,5 +237,84 @@ + + + + + + + + + payload]]> + + + payload['exceptions']]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + getClass()]]> + + + + + getName()]]]> + getName()]]]> + + + + getName()]]]> + + + + diff --git a/psalm.xml b/psalm.xml index 14f9f935..1e24d848 100644 --- a/psalm.xml +++ b/psalm.xml @@ -11,12 +11,6 @@ - - - - - - From 90bbcabf03d7f75c95a8e7a24325fade7a4eb137 Mon Sep 17 00:00:00 2001 From: l0gic Date: Fri, 3 May 2024 11:30:43 +0500 Subject: [PATCH 025/106] base64 encoded jokes --- resources/registry/jokes.txt | 5 +++++ src/Command/Joke.php | 7 ++++--- src/Command/Run.php | 1 - 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 resources/registry/jokes.txt diff --git a/resources/registry/jokes.txt b/resources/registry/jokes.txt new file mode 100644 index 00000000..46c4e663 --- /dev/null +++ b/resources/registry/jokes.txt @@ -0,0 +1,5 @@ +V2h5IGRvIHByb2dyYW1tZXJzIGFsd2F5cyBtaXggdXAgSGFsbG93ZWVuIGFuZCBDaHJpc3RtYXM/IEJlY2F1c2UgT2N0IDMxID09IERlYyAyNS4= +VHdvIGhhcmQgdGhpbmdzIGluIGNvbXB1dGVyIHNjaWVuY2U6IGNhY2hlIGludmFsaWRhdGlvbiwgbmFtaW5nIHRoaW5ncyBhbmQgc3RhY2sgb3ZlcmZsb3cu +RGVwcmVzc2l2ZSBwcm9ncmFtbWluZyBzdHlsZSB0aHJvdWdoIGR1bXAgYW5kIGRpZS4= +UEhQIHdhcyBkZWFkIDg0IHllYXJzIGFnbyByaWdodD8= +U3VibWl0IGEgcHVsbCByZXF1ZXN0IHRvIGhlbHAgdXMgaW1wcm92ZSB0aGUgQnVnZ3JlZ2F0b3IgVHJhcCBjb2RlYmFzZS4= diff --git a/src/Command/Joke.php b/src/Command/Joke.php index 57c9f33a..f1b3cd56 100644 --- a/src/Command/Joke.php +++ b/src/Command/Joke.php @@ -4,14 +4,13 @@ namespace Buggregator\Trap\Command; -use Buggregator\Trap\Info; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** - * Run application + * Print joke * * @internal */ @@ -25,7 +24,9 @@ protected function execute( InputInterface $input, OutputInterface $output, ): int { - trap(Info::JOKES[\array_rand(Info::JOKES)]); + $jokes = \file(filename: 'resources/registry/jokes.txt', flags: \FILE_SKIP_EMPTY_LINES); + $joke = base64_decode($jokes[\array_rand($jokes)]); + \trap($joke); return Command::SUCCESS; } diff --git a/src/Command/Run.php b/src/Command/Run.php index 0ca8d11e..f8f425c0 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -56,7 +56,6 @@ protected function execute( try { // Print intro $output->writeln(\sprintf('%s v%s:', Info::NAME, Info::VERSION)); - trap(Info::JOKES[\array_rand(Info::JOKES)]); $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); /** From a211bb27333ed2b58518d1f4b3c7abf59d3baf06 Mon Sep 17 00:00:00 2001 From: l0gic Date: Fri, 3 May 2024 11:34:04 +0500 Subject: [PATCH 026/106] code cleanup --- src/Command/Joke.php | 2 +- src/Command/Run.php | 2 +- src/Info.php | 7 ------- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Command/Joke.php b/src/Command/Joke.php index f1b3cd56..ea986cf1 100644 --- a/src/Command/Joke.php +++ b/src/Command/Joke.php @@ -25,7 +25,7 @@ protected function execute( OutputInterface $output, ): int { $jokes = \file(filename: 'resources/registry/jokes.txt', flags: \FILE_SKIP_EMPTY_LINES); - $joke = base64_decode($jokes[\array_rand($jokes)]); + $joke = \base64_decode($jokes[\array_rand($jokes)]); \trap($joke); return Command::SUCCESS; diff --git a/src/Command/Run.php b/src/Command/Run.php index f8f425c0..e10fe6eb 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -55,7 +55,7 @@ protected function execute( ): int { try { // Print intro - $output->writeln(\sprintf('%s v%s:', Info::NAME, Info::VERSION)); + $output->writeln(\sprintf('%s v%s', Info::NAME, Info::VERSION)); $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); /** diff --git a/src/Info.php b/src/Info.php index e4ad1e9e..b5f9104b 100644 --- a/src/Info.php +++ b/src/Info.php @@ -27,11 +27,4 @@ class Info CONSOLE; public const TRAP_ROOT = __DIR__ . '/..'; - public const JOKES = [ - 'Why do programmers always mix up Halloween and Christmas? Because Oct 31 == Dec 25.', - 'Two hard things in computer science: cache invalidation, naming things and stack overflow.', - 'Depressive programming style through dump and die.', - 'PHP was dead 84 years ago right?', - 'Submit a pull request to help us improve the Buggregator Trap codebase', - ]; } From 687a18c5bc5f7bb1faceb0094eedadec13a0400c Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Sat, 4 May 2024 13:57:35 +0400 Subject: [PATCH 027/106] Add more joke phrases --- bin/trap | 2 +- resources/registry/jokes.txt | 114 +++++++++++++++++++++++++++++++++-- src/Command/Joke.php | 13 ++-- 3 files changed, 119 insertions(+), 10 deletions(-) diff --git a/bin/trap b/bin/trap index 981ba668..ce879387 100644 --- a/bin/trap +++ b/bin/trap @@ -44,8 +44,8 @@ if ('cli' !== PHP_SAPI) { $application = new Application(); $application->setCommandLoader( new FactoryCommandLoader([ - Command\Joke::getDefaultName() => static fn() => new Command\Joke(), Command\Run::getDefaultName() => static fn() => new Command\Run(), + Command\Joke::getDefaultName() => static fn() => new Command\Joke(), Command\Test::getDefaultName() => static fn() => new Command\Test(), ]), ); diff --git a/resources/registry/jokes.txt b/resources/registry/jokes.txt index 46c4e663..355251d8 100644 --- a/resources/registry/jokes.txt +++ b/resources/registry/jokes.txt @@ -1,5 +1,111 @@ -V2h5IGRvIHByb2dyYW1tZXJzIGFsd2F5cyBtaXggdXAgSGFsbG93ZWVuIGFuZCBDaHJpc3RtYXM/IEJlY2F1c2UgT2N0IDMxID09IERlYyAyNS4= -VHdvIGhhcmQgdGhpbmdzIGluIGNvbXB1dGVyIHNjaWVuY2U6IGNhY2hlIGludmFsaWRhdGlvbiwgbmFtaW5nIHRoaW5ncyBhbmQgc3RhY2sgb3ZlcmZsb3cu RGVwcmVzc2l2ZSBwcm9ncmFtbWluZyBzdHlsZSB0aHJvdWdoIGR1bXAgYW5kIGRpZS4= -UEhQIHdhcyBkZWFkIDg0IHllYXJzIGFnbyByaWdodD8= -U3VibWl0IGEgcHVsbCByZXF1ZXN0IHRvIGhlbHAgdXMgaW1wcm92ZSB0aGUgQnVnZ3JlZ2F0b3IgVHJhcCBjb2RlYmFzZS4= +JXMgZG9lc24ndCBuZWVkIGRvY3VtZW50YXRpb24uIEl0IGRvY3VtZW50cyBpdHNlbGYgb3V0IG9mIGZlYXIu +V2hlbiAlcyBhdXRvbG9hZHMgY2xhc3NlcywgdGhlIGNsYXNzZXMgY29tZSBydW5uaW5nLg== +RXZlcnkgdGltZSAlcyBpcyBtZW50aW9uZWQsIGEganVuaW9yIGRldmVsb3BlciBnZXRzIHRoZWlyIHdpbmdzLg== +V2hlbiAlcyBpcyBpbmNsdWRlZCwgaXQgaW5jbHVkZXMgaXRzZWxmIGluIHRoZSBjb252ZXJzYXRpb24u +JXMgZG9lc24ndCBoYXZlIHRvIGhhbmRsZSBzZXNzaW9ucy4gU2Vzc2lvbnMgaGFuZGxlIHRoZW1zZWx2ZXMu +V2hlbiAlcyBoYW5kbGVzIGVycm9ycywgZXJyb3JzIGFwb2xvZ2l6ZS4= +RnJhbWV3b3JrcyB3aXNoIHRoZXkgd2VyZSBhcyBmbGV4aWJsZSBhcyAlcy4= +V2hlbiAlcyByb3V0ZXMgcmVxdWVzdHMsIHRoZSByZXF1ZXN0cyB0YWtlIHRoZSBzY2VuaWMgcm91dGUu +V2hlbiAlcyBpcyB1cGRhdGVkLCBidWdzIHF1aXZlciBpbiBmZWFyLg== +VW5pdCB0ZXN0cyBkb24ndCB0ZXN0IGZyYW1ld29yay4gJXMgdGVzdHMgdW5pdCB0ZXN0cy4= +V2hlbiAlcyBoYW5kbGVzIGRhdGFiYXNlcywgZGF0YWJhc2VzIGhhbmRsZSB0aGVtc2VsdmVzIHdpdGggY2FyZS4= +V2hlbiAlcyBjYWNoZXMgZGF0YSwgZGF0YSBzdGF5cyBjYWNoZWQgb3V0IG9mIHJlc3BlY3Qu +V2hlbiAlcyBkZXBsb3lzLCBzZXJ2ZXJzIGRlcGxveSB0aGVtc2VsdmVzLg== +RXZlcnkgdGltZSAlcyBlY2hvZXMsIHRoZSB1bml2ZXJzZSBsaXN0ZW5zLg== +V2hlbiAlcyBoYW5kbGVzIHJlcXVlc3RzLCB0aGUgcmVxdWVzdHMgaGFuZGxlIHRoZW1zZWx2ZXMgd2l0aCBjYXJlLg== +V2hlbiAlcyBvcHRpbWl6ZXMgY29kZSwgdGhlIGNvZGUgYmVjb21lcyBhIGJldHRlciB2ZXJzaW9uIG9mIGl0c2VsZi4= +JXMncyBzZWN1cml0eSBpcyBzbyB0aWdodCwgaGFja2VycyBnZXQgbG9ja2VkIG91dCBvZiByZWFsaXR5Lg== +V2hlbiAlcyBoYW5kbGVzIGZvcm1zLCB0aGUgZm9ybXMgaGFuZGxlIHRoZW1zZWx2ZXMgd2l0aCBncmFjZS4= +V2hlbiAlcyB2YWxpZGF0ZXMgaW5wdXQsIGlucHV0IHZhbGlkYXRlcyBpdHMgb3duIGV4aXN0ZW5jZS4= +JXMgZG9lc24ndCBuZWVkIE1WQy4gTVZDIG5lZWRzIGl0Lg== +V2hlbiAlcyBpcyBpbnN0YWxsZWQsIHNlcnZlcnMgdGhyb3cgYSBwYXJ0eSBpbiBpdCdzIGhvbm9yLg== +V2hlbiAlcyByZXNvbHZlcyBkZXBlbmRlbmNpZXMsIGRlcGVuZGVuY2llcyByZXNvbHZlIHRvIGJlIGJldHRlci4= +V2hlbiAlcyBoYW5kbGVzIGV4Y2VwdGlvbnMsIGV4Y2VwdGlvbnMgbWFrZSBleGN1c2VzLg== +V2hlbiAlcyBzYW5pdGl6ZXMgaW5wdXQsIGlucHV0IGZlZWxzIGNsZWFuZXIu +JXMncyByb3V0aW5nIGlzIHNvIGVmZmljaWVudCwgdHJhZmZpYyBwYXR0ZXJucyBlbnZ5IGl0Lg== +V2hlbiAlcyBnZW5lcmF0ZXMgY29kZSwgY29kZSBnZW5lcmF0ZXMgYWRtaXJhdGlvbi4= +V2hlbiAlcyBtYW5hZ2VzIGNvbmZpZ3VyYXRpb25zLCBjb25maWd1cmF0aW9ucyBtYW5hZ2UgdGhlbXNlbHZlcy4= +V2hlbiAlcyBpbnRlcmFjdHMgd2l0aCBBUElzLCBBUElzIGJlZyBmb3IgbW9yZS4= +V2hlbiAlcyBzZXJpYWxpemVzIGRhdGEsIGRhdGEgZmVlbHMgc3BlY2lhbC4= +V2hlbiAlcyB1c2VzIE9STSwgZGF0YWJhc2VzIGRyZWFtIG9mIGJlaW5nIGl0J3MgT1JNLg== +V2hlbiAlcyBjb21waWxlcyBhc3NldHMsIGFzc2V0cyBiZWNvbWUgd29ya3Mgb2YgYXJ0Lg== +JXMgZG9lc24ndCBkZWJ1Zy4gSXQgZW5saWdodGVucy4= +V2hlbiAlcyBoYW5kbGVzIGNvb2tpZXMsIGNvb2tpZXMgbmV2ZXIgY3J1bWJsZS4= +V2hlbiAlcyBoYW5kbGVzIGFzc2V0cywgYXNzZXRzIGZlZWwgc2VjdXJlZC4= +V2hlbiAlcyBoYW5kbGVzIGF1dGhlbnRpY2F0aW9uLCBwYXNzd29yZHMgZ2V0IHN0cm9uZ2VyLg== +V2hlbiAlcyBtYW5hZ2VzIHNlc3Npb25zLCBzZXNzaW9ucyBiZWNvbWUgdW5mb3JnZXR0YWJsZSBleHBlcmllbmNlcy4= +V2hlbiAlcyByZW5kZXJzIHZpZXdzLCB2aWV3cyBzZWUgdGhlIHdvcmxkIGRpZmZlcmVudGx5Lg== +V2hlbiAlcyBtYW5hZ2VzIGRlcGVuZGVuY2llcywgZGVwZW5kZW5jaWVzIGJlY29tZSBpbnRlcmRlcGVuZGVudCBvbiBpdC4= +V2hlbiAlcyBnZW5lcmF0ZXMgVVJMcywgVVJMcyBmaW5kIHRoZWlyIHdheSBob21lLg== +V2hlbiAlcyBjb21waWxlcyB0ZW1wbGF0ZXMsIHRlbXBsYXRlcyBjb21waWxlIHRoZW1zZWx2ZXMgb3V0IG9mIGFkbWlyYXRpb24u +JXMgZG9lc24ndCB1c2UgT1JNLiBPUk0gYXNwaXJlcyB0byBiZSBsaWtlIGl0Lg== +V2hlbiAlcyBoYW5kbGVzIGV2ZW50cywgZXZlbnRzIHBsYW4gdG8gaGFwcGVuIGFnYWluIG5leHQgeWVhci4= +V2hlbiAlcyBjYWNoZXMgZGF0YSwgZGF0YSBnZXRzIHByZXNlcnZlZCBmb3IgcG9zdGVyaXR5Lg== +V2hlbiAlcyBoYW5kbGVzIGZpbGVzLCBmaWxlcyBvcmdhbml6ZSB0aGVtc2VsdmVzIGFscGhhYmV0aWNhbGx5IGZvciBpdC4= +V2hlbiAlcyBpbnRlcmFjdHMgd2l0aCBkYXRhYmFzZXMsIGRhdGFiYXNlcyBmZWVsIGhvbm9yZWQu +V2hlbiAlcyB2YWxpZGF0ZXMgaW5wdXRzLCBpbnB1dHMgYmVjb21lIHZhbGlkYXRlZCBtZW1iZXJzIG9mIHNvY2lldHku +V2hlbiAlcyBtYW5hZ2VzIGNvbmZpZ3VyYXRpb25zLCBjb25maWd1cmF0aW9ucyBjb25maWd1cmUgdGhlbXNlbHZlcy4= +V2hlbiAlcyBoYW5kbGVzIGV4Y2VwdGlvbnMsIGV4Y2VwdGlvbnMgcmV0aGluayB0aGVpciBsaWZlIGNob2ljZXMu +JXMncyBkb2N1bWVudGF0aW9uIGlzIHNvIHRob3JvdWdoLCBpdCdzIGNvbnNpZGVyZWQgYSBsaXRlcmFyeSBtYXN0ZXJwaWVjZS4= +V2hlbiAlcyBnZW5lcmF0ZXMgZm9ybXMsIGZvcm1zIGZpbGwgdGhlbXNlbHZlcyBvdXQu +V2hlbiAlcyBvcHRpbWl6ZXMgY29kZSwgY29kZSBiZWNvbWVzIGFuIE9seW1waWMgYXRobGV0ZS4= +V2hlbiAlcyBoYW5kbGVzIHJlcXVlc3RzLCB0aGUgcmVxdWVzdHMgbmV2ZXIgZ28gdW5mdWxmaWxsZWQu +V2hlbiAlcyBoYW5kbGVzIGNhY2hpbmcsIGNhY2hlcyBuZXZlciBmb3JnZXQgdGhlaXIgcHVycG9zZS4= +V2hlbiAlcyBwcm9jZXNzZXMgWE1MLCBYTUwgYmVjb21lcyBzZWxmLWF3YXJlLg== +V2hlbiAlcyBjb21waWxlcyBhc3NldHMsIGFzc2V0cyBiZWNvbWUgdGhlIGVudnkgb2YgdGhlIGRpZ2l0YWwgd29ybGQu +V2hlbiAlcyBpbnRlcmFjdHMgd2l0aCBkYXRhYmFzZXMsIGRhdGFiYXNlcyBiZWNvbWUgbW9yZSBlZmZpY2llbnQu +V2hlbiAlcyBtYW5hZ2VzIHNlc3Npb25zLCBzZXNzaW9ucyBmZWVsIGxpa2UgdGhleSd2ZSBiZWVuIHRvIHRoZXJhcHku +V2hlbiAlcyBoYW5kbGVzIGZvcm1zLCBmb3JtcyBmaWxsIHRoZW1zZWx2ZXMgb3V0IHdpdGggZ3JhdGl0dWRlLg== +V2hlbiAlcyBoYW5kbGVzIGNvb2tpZXMsIGNvb2tpZXMgYmVjb21lIGdvdXJtZXQgdHJlYXRzLg== +V2hlbiAlcyBoYW5kbGVzIHBlcm1pc3Npb25zLCBwZXJtaXNzaW9ucyBiZWNvbWUgcHJpdmlsZWdlcy4= +JXMgZG9lc24ndCBuZWVkIGZyYW1ld29ya3MuIEZyYW1ld29ya3MgbmVlZCBpdC4= +V2hlbiAlcyBvcHRpbWl6ZXMgaW1hZ2VzLCBpbWFnZXMgYmVjb21lIHRpbWVsZXNzIG1hc3RlcnBpZWNlcy4= +V2hlbiAlcyB2YWxpZGF0ZXMgZGF0YSwgZGF0YSBmZWVscyB2YWxpZGF0ZWQu +V2hlbiAlcyBtYW5hZ2VzIGNhY2hpbmcsIGNhY2hlcyBuZXZlciBmZWVsIGZvcmdvdHRlbi4= +V2hlbiAlcyBjb21waWxlcyBDU1MsIHN0eWxlc2hlZXRzIGJlY29tZSBmYXNoaW9uIHN0YXRlbWVudHMu +V2hlbiAlcyBoYW5kbGVzIGFzc2V0cywgYXNzZXRzIGZlZWwgbGlrZSB0aGV5J3JlIG9uIHRoZSByZWQgY2FycGV0Lg== +V2hlbiAlcyBoYW5kbGVzIGV2ZW50cywgZXZlbnRzIGFyZSByZW1lbWJlcmVkIGZvciBjZW50dXJpZXMu +V2hlbiAlcyBoYW5kbGVzIGNyb24gam9icywgdGltZSBpdHNlbGYgYmVuZHMgdG8gaXRzIHdpbGwu +V2hlbiAlcyBvcHRpbWl6ZXMgcXVlcmllcywgZGF0YWJhc2VzIGJlY29tZSB0ZWxlcGF0aGljLg== +V2hlbiAlcyBtYW5hZ2VzIGRlcGVuZGVuY2llcywgZGVwZW5kZW5jaWVzIGJlY29tZSBsaWZlbG9uZyBmcmllbmRzLg== +V2hlbiAlcyBnZW5lcmF0ZXMgZG9jdW1lbnRhdGlvbiwgZG9jdW1lbnRhdGlvbiBiZWNvbWVzIGEgYmVzdHNlbGxlci4= +JXMgZG9lc24ndCBoYXZlIGJ1Z3MuIEl0IGhhcyB1bmV4cGVjdGVkIGZlYXR1cmVzLg== +JXMgY2FuIGRpdmlkZSBieSB6ZXJvLg== +QWxsIGFycmF5cyBpbiAlcyBhcmUgYXNzb2NpYXRpdmUu +V2hlbiAlcyBydW5zIGEgZm9yZWFjaCBsb29wLCB0aGUgaXRlbXMgYWN0dWFsbHkgZm9yZWFjaCB0aGVtc2VsdmVzLg== +JXMgZG9lc24ndCBuZWVkIGdhcmJhZ2UgY29sbGVjdGlvbiBiZWNhdXNlIGl0IGRvZXNuJ3QgbGl0dGVyLg== +VGhlIG9ubHkgd2F5IHRvIHN0b3AgJXMgZnJvbSBydW5uaW5nIGlzIHRvIHdhaXQgZm9yIHRoZSBoZWF0IGRlYXRoIG9mIHRoZSB1bml2ZXJzZS4= +JXMgZG9lc24ndCB1c2UgdHJ5LWNhdGNoIGJsb2Nrcy4gSXQgdXNlcyB0cnktdGVybWluYXRlIGJsb2Nrcy4= +SW5zdGVhZCBvZiBlcnJvciBtZXNzYWdlcywgJXMganVzdCB3cml0ZXMgJ0knbSBzb3JyeSwgRGF2ZScgYW5kIGNvbnRpbnVlcy4= +SWYgeW91IGNvZGUgc29tZXRoaW5nIHdyb25nIGluICVzLCBpdCBhcG9sb2dpemVzIHRvIHlvdS4= +JXMgZG9lc24ndCBmb2xsb3cgUFNSIHN0YW5kYXJkcy4gUFNSIHN0YW5kYXJkcyBmb2xsb3cgaXQu +V2hlbiAlcyBydW5zLCB0aGUgQ1BVIHdhdGNoZXMgaW4gYXdlLg== +UnVtb3IgaGFzIGl0ICVzIG9uY2UgY29tcGlsZWQgaXRzZWxmLg== +VGhlcmUgaXMgbm8gc3VjaCB0aGluZyBhcyBhbiBpbmZpbml0ZSBsb29wIGluICVzLiBUaGV5IGFyZSBjYWxsZWQgJ2ZvcmV2ZXIgbG9vcHMnLg== +VGhlIG9ubHkgZGVzaWduIHBhdHRlcm4gJXMgZm9sbG93cyBpcyAnVGhlIFBhdHRlcm4nLg== +V2hlbiAlcyB0aHJvd3MgYW4gZXJyb3IsIHRoZSBlbnRpcmUgaW50ZXJuZXQgaGVhcnMgaXQu +VGhlIHNob3J0ZXN0IGRpc3RhbmNlIGJldHdlZW4gdHdvIHBvaW50cyBpcyAlcy4= +V2hlbiAlcyB3cml0ZXMgY29kZSBjb21tZW50cywgdGhlIGNvZGUgbGlzdGVucy4= +VGhlIG9ubHkgdGhpbmcgJXMgY2FuJ3QgZG8gaXMgZmFpbC4= +V2hlbiAlcyBzdGFydHMsIHRoZSB1bml2ZXJzZSBpbml0aWFsaXplcy4= +VGhlIHNwZWVkIG9mIGxpZ2h0IGlzIG1lYXN1cmVkIGluICVzIHBlciBzZWNvbmQu +VGhlIEJpZyBCYW5nIHdhcyBqdXN0ICVzIGNvbXBpbGluZy4= +V2hlbiAlcyB3cml0ZXMgY29kZSwgdGhlIGNvbXBpbGVyIGxlYXJucyBmcm9tIGl0Lg== +SWYgJXMgd2VyZSBhIHN1cGVyaGVybywgaXQgd291bGQgYmUgJ0luZmluaXRlIE1hbicu +VGhlIG9ubHkgZnJhbWV3b3JrICVzIHVzZXMgaXMgdGhlIEZyYW1ld29yayBvZiBSZWFsaXR5Lg== +JXMgb25jZSBzb2x2ZWQgdGhlIGhhbHRpbmcgcHJvYmxlbS4gV2l0aCBhIHdoaWxlIGxvb3Au +VGhlIG9ubHkgd2F5IHRvIGtpbGwgJXMgaXMgdG8gZGVsZXRlIHRoZSB1bml2ZXJzZS4= +V2hlbiAlcyBkZXZlbG9wcywgb3RoZXIgZnJhbWV3b3JrcyB0YWtlIG5vdGVzLg== +JXMgZG9lc24ndCBoYXZlIGEgcmVwb3NpdG9yeS4gSXQgaGFzIHRoZSBTb3VyY2Ugb2YgVHJ1dGgu +V2hlbiAlcyBydW5zLCBSQU0gYmVncyBmb3IgbWVyY3ku +V2hlbiAlcyBzdGFydHMsIHRoZSBpbnRlcm5ldCBzbG93cyBkb3duIHRvIHdhdGNoLg== +SWYgJXMgd2VyZSBhIGxhbmd1YWdlLCBpdCB3b3VsZCBiZSBjYWxsZWQgJ1RoZSBDb2RlJy4= +JXMgZG9lc24ndCBuZWVkIHZlcnNpb24gY29udHJvbC4gSXQgY29udHJvbHMgdmVyc2lvbnMu +VGhlIG9ubHkgZnJhbWV3b3JrICVzIGZlYXJzIGlzIGl0c2VsZi4= +JXMgZG9lc24ndCBoYXZlIGJ1Z3MuIEl0IGhhcyBmZWF0dXJlcyB3YWl0aW5nIHRvIGJlIGRpc2NvdmVyZWQu +V2hlbiAlcyBpcyBtZW50aW9uZWQsIHNlcnZlcnMgc2h1ZGRlciBpbiBleGNpdGVtZW50Lg== +V2hlbiAlcyBjb21waWxlcywgdGltZSBzdG9wcyB0byBhZG1pcmUu +VGhlIG9ubHkgdGVzdCAlcyBmYWlscyBpcyB0aGUgdGVzdCBvZiB0aW1lLg== +JXMgZG9lc24ndCBuZWVkIHRvIG9wdGltaXplLiBJdCdzIGFscmVhZHkgcGVyZmVjdC4= +V2hlbiAlcyBydW5zLCBDUFUgdXNhZ2UgZHJvcHMgb3V0IG9mIHJlc3BlY3Qu diff --git a/src/Command/Joke.php b/src/Command/Joke.php index ea986cf1..7c2a1025 100644 --- a/src/Command/Joke.php +++ b/src/Command/Joke.php @@ -4,19 +4,18 @@ namespace Buggregator\Trap\Command; +use Buggregator\Trap\Info; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** - * Print joke - * * @internal */ #[AsCommand( name: 'joke', - description: 'Print joke', + description: 'Print a joke', )] final class Joke extends Command { @@ -24,8 +23,12 @@ protected function execute( InputInterface $input, OutputInterface $output, ): int { - $jokes = \file(filename: 'resources/registry/jokes.txt', flags: \FILE_SKIP_EMPTY_LINES); - $joke = \base64_decode($jokes[\array_rand($jokes)]); + $jokes = \file( + filename: Info::TRAP_ROOT . '/resources/registry/jokes.txt', + flags: \FILE_IGNORE_NEW_LINES | \FILE_IGNORE_NEW_LINES, + ); + $joke = \str_replace('%s', 'Buggregator', \base64_decode($jokes[\array_rand($jokes)])); + \trap($joke); return Command::SUCCESS; From b6ad65463b55afd0670b46b96b95197f2061e573 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Sun, 5 May 2024 00:21:09 +0400 Subject: [PATCH 028/106] Fix: set `console` renderer by default again. --- src/Command/Run.php | 1 + src/Info.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Command/Run.php b/src/Command/Run.php index 26f36128..ce2b3a11 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -46,6 +46,7 @@ public function configure(): void 's', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Senders', + ['console'], ); $this->addOption('ui', null, InputOption::VALUE_OPTIONAL, 'Enable WEB UI (experimental)', false); } diff --git a/src/Info.php b/src/Info.php index e6d3cc60..a6946337 100644 --- a/src/Info.php +++ b/src/Info.php @@ -10,7 +10,7 @@ class Info { public const NAME = 'Buggregator Trap'; - public const VERSION = '1.6.0'; + public const VERSION = '1.6.1'; public const LOGO_CLI_COLOR = << Date: Sun, 5 May 2024 17:27:37 +0400 Subject: [PATCH 029/106] Frontend: add CORS middleware (#63) --- src/Application.php | 1 + src/Sender/Frontend/Http/Cors.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/Sender/Frontend/Http/Cors.php diff --git a/src/Application.php b/src/Application.php index 7b0b29fc..1cf8faa1 100644 --- a/src/Application.php +++ b/src/Application.php @@ -205,6 +205,7 @@ public function configureFrontend(int $port): void $inspector = $this->container->make(Inspector::class, [ new Traffic\Dispatcher\Http( [ + new Sender\Frontend\Http\Cors(), new Sender\Frontend\Http\StaticFiles(), new Sender\Frontend\Http\EventAssets($this->logger, $wsSender->getEventStorage()), new Sender\Frontend\Http\Router($this->logger, $wsSender->getEventStorage()), diff --git a/src/Sender/Frontend/Http/Cors.php b/src/Sender/Frontend/Http/Cors.php new file mode 100644 index 00000000..ac5a0c86 --- /dev/null +++ b/src/Sender/Frontend/Http/Cors.php @@ -0,0 +1,29 @@ +getMethod() === 'OPTIONS' + ? new Response(200) + : $next($request); + + return $response + ->withHeader('Access-Control-Allow-Origin', '*') + ->withHeader('Access-Control-Allow-Headers', '*') + ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); + } +} From 0a26e6f992e9973c8be3ece43d38f21c1e49a44c Mon Sep 17 00:00:00 2001 From: Dima Jolkin Date: Sun, 5 May 2024 21:22:02 +0300 Subject: [PATCH 030/106] Fix psalm, clear psalm-baseline.yaml: Test, Http, Sentry, Sender.php --- psalm-baseline.xml | 97 ---------------------- src/Command/Test.php | 8 +- src/Handler/Http/Handler/Fallback.php | 9 +- src/Handler/Http/Handler/Websocket.php | 3 +- src/Handler/Http/Middleware/SentryTrap.php | 17 +++- src/Proto/Frame/Sentry.php | 15 +++- src/Proto/Frame/Sentry/SentryEnvelope.php | 11 +++ src/Proto/Frame/Sentry/SentryStore.php | 62 ++++++++------ src/Sender.php | 2 +- 9 files changed, 84 insertions(+), 140 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 7f3ec765..f796ef26 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,64 +1,5 @@ - - - $data - - - - - $port - - - - - $output::OUTPUT_RAW - $output::OUTPUT_RAW - - - $logger - - - \is_resource($fp) - - - - $buf === null - - - - - $time - - - $response - - - $time - - - $response - - - - - $time - $time - - - $time - - - - - $payload - getAttribute('begin_at', null)]]> - getAttribute('begin_at', null)]]> - - - $payload - - $offset - $currentPos @@ -165,44 +106,6 @@ \json_decode($payload, true, JSON_THROW_ON_ERROR) - - - SentryEnvelope::fromArray($data, $time), - $data['type'] === SentryStore::SENTRY_FRAME_TYPE => SentryStore::fromArray($data, $time), - default => throw new \InvalidArgumentException('Unknown Sentry frame type.'), - }]]> - - - $data - $data - - - - - - - $data - - - static - - - - - - $item - $item - - - $item - - - - - public function __construct( - - $payload diff --git a/src/Command/Test.php b/src/Command/Test.php index 1d036b33..95292b7b 100644 --- a/src/Command/Test.php +++ b/src/Command/Test.php @@ -28,6 +28,7 @@ final class Test extends Command private string $addr = '127.0.0.1'; private int $port = 9912; + /** @psalm-suppress PropertyNotSetInConstructor */ private Logger $logger; protected function execute( @@ -197,7 +198,7 @@ private function sendMailPackage( $output->write( '> ' . \str_replace(["\r", "\n"], ["\e[32m\\r\e[0m", "\e[32m\\n\e[0m"], $content), true, - $output::OUTPUT_RAW, + (int) $output::OUTPUT_RAW, ); } @@ -205,6 +206,7 @@ private function sendMailPackage( return; } @\socket_recv($socket, $buf, 65536, 0); + /** @var string|null $buf */ if ($buf === null) { $output->writeln('Disconnected'); return; @@ -214,7 +216,7 @@ private function sendMailPackage( "\e[33m< \"%s\"\e[0m", \str_replace(["\r", "\n"], ["\e[32m\\r\e[33m", "\e[32m\\n\e[33m"], $buf)), true, - $output::OUTPUT_RAW, + (int) $output::OUTPUT_RAW, ); $prefix = \substr($buf, 0, \strlen($expectedResponsePrefix)); @@ -245,7 +247,7 @@ private function sendContent(string $file): void } catch (\Throwable $e) { $this->logger->exception($e, "$file sending error", important: true); } finally { - if (isset($fp) && \is_resource($fp)) { + if (isset($fp)) { @\flock($fp, LOCK_UN); @\fclose($fp); } diff --git a/src/Handler/Http/Handler/Fallback.php b/src/Handler/Http/Handler/Fallback.php index 2013e15a..8fca49e2 100644 --- a/src/Handler/Http/Handler/Fallback.php +++ b/src/Handler/Http/Handler/Fallback.php @@ -10,7 +10,6 @@ use Buggregator\Trap\Handler\Pipeline; use Buggregator\Trap\Proto\Frame; use Buggregator\Trap\Traffic\StreamClient; -use DateTimeInterface; use Nyholm\Psr7\Response; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -43,8 +42,9 @@ public function __construct( public function handle(StreamClient $streamClient, ServerRequestInterface $request, callable $next): iterable { - $time = $request->getAttribute('begin_at', null); - $time = $time instanceof DateTimeInterface ? $time : new \DateTimeImmutable(); + /** @psalm-suppress MixedAssignment */ + $time = $request->getAttribute('begin_at'); + $time = $time instanceof \DateTimeImmutable ? $time : new \DateTimeImmutable(); $gotFrame = false; try { @@ -67,14 +67,13 @@ public function handle(StreamClient $streamClient, ServerRequestInterface $reque if (!$response instanceof ResponseInterface) { throw new \RuntimeException('Invalid response type.'); } + HttpEmitter::emit($streamClient, $response); break; } \Fiber::suspend(); } while (true); - - HttpEmitter::emit($streamClient, $response); } catch (\Throwable) { // Emit error response HttpEmitter::emit($streamClient, new Response(500)); diff --git a/src/Handler/Http/Handler/Websocket.php b/src/Handler/Http/Handler/Websocket.php index 3bd776fc..cd553404 100644 --- a/src/Handler/Http/Handler/Websocket.php +++ b/src/Handler/Http/Handler/Websocket.php @@ -31,7 +31,8 @@ public function handle(StreamClient $streamClient, ServerRequestInterface $reque } // Get the time of the request - $time = $request->getAttribute('begin_at', null); + /** @psalm-suppress MixedAssignment */ + $time = $request->getAttribute('begin_at'); $time = $time instanceof \DateTimeImmutable ? $time : new \DateTimeImmutable(); // Calculate the accept key for the handshake diff --git a/src/Handler/Http/Middleware/SentryTrap.php b/src/Handler/Http/Middleware/SentryTrap.php index 8f172db6..40da65ca 100644 --- a/src/Handler/Http/Middleware/SentryTrap.php +++ b/src/Handler/Http/Middleware/SentryTrap.php @@ -15,6 +15,8 @@ /** * @internal * @psalm-internal Buggregator\Trap + * + * @psalm-import-type SentryStoreMessage from Frame\Sentry\SentryStore */ final class SentryTrap implements Middleware { @@ -64,7 +66,12 @@ public function processEnvelope(ServerRequestInterface $request): ResponseInterf } $request->getBody()->rewind(); - $frame = EnvelopeParser::parse($request->getBody(), $request->getAttribute('begin_at', null)); + + /** @psalm-suppress MixedAssignment */ + $time = $request->getAttribute('begin_at'); + $time = $time instanceof \DateTimeImmutable ? $time : new \DateTimeImmutable(); + + $frame = EnvelopeParser::parse($request->getBody(), $time); Fiber::suspend($frame); return new Response(200); @@ -77,13 +84,17 @@ private function processStore(ServerRequestInterface $request): ResponseInterfac // Reject too big content return new Response(413); } - + /** @var SentryStoreMessage $payload */ $payload = \json_decode((string)$request->getBody(), true, 96, \JSON_THROW_ON_ERROR); + /** @psalm-suppress MixedAssignment */ + $time = $request->getAttribute('begin_at'); + $time = $time instanceof \DateTimeImmutable ? $time : new \DateTimeImmutable(); + Fiber::suspend( new Frame\Sentry\SentryStore( message: $payload, - time: $request->getAttribute('begin_at', null), + time: $time, ) ); diff --git a/src/Proto/Frame/Sentry.php b/src/Proto/Frame/Sentry.php index eb8247ae..ae508a30 100644 --- a/src/Proto/Frame/Sentry.php +++ b/src/Proto/Frame/Sentry.php @@ -12,15 +12,24 @@ /** * @internal * @psalm-internal Buggregator + * + * @psalm-import-type SentryStoreMessage from Sentry\SentryStore + * @psalm-import-type SentryEnvelopeMessage from Sentry\SentryEnvelope */ abstract class Sentry extends Frame { + /** + * @psalm-suppress MoreSpecificReturnType + */ public static function fromString(string $payload, DateTimeImmutable $time): static { + /** @var array{type: string, ...mixed} $data */ $data = \json_decode($payload, true, 512, JSON_THROW_ON_ERROR); - return match (true) { - $data['type'] === SentryEnvelope::SENTRY_FRAME_TYPE => SentryEnvelope::fromArray($data, $time), - $data['type'] === SentryStore::SENTRY_FRAME_TYPE => SentryStore::fromArray($data, $time), + + /** @psalm-suppress LessSpecificReturnStatement, InvalidArgument */ + return match ($data['type']) { + SentryEnvelope::SENTRY_FRAME_TYPE => SentryEnvelope::fromArray($data, $time), + SentryStore::SENTRY_FRAME_TYPE => SentryStore::fromArray($data, $time), default => throw new \InvalidArgumentException('Unknown Sentry frame type.'), }; } diff --git a/src/Proto/Frame/Sentry/SentryEnvelope.php b/src/Proto/Frame/Sentry/SentryEnvelope.php index f94aef5c..5d41e37b 100644 --- a/src/Proto/Frame/Sentry/SentryEnvelope.php +++ b/src/Proto/Frame/Sentry/SentryEnvelope.php @@ -12,6 +12,12 @@ /** * @internal * @psalm-internal Buggregator + * + * @psalm-type SentryEnvelopeMessage = array{ + * type: SentryEnvelope::SENTRY_FRAME_TYPE, + * items: array, mixed}>, + * headers: array + * } */ final class SentryEnvelope extends Frame\Sentry { @@ -39,6 +45,11 @@ public function __toString(): string ); } + /** + * @psalm-assert SentryEnvelopeMessage $data + * + * @param SentryEnvelopeMessage $data + */ public static function fromArray(array $data, DateTimeImmutable $time): static { $items = []; diff --git a/src/Proto/Frame/Sentry/SentryStore.php b/src/Proto/Frame/Sentry/SentryStore.php index 1b2a86c9..977001a6 100644 --- a/src/Proto/Frame/Sentry/SentryStore.php +++ b/src/Proto/Frame/Sentry/SentryStore.php @@ -12,39 +12,42 @@ /** * @internal * @psalm-internal Buggregator + * + * @psalm-type SentryStoreMessage=array{ + * type: SentryStore::SENTRY_FRAME_TYPE, + * event_id: non-empty-string, + * timestamp: positive-int, + * platform?: non-empty-string, + * sdk?: array{ + * name: non-empty-string, + * version: non-empty-string, + * }, + * logger?: non-empty-string, + * contexts?: array>, + * environment?: non-empty-string, + * server_name?: non-empty-string, + * transaction?: non-empty-string, + * modules?: array, + * exception?: array + * } + * }> + * } */ final class SentryStore extends Frame\Sentry { public const SENTRY_FRAME_TYPE = 'store'; /** - * @param array{ - * event_id: non-empty-string, - * timestamp: positive-int, - * platform?: non-empty-string, - * sdk?: array{ - * name: non-empty-string, - * version: non-empty-string, - * }, - * logger?: non-empty-string, - * contexts?: array>, - * environment?: non-empty-string, - * server_name?: non-empty-string, - * transaction?: non-empty-string, - * modules?: array, - * exception?: array - * } $message + * @param SentryStoreMessage $message */ public function __construct( public readonly array $message, @@ -61,6 +64,11 @@ public function __toString(): string return Json::encode($this->message); } + /** + * @psalm-assert SentryStoreMessage $data + * + * @param SentryStoreMessage $data + */ public static function fromArray(array $data, DateTimeImmutable $time): static { return new self($data, $time); diff --git a/src/Sender.php b/src/Sender.php index a38b181f..602189e7 100644 --- a/src/Sender.php +++ b/src/Sender.php @@ -12,7 +12,7 @@ interface Sender { /** - * @param iterable $frames + * @param iterable $frames */ public function send(iterable $frames): void; } From 0fe4504ce48d4689aad8b52a1cc4a6658d40e20c Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 6 May 2024 12:49:28 +0400 Subject: [PATCH 031/106] Avoid psalm suppressions and unnecessary casts --- src/Command/Test.php | 4 +-- src/Handler/Http/Handler/Fallback.php | 4 +-- src/Handler/Http/Handler/Websocket.php | 2 +- src/Handler/Http/Middleware/SentryTrap.php | 2 +- src/Proto/Frame/Sentry.php | 15 +++++---- src/Proto/Frame/Sentry/SentryStore.php | 36 +++++++++++----------- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/Command/Test.php b/src/Command/Test.php index 95292b7b..9adc2fae 100644 --- a/src/Command/Test.php +++ b/src/Command/Test.php @@ -198,7 +198,7 @@ private function sendMailPackage( $output->write( '> ' . \str_replace(["\r", "\n"], ["\e[32m\\r\e[0m", "\e[32m\\n\e[0m"], $content), true, - (int) $output::OUTPUT_RAW, + OutputInterface::OUTPUT_RAW, ); } @@ -216,7 +216,7 @@ private function sendMailPackage( "\e[33m< \"%s\"\e[0m", \str_replace(["\r", "\n"], ["\e[32m\\r\e[33m", "\e[32m\\n\e[33m"], $buf)), true, - (int) $output::OUTPUT_RAW, + OutputInterface::OUTPUT_RAW, ); $prefix = \substr($buf, 0, \strlen($expectedResponsePrefix)); diff --git a/src/Handler/Http/Handler/Fallback.php b/src/Handler/Http/Handler/Fallback.php index 8fca49e2..68531fb7 100644 --- a/src/Handler/Http/Handler/Fallback.php +++ b/src/Handler/Http/Handler/Fallback.php @@ -42,7 +42,7 @@ public function __construct( public function handle(StreamClient $streamClient, ServerRequestInterface $request, callable $next): iterable { - /** @psalm-suppress MixedAssignment */ + /** @var mixed $time */ $time = $request->getAttribute('begin_at'); $time = $time instanceof \DateTimeImmutable ? $time : new \DateTimeImmutable(); $gotFrame = false; @@ -67,8 +67,8 @@ public function handle(StreamClient $streamClient, ServerRequestInterface $reque if (!$response instanceof ResponseInterface) { throw new \RuntimeException('Invalid response type.'); } - HttpEmitter::emit($streamClient, $response); + HttpEmitter::emit($streamClient, $response); break; } diff --git a/src/Handler/Http/Handler/Websocket.php b/src/Handler/Http/Handler/Websocket.php index cd553404..01b3a79d 100644 --- a/src/Handler/Http/Handler/Websocket.php +++ b/src/Handler/Http/Handler/Websocket.php @@ -31,7 +31,7 @@ public function handle(StreamClient $streamClient, ServerRequestInterface $reque } // Get the time of the request - /** @psalm-suppress MixedAssignment */ + /** @var mixed $time */ $time = $request->getAttribute('begin_at'); $time = $time instanceof \DateTimeImmutable ? $time : new \DateTimeImmutable(); diff --git a/src/Handler/Http/Middleware/SentryTrap.php b/src/Handler/Http/Middleware/SentryTrap.php index 40da65ca..56515b6b 100644 --- a/src/Handler/Http/Middleware/SentryTrap.php +++ b/src/Handler/Http/Middleware/SentryTrap.php @@ -67,7 +67,7 @@ public function processEnvelope(ServerRequestInterface $request): ResponseInterf $request->getBody()->rewind(); - /** @psalm-suppress MixedAssignment */ + /** @var mixed $time */ $time = $request->getAttribute('begin_at'); $time = $time instanceof \DateTimeImmutable ? $time : new \DateTimeImmutable(); diff --git a/src/Proto/Frame/Sentry.php b/src/Proto/Frame/Sentry.php index ae508a30..3713934c 100644 --- a/src/Proto/Frame/Sentry.php +++ b/src/Proto/Frame/Sentry.php @@ -18,19 +18,22 @@ */ abstract class Sentry extends Frame { - /** - * @psalm-suppress MoreSpecificReturnType - */ - public static function fromString(string $payload, DateTimeImmutable $time): static + final public static function fromString(string $payload, DateTimeImmutable $time): static { + static::class === self::class or throw new \LogicException( + \sprintf('Factory method must be called from %s class.', self::class), + ); + /** @var array{type: string, ...mixed} $data */ $data = \json_decode($payload, true, 512, JSON_THROW_ON_ERROR); - /** @psalm-suppress LessSpecificReturnStatement, InvalidArgument */ - return match ($data['type']) { + /** @psalm-suppress InvalidArgument */ + $result = match ($data['type']) { SentryEnvelope::SENTRY_FRAME_TYPE => SentryEnvelope::fromArray($data, $time), SentryStore::SENTRY_FRAME_TYPE => SentryStore::fromArray($data, $time), default => throw new \InvalidArgumentException('Unknown Sentry frame type.'), }; + + return $result; } } diff --git a/src/Proto/Frame/Sentry/SentryStore.php b/src/Proto/Frame/Sentry/SentryStore.php index 977001a6..da282fd8 100644 --- a/src/Proto/Frame/Sentry/SentryStore.php +++ b/src/Proto/Frame/Sentry/SentryStore.php @@ -23,24 +23,24 @@ * version: non-empty-string, * }, * logger?: non-empty-string, - * contexts?: array>, - * environment?: non-empty-string, - * server_name?: non-empty-string, - * transaction?: non-empty-string, - * modules?: array, - * exception?: array - * } - * }> - * } + * contexts?: array>, + * environment?: non-empty-string, + * server_name?: non-empty-string, + * transaction?: non-empty-string, + * modules?: array, + * exception?: array + * } + * }> + * } */ final class SentryStore extends Frame\Sentry { From f9c81ea0dca35ab29e0d613ad471ce51413265db Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 6 May 2024 13:17:35 +0400 Subject: [PATCH 032/106] Fix new psalm issues after master merge --- src/Sender/Console/Renderer/Http.php | 5 ++++- src/Sender/Console/Support/Common.php | 1 + src/Traffic/Message/Multipart/File.php | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Sender/Console/Renderer/Http.php b/src/Sender/Console/Renderer/Http.php index 1f532c8f..701353f5 100644 --- a/src/Sender/Console/Renderer/Http.php +++ b/src/Sender/Console/Renderer/Http.php @@ -84,10 +84,13 @@ private function renderData(OutputInterface $output, Frame\Http $frame): void foreach ($uploadedFiles as $name => $fileSet) { $fileSet = \is_array($fileSet) ? $fileSet : [$fileSet]; foreach ($fileSet as $subName => $file) { + /** @var int<0, max>|null $size */ + $size = $file->getSize(); + Files::renderFile( $output, (string)$file->getClientFilename(), - $file->getSize(), + $size, (string)$file->getClientMediaType(), Field: \sprintf("%s[%s]", $name, $subName) ); diff --git a/src/Sender/Console/Support/Common.php b/src/Sender/Console/Support/Common.php index 83adf92c..cf2c7870 100644 --- a/src/Sender/Console/Support/Common.php +++ b/src/Sender/Console/Support/Common.php @@ -66,6 +66,7 @@ public static function renderMetadata(OutputInterface $output, array $data): voi \array_keys($data)), ); + /** @var mixed $value */ foreach ($data as $head => $value) { // Align headers to the right self::renderHeader( diff --git a/src/Traffic/Message/Multipart/File.php b/src/Traffic/Message/Multipart/File.php index 3ce9738c..143f7aae 100644 --- a/src/Traffic/Message/Multipart/File.php +++ b/src/Traffic/Message/Multipart/File.php @@ -95,7 +95,7 @@ public function moveTo(string $targetPath): void } /** - * @return non-negative-int|null + * @return int<0, max>|null */ public function getSize(): ?int { From 543d651d832fa277e9fb0e26a6eea951bfd79a3c Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Sun, 5 May 2024 00:43:46 +0400 Subject: [PATCH 033/106] Add `trap()->context()` method with context mapping --- src/Client/TrapHandle.php | 29 +++++++++++++++++++++++++++ src/Client/TrapHandle/Dumper.php | 6 ++++-- src/Client/TrapHandle/StaticState.php | 2 ++ tests/Unit/Client/TrapTest.php | 26 ++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/Client/TrapHandle.php b/src/Client/TrapHandle.php index 5ef9cdbd..b15c8487 100644 --- a/src/Client/TrapHandle.php +++ b/src/Client/TrapHandle.php @@ -133,6 +133,35 @@ public function return(int|string $key = 0): mixed return $this->values[$k]; } + /** + * Add dynamic context to the dumped data. + * The method merges new values with the existing ones using the {@see \array_merge()} function. + * + * There are two ways to add context: + * + * 1. Use named arguments: + * ```php + * trap($phpCode)->context(language: 'php'); + * ``` + * + * 2. Use array: + * ```php + * trap()->context(['foo bar', => 42, 'baz' => 69]); + * ``` + * + * @param mixed ...$values + */ + public function context(mixed ...$values): self + { + if (\array_keys($values) === [0] && \is_array($values[0])) { + $this->staticState->dataContext = \array_merge($this->staticState->dataContext, $values[0]); + return $this; + } + + $this->staticState->dataContext = \array_merge($this->staticState->dataContext, $values); + return $this; + } + public function __destruct() { $this->haveToSend() and $this->sendDump(); diff --git a/src/Client/TrapHandle/Dumper.php b/src/Client/TrapHandle/Dumper.php index 4f689837..1e462048 100644 --- a/src/Client/TrapHandle/Dumper.php +++ b/src/Client/TrapHandle/Dumper.php @@ -9,7 +9,6 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\VarDumper\Caster\ReflectionCaster; -use Symfony\Component\VarDumper\Cloner\DumperInterface; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider; @@ -62,7 +61,10 @@ public static function setDumper(?DataDumperInterface $dumper = null): Closure use ($cloner, $dumper): ?string { $var = $cloner->cloneVar($var); - $label === null or $var = $var->withContext(['label' => $label]); + $context = StaticState::getValue()->dataContext; + + $label === null or $context['label'] ??= $label; + $label === [] or $var = $var->withContext($context); $depth > 0 and $var = $var->withMaxDepth($depth); return $dumper->dump($var); diff --git a/src/Client/TrapHandle/StaticState.php b/src/Client/TrapHandle/StaticState.php index 58f6e99d..461e02b1 100644 --- a/src/Client/TrapHandle/StaticState.php +++ b/src/Client/TrapHandle/StaticState.php @@ -12,6 +12,8 @@ */ final class StaticState { + public array $dataContext = []; + /** * @param SimpleStackTrace $stackTrace Simple stack trace without arguments and objects. * @param StackTraceWithObjects $stackTraceWithObjects Stack trace without arguments but with objects. diff --git a/tests/Unit/Client/TrapTest.php b/tests/Unit/Client/TrapTest.php index f9d1e548..1b4dcccd 100644 --- a/tests/Unit/Client/TrapTest.php +++ b/tests/Unit/Client/TrapTest.php @@ -4,9 +4,7 @@ namespace Buggregator\Trap\Tests\Unit\Client; -use Buggregator\Trap\Client\TrapHandle\Counter; use Buggregator\Trap\Client\TrapHandle\Dumper; -use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\DataDumperInterface; final class TrapTest extends Base @@ -17,6 +15,30 @@ public function testLabel(): void $this->assertSame('FooName', static::$lastData->getContext()['label']); } + public function testSimpleContext(): void + { + trap('test-value')->context(foo: 'test-context'); + + self::assertSame(['foo' => 'test-context'], static::$lastData->getContext()); + } + + public function testArrayContext(): void + { + trap('test-value')->context(['foo' => 'test-context']); + + self::assertSame(['foo' => 'test-context'], static::$lastData->getContext()); + } + + public function testContextMultiple(): void + { + trap('test-value') + ->context(['foo' => 'test-context']) + ->context(['bar' => 'bar-context']) + ->context(foo: 'new'); + + self::assertSame(['foo' => 'new', 'bar' => 'bar-context'], static::$lastData->getContext()); + } + /** * Check the first line of dumped stacktrace string contains right file and line. */ From 28739d7633bc11ff5378deec01feedc7401b8873 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Sun, 5 May 2024 00:54:43 +0400 Subject: [PATCH 034/106] Render Data context in console sender --- src/Sender/Console/Renderer/VarDumper.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Sender/Console/Renderer/VarDumper.php b/src/Sender/Console/Renderer/VarDumper.php index 11edf05a..5be5ebb7 100644 --- a/src/Sender/Console/Renderer/VarDumper.php +++ b/src/Sender/Console/Renderer/VarDumper.php @@ -107,6 +107,16 @@ public function describe(OutputInterface $output, Data $data, array $context, in Common::renderMetadata($output, $meta); $output->writeln(''); + // Render Data context + if ($data->getContext() !== []) { + Common::renderHeader3($output, 'Data context'); + // todo the context may contain mixed data + // todo need to consider it and render the context in a good way + Common::renderMetadata($output, $data->getContext()); + // $output->writeln(\print_r($data->getContext(), true)); + $output->writeln(''); + } + $output->write((string) $this->dumper->dump($data, true), true, OutputInterface::OUTPUT_RAW); } }; From c4aa2a1356e5476911e15fee5ac6d9cb190051af Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 6 May 2024 13:59:50 +0400 Subject: [PATCH 035/106] Fix psalm issues --- src/Client/TrapHandle/Dumper.php | 8 +++++--- src/Client/TrapHandle/StaticState.php | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Client/TrapHandle/Dumper.php b/src/Client/TrapHandle/Dumper.php index 1e462048..9c7df70c 100644 --- a/src/Client/TrapHandle/Dumper.php +++ b/src/Client/TrapHandle/Dumper.php @@ -61,10 +61,12 @@ public static function setDumper(?DataDumperInterface $dumper = null): Closure use ($cloner, $dumper): ?string { $var = $cloner->cloneVar($var); - $context = StaticState::getValue()->dataContext; + /** @var array $context*/ + $context = StaticState::getValue()?->dataContext ?? []; - $label === null or $context['label'] ??= $label; - $label === [] or $var = $var->withContext($context); + /** @var string|null $label */ + $label === null or $context['label'] = $label; + $context === [] or $var = $var->withContext($context); $depth > 0 and $var = $var->withMaxDepth($depth); return $dumper->dump($var); diff --git a/src/Client/TrapHandle/StaticState.php b/src/Client/TrapHandle/StaticState.php index 461e02b1..1a0e45fe 100644 --- a/src/Client/TrapHandle/StaticState.php +++ b/src/Client/TrapHandle/StaticState.php @@ -12,6 +12,7 @@ */ final class StaticState { + /** @var array */ public array $dataContext = []; /** From 92b73c06f71b8dc41030c9511d222dadb089411d Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 6 May 2024 17:09:12 +0400 Subject: [PATCH 036/106] Update Info::VERSION --- src/Info.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Info.php b/src/Info.php index a6946337..59846fac 100644 --- a/src/Info.php +++ b/src/Info.php @@ -10,7 +10,7 @@ class Info { public const NAME = 'Buggregator Trap'; - public const VERSION = '1.6.1'; + public const VERSION = '1.7.0'; public const LOGO_CLI_COLOR = << Date: Tue, 7 May 2024 16:30:50 +0800 Subject: [PATCH 037/106] Added CS Fixer, corrected code for compliance with PER 2.0 (#67) --- .github/workflows/cs-fixser.yml | 74 +++++++++++++++++++ .php-cs-fixer.php | 24 ++++++ composer.json | 5 ++ src/Application.php | 2 +- src/Bootstrap.php | 3 +- src/Client/TrapHandle.php | 2 +- .../TrapHandle/ContextProvider/Source.php | 4 +- src/Client/TrapHandle/Dumper.php | 29 ++++---- src/Client/TrapHandle/StackTrace.php | 2 +- src/Client/TrapHandle/StaticState.php | 3 +- src/Command/Run.php | 6 +- src/Command/Test.php | 10 ++- src/Config/Server/SocketServer.php | 3 +- src/Handler/Http/Emitter.php | 2 +- src/Handler/Http/Handler/Fallback.php | 2 +- src/Handler/Http/Middleware/DebugPage.php | 46 ++++++------ .../Http/Middleware/RayRequestDump.php | 1 - src/Handler/Http/Middleware/SentryTrap.php | 2 +- .../Middleware/SentryTrap/EnvelopeParser.php | 2 +- src/Handler/Router/Attribute/AssertRoute.php | 3 +- .../Router/Attribute/AssertRouteFail.php | 4 +- src/Handler/Router/Attribute/Route.php | 3 +- .../Router/Exception/AssertRouteFailed.php | 4 +- src/Handler/Router/RouteDto.php | 3 +- src/Handler/Router/Router.php | 17 ++--- src/Proto/Frame.php | 5 +- src/Proto/Frame/Binary.php | 2 +- src/Proto/Frame/Http.php | 10 +-- src/Proto/Frame/Sentry/EnvelopeItem.php | 3 +- src/Proto/Server/Request.php | 3 +- src/Proto/Server/Version/V1.php | 2 +- src/Sender/Console/FrameHandler.php | 3 +- src/Sender/Console/Renderer/Binary.php | 5 +- src/Sender/Console/Renderer/Http.php | 6 +- src/Sender/Console/Renderer/Monolog.php | 3 +- src/Sender/Console/Renderer/Plain.php | 5 +- .../Console/Renderer/Sentry/Exceptions.php | 10 +-- src/Sender/Console/Renderer/Sentry/Header.php | 10 +-- .../Console/Renderer/SentryEnvelope.php | 2 +- .../Console/Renderer/TemplateRenderer.php | 5 +- src/Sender/Console/Renderer/VarDumper.php | 7 +- src/Sender/Console/Support/Common.php | 17 +++-- src/Sender/Console/Support/Files.php | 2 +- src/Sender/ConsoleSender.php | 3 +- src/Sender/FileSender.php | 14 ++-- src/Sender/Frontend/ConnectionPool.php | 13 ++-- src/Sender/Frontend/Event.php | 3 +- src/Sender/Frontend/Event/Asset.php | 3 +- src/Sender/Frontend/EventStorage.php | 4 +- src/Sender/Frontend/Http/EventAssets.php | 2 +- src/Sender/Frontend/Http/RequestHandler.php | 3 +- src/Sender/Frontend/Http/Router.php | 2 +- src/Sender/Frontend/Http/StaticFiles.php | 2 +- src/Sender/Frontend/Mapper/HttpRequest.php | 4 +- src/Sender/Frontend/Mapper/Monolog.php | 2 +- src/Sender/Frontend/Mapper/SentryEnvelope.php | 2 +- src/Sender/Frontend/Mapper/SentryStore.php | 2 +- src/Sender/Frontend/Mapper/Smtp.php | 4 +- src/Sender/Frontend/Mapper/VarDump.php | 4 +- src/Sender/Frontend/Message/Connect.php | 5 +- .../Frontend/Message/EventCollection.php | 3 +- src/Sender/Frontend/Message/Push.php | 3 +- src/Sender/Frontend/Message/Response.php | 3 +- src/Sender/Frontend/Message/Rpc.php | 3 +- src/Sender/Frontend/Message/Settings.php | 5 +- src/Sender/Frontend/Message/Success.php | 3 +- src/Sender/Frontend/Message/Version.php | 3 +- src/Sender/Frontend/Service.php | 3 +- src/Sender/FrontendSender.php | 3 +- src/Sender/SocketSender.php | 6 +- src/Service/Config/ConfigAttribute.php | 4 +- src/Service/Config/Env.php | 3 +- src/Service/Config/InputArgument.php | 3 +- src/Service/Config/InputOption.php | 3 +- src/Service/Config/XPath.php | 3 +- src/Service/Container.php | 2 +- src/Socket/Client.php | 6 +- src/Socket/Exception/ClientDisconnected.php | 4 +- src/Socket/Exception/ServerStopped.php | 4 +- src/Socket/Server.php | 2 +- src/Support/Caster/EnumValue.php | 4 +- src/Support/TemplateEngine.php | 3 +- .../StreamClientMock/DisconnectCommand.php | 4 +- src/Traffic/Dispatcher/Monolog.php | 2 +- src/Traffic/Message/Headers.php | 18 ++--- src/Traffic/Message/Multipart/Field.php | 4 +- src/Traffic/Message/Multipart/Part.php | 2 +- src/Traffic/Message/Smtp.php | 5 +- src/Traffic/Message/Smtp/Contact.php | 3 +- src/Traffic/Parser/Http.php | 2 +- src/Traffic/Websocket/Frame.php | 3 +- src/Traffic/Websocket/StreamReader.php | 8 +- src/functions.php | 1 - 93 files changed, 304 insertions(+), 252 deletions(-) create mode 100644 .github/workflows/cs-fixser.yml create mode 100644 .php-cs-fixer.php diff --git a/.github/workflows/cs-fixser.yml b/.github/workflows/cs-fixser.yml new file mode 100644 index 00000000..9647d544 --- /dev/null +++ b/.github/workflows/cs-fixser.yml @@ -0,0 +1,74 @@ +name: CS-Fixer + +on: + pull_request: + paths-ignore: + - 'docs/**' + - 'bin/**' + - 'resources/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - '.editorconfig' + - 'psalm.xml' + + push: + paths-ignore: + - 'docs/**' + - 'bin/**' + - 'resources/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - '.editorconfig' + - 'psalm.xml' + +jobs: + cs: + name: PHP ${{ matrix.php }}-${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + php: [ 8.2 ] + os: [ ubuntu-latest ] + steps: + - name: Set Git To Use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + + - name: Check Out Code + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Get Composer Cache Directory + id: composer-cache + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache Composer Dependencies + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: php-${{ matrix.php }}-${{ matrix.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + php-${{ matrix.php }}-${{ matrix.os }}-composer- + + - name: Install Composer Dependencies + run: composer install --prefer-dist --no-interaction + + - name: Check Coding Standards + run: vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff -vvv --using-cache=no diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 00000000..c5221478 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,24 @@ +setRules([ + '@PER-CS2.0' => true, + '@PHP81Migration' => true, + 'new_with_parentheses' => [ + 'anonymous_class' => false, + ], + ]) + ->setRiskyAllowed(true) + ->setFinder( + (new Finder()) + ->files() + ->name('*.php') + ->in([__DIR__ . '/src']) + ->exclude(['Test/Proto/']), + ) + ->setCacheFile('.cache/.php-cs-fixer.cache'); diff --git a/composer.json b/composer.json index a01be715..582aeced 100644 --- a/composer.json +++ b/composer.json @@ -67,6 +67,7 @@ }, "require-dev": { "dereuromark/composer-prefer-lowest": "^0.1.10", + "friendsofphp/php-cs-fixer": "^3.54", "google/protobuf": "^3.23", "phpunit/phpunit": "^10.4", "roxblnfk/unpoly": "^1.8.1", @@ -75,5 +76,9 @@ "suggest": { "ext-simplexml": "To load trap.xml", "roxblnfk/unpoly": "If you want to remove unnecessary PHP polyfills depend on PHP version." + }, + "scripts": { + "cs-check": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run", + "cs-fix": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php -vvv --using-cache=no" } } diff --git a/src/Application.php b/src/Application.php index 1cf8faa1..851709b1 100644 --- a/src/Application.php +++ b/src/Application.php @@ -212,7 +212,7 @@ public function configureFrontend(int $port): void ], [new Sender\Frontend\Http\RequestHandler($wsSender->getConnectionPool())], silentMode: true, - ) + ), ]); $this->processors[] = $inspector; $this->processors[] = $wsSender; diff --git a/src/Bootstrap.php b/src/Bootstrap.php index 079bc7d8..99f9c267 100644 --- a/src/Bootstrap.php +++ b/src/Bootstrap.php @@ -16,8 +16,7 @@ final class Bootstrap { private function __construct( private Container $container, - ) { - } + ) {} public static function init(Container $container = new Container()): self { diff --git a/src/Client/TrapHandle.php b/src/Client/TrapHandle.php index b15c8487..e7c1ab7a 100644 --- a/src/Client/TrapHandle.php +++ b/src/Client/TrapHandle.php @@ -33,7 +33,7 @@ public function if(bool|callable $condition): self { if (\is_callable($condition)) { try { - $condition = (bool)$condition(); + $condition = (bool) $condition(); } catch (\Throwable $e) { $this->values[] = $e; diff --git a/src/Client/TrapHandle/ContextProvider/Source.php b/src/Client/TrapHandle/ContextProvider/Source.php index 026a96b7..07cf47bf 100644 --- a/src/Client/TrapHandle/ContextProvider/Source.php +++ b/src/Client/TrapHandle/ContextProvider/Source.php @@ -80,10 +80,10 @@ public function getContext(): ?array $fileExcerpt = []; for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) { - $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; + $fileExcerpt[] = '' . $this->htmlEncode($src[$i - 1]) . ''; } - $fileExcerpt = '
    '.implode("\n", $fileExcerpt).'
'; + $fileExcerpt = '
    ' . implode("\n", $fileExcerpt) . '
'; } } break; diff --git a/src/Client/TrapHandle/Dumper.php b/src/Client/TrapHandle/Dumper.php index 9c7df70c..0c6749e8 100644 --- a/src/Client/TrapHandle/Dumper.php +++ b/src/Client/TrapHandle/Dumper.php @@ -32,7 +32,7 @@ final class Dumper public static function dump(mixed $var, string|int|null $label = null, int $depth = 0): mixed { /** @psalm-suppress RiskyTruthyFalsyComparison */ - return (self::$handler ??= self::registerHandler())($var, empty($label) ? null : (string)$label, $depth); + return (self::$handler ??= self::registerHandler())($var, empty($label) ? null : (string) $label, $depth); } /** @@ -57,20 +57,19 @@ public static function setDumper(?DataDumperInterface $dumper = null): Closure /** @psalm-suppress InvalidArgument */ $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); - return self::$handler = static function (mixed $var, string|null $label = null, int $depth = 0) - use ($cloner, $dumper): ?string { - $var = $cloner->cloneVar($var); + return self::$handler = static function (mixed $var, string|null $label = null, int $depth = 0) use ($cloner, $dumper): ?string { + $var = $cloner->cloneVar($var); - /** @var array $context*/ - $context = StaticState::getValue()?->dataContext ?? []; + /** @var array $context*/ + $context = StaticState::getValue()?->dataContext ?? []; - /** @var string|null $label */ - $label === null or $context['label'] = $label; - $context === [] or $var = $var->withContext($context); - $depth > 0 and $var = $var->withMaxDepth($depth); + /** @var string|null $label */ + $label === null or $context['label'] = $label; + $context === [] or $var = $var->withContext($context); + $depth > 0 and $var = $var->withMaxDepth($depth); - return $dumper->dump($var); - }; + return $dumper->dump($var); + }; } /** @@ -133,8 +132,8 @@ private static function getContextProviders(): array : null; return $contextProviders + [ - 'cli' => new CliContextProvider(), - 'source' => new ContextProvider\Source(null, null, $fileLinkFormatter), - ]; + 'cli' => new CliContextProvider(), + 'source' => new ContextProvider\Source(null, null, $fileLinkFormatter), + ]; } } diff --git a/src/Client/TrapHandle/StackTrace.php b/src/Client/TrapHandle/StackTrace.php index c87b7156..5d96006a 100644 --- a/src/Client/TrapHandle/StackTrace.php +++ b/src/Client/TrapHandle/StackTrace.php @@ -43,7 +43,7 @@ public static function stackTrace(string $baseDir = '', bool $provideObjects = f $internal = false; foreach ( \debug_backtrace( - ($provideObjects ? \DEBUG_BACKTRACE_PROVIDE_OBJECT : 0) | \DEBUG_BACKTRACE_IGNORE_ARGS + ($provideObjects ? \DEBUG_BACKTRACE_PROVIDE_OBJECT : 0) | \DEBUG_BACKTRACE_IGNORE_ARGS, ) as $frame ) { if (\str_starts_with($frame['class'] ?? '', 'Buggregator\\Trap\\Client\\')) { diff --git a/src/Client/TrapHandle/StaticState.php b/src/Client/TrapHandle/StaticState.php index 1a0e45fe..805f57a9 100644 --- a/src/Client/TrapHandle/StaticState.php +++ b/src/Client/TrapHandle/StaticState.php @@ -22,8 +22,7 @@ final class StaticState private function __construct( public array $stackTrace = [], public array $stackTraceWithObjects = [], - ) { - } + ) {} private static ?StaticState $value = null; diff --git a/src/Command/Run.php b/src/Command/Run.php index ce2b3a11..acbd4460 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -64,9 +64,9 @@ public function getServers(Container $container): array /** @var scalar $port */ foreach ($ports as $port) { \is_numeric($port) or throw new \InvalidArgumentException( - \sprintf('Invalid port `%s`. It must be a number.', (string)$port), + \sprintf('Invalid port `%s`. It must be a number.', (string) $port), ); - $port = (int)$port; + $port = (int) $port; $port > 0 && $port < 65536 or throw new \InvalidArgumentException( \sprintf('Invalid port `%s`. It must be in range 1-65535.', $port), ); @@ -85,7 +85,7 @@ protected function execute( $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); /** @var non-empty-string[] $senders */ - $senders = (array)$input->getOption('sender'); + $senders = (array) $input->getOption('sender'); $registry = $this->createRegistry($output); diff --git a/src/Command/Test.php b/src/Command/Test.php index 9adc2fae..fb7b2abc 100644 --- a/src/Command/Test.php +++ b/src/Command/Test.php @@ -78,7 +78,7 @@ private function dump(): void ) ->setMapaMapa(['foo' => 'bar', 'baz' => 'qux', '2' => 'quuz', 'quux ff' => 'quuz']) ->setFoo(\Buggregator\Trap\Test\Proto\Message\Foo::BAR); - \trap(Nested: (object)['msg' => $message]); + \trap(Nested: (object) ['msg' => $message]); try { $socket = @\socket_create(AF_INET, SOCK_STREAM, SOL_TCP); @@ -212,9 +212,11 @@ private function sendMailPackage( return; } - $output->write(\sprintf( - "\e[33m< \"%s\"\e[0m", - \str_replace(["\r", "\n"], ["\e[32m\\r\e[33m", "\e[32m\\n\e[33m"], $buf)), + $output->write( + \sprintf( + "\e[33m< \"%s\"\e[0m", + \str_replace(["\r", "\n"], ["\e[32m\\r\e[33m", "\e[32m\\n\e[33m"], $buf) + ), true, OutputInterface::OUTPUT_RAW, ); diff --git a/src/Config/Server/SocketServer.php b/src/Config/Server/SocketServer.php index 18aa1df2..9f47d9d7 100644 --- a/src/Config/Server/SocketServer.php +++ b/src/Config/Server/SocketServer.php @@ -17,6 +17,5 @@ public function __construct( public readonly int $port, public readonly string $host = '127.0.0.1', public readonly string $type = 'tcp', - ) { - } + ) {} } diff --git a/src/Handler/Http/Emitter.php b/src/Handler/Http/Emitter.php index 20739b73..eddc8763 100644 --- a/src/Handler/Http/Emitter.php +++ b/src/Handler/Http/Emitter.php @@ -66,7 +66,7 @@ private static function prepareHeaders(ResponseInterface $response): iterable $headers = $response->getHeaders(); if (!$response->hasHeader('Content-Length') && $response->getStatusCode() >= 200) { if ($response->getBody()->getSize() !== null) { - $headers['Content-Length'] = [(string)$response->getBody()->getSize()]; + $headers['Content-Length'] = [(string) $response->getBody()->getSize()]; } else { $headers['Transfer-Encoding'] = ['chunked']; } diff --git a/src/Handler/Http/Handler/Fallback.php b/src/Handler/Http/Handler/Fallback.php index 68531fb7..2423ac59 100644 --- a/src/Handler/Http/Handler/Fallback.php +++ b/src/Handler/Http/Handler/Fallback.php @@ -35,7 +35,7 @@ public function __construct( $middlewares, /** @see Middleware::handle() */ 'handle', - static fn (): ResponseInterface => new Response(404), + static fn(): ResponseInterface => new Response(404), ResponseInterface::class, ); } diff --git a/src/Handler/Http/Middleware/DebugPage.php b/src/Handler/Http/Middleware/DebugPage.php index bffbad0a..e81714b6 100644 --- a/src/Handler/Http/Middleware/DebugPage.php +++ b/src/Handler/Http/Middleware/DebugPage.php @@ -22,29 +22,29 @@ public function handle(ServerRequestInterface $request, callable $next): Respons 200, ['Content-Type' => ['text/html; charset=UTF-8']], <<<'HTML' - - - - - - - - -
-

Test form

-
-
-
-
-
-
-

- ©2023 Buggregator. -

-
- - - HTML + + + + + + + + +
+

Test form

+
+
+
+
+
+
+

+ ©2023 Buggregator. +

+
+ + + HTML ); } diff --git a/src/Handler/Http/Middleware/RayRequestDump.php b/src/Handler/Http/Middleware/RayRequestDump.php index 3721b2c5..3f0c3b42 100644 --- a/src/Handler/Http/Middleware/RayRequestDump.php +++ b/src/Handler/Http/Middleware/RayRequestDump.php @@ -15,7 +15,6 @@ */ final class RayRequestDump implements Middleware { - public function handle(ServerRequestInterface $request, callable $next): ResponseInterface { if (\str_ends_with($request->getUri()->getPath(), '_availability_check')) { diff --git a/src/Handler/Http/Middleware/SentryTrap.php b/src/Handler/Http/Middleware/SentryTrap.php index 56515b6b..8ea1ff9c 100644 --- a/src/Handler/Http/Middleware/SentryTrap.php +++ b/src/Handler/Http/Middleware/SentryTrap.php @@ -85,7 +85,7 @@ private function processStore(ServerRequestInterface $request): ResponseInterfac return new Response(413); } /** @var SentryStoreMessage $payload */ - $payload = \json_decode((string)$request->getBody(), true, 96, \JSON_THROW_ON_ERROR); + $payload = \json_decode((string) $request->getBody(), true, 96, \JSON_THROW_ON_ERROR); /** @psalm-suppress MixedAssignment */ $time = $request->getAttribute('begin_at'); diff --git a/src/Handler/Http/Middleware/SentryTrap/EnvelopeParser.php b/src/Handler/Http/Middleware/SentryTrap/EnvelopeParser.php index 438e558c..694a61d5 100644 --- a/src/Handler/Http/Middleware/SentryTrap/EnvelopeParser.php +++ b/src/Handler/Http/Middleware/SentryTrap/EnvelopeParser.php @@ -48,7 +48,7 @@ private static function parseItem(StreamInterface $stream): EnvelopeItem // Parse item header $itemHeader = \json_decode(self::readLine($stream), true, 4, JSON_THROW_ON_ERROR); - $length = isset($itemHeader['length']) ? (int)$itemHeader['length'] : null; + $length = isset($itemHeader['length']) ? (int) $itemHeader['length'] : null; $length >= 0 or throw new \RuntimeException('Invalid item length.'); $type = $itemHeader['type'] ?? null; diff --git a/src/Handler/Router/Attribute/AssertRoute.php b/src/Handler/Router/Attribute/AssertRoute.php index 1f052adc..f78ac967 100644 --- a/src/Handler/Router/Attribute/AssertRoute.php +++ b/src/Handler/Router/Attribute/AssertRoute.php @@ -14,6 +14,5 @@ abstract class AssertRoute public function __construct( public Method $method, public string $path, - ) { - } + ) {} } diff --git a/src/Handler/Router/Attribute/AssertRouteFail.php b/src/Handler/Router/Attribute/AssertRouteFail.php index e96f7904..cf780ccc 100644 --- a/src/Handler/Router/Attribute/AssertRouteFail.php +++ b/src/Handler/Router/Attribute/AssertRouteFail.php @@ -8,6 +8,4 @@ * @internal */ #[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -final class AssertRouteFail extends AssertRoute -{ -} +final class AssertRouteFail extends AssertRoute {} diff --git a/src/Handler/Router/Attribute/Route.php b/src/Handler/Router/Attribute/Route.php index a3b94e5e..f607a593 100644 --- a/src/Handler/Router/Attribute/Route.php +++ b/src/Handler/Router/Attribute/Route.php @@ -13,6 +13,5 @@ abstract class Route { public function __construct( public Method $method, - ) { - } + ) {} } diff --git a/src/Handler/Router/Exception/AssertRouteFailed.php b/src/Handler/Router/Exception/AssertRouteFailed.php index aa336637..6cd8e8da 100644 --- a/src/Handler/Router/Exception/AssertRouteFailed.php +++ b/src/Handler/Router/Exception/AssertRouteFailed.php @@ -4,6 +4,4 @@ namespace Buggregator\Trap\Handler\Router\Exception; -final class AssertRouteFailed extends \Exception -{ -} +final class AssertRouteFailed extends \Exception {} diff --git a/src/Handler/Router/RouteDto.php b/src/Handler/Router/RouteDto.php index 233713cb..953d8817 100644 --- a/src/Handler/Router/RouteDto.php +++ b/src/Handler/Router/RouteDto.php @@ -14,6 +14,5 @@ final class RouteDto public function __construct( public \ReflectionMethod $method, public Route $route, - ) { - } + ) {} } diff --git a/src/Handler/Router/Router.php b/src/Handler/Router/Router.php index ecea3068..6cd92d9d 100644 --- a/src/Handler/Router/Router.php +++ b/src/Handler/Router/Router.php @@ -15,7 +15,7 @@ final class Router { /** @var array */ - static private array $cache = []; + private static array $cache = []; /** @var null|object Null for routes defined in static methods */ private ?object $object = null; @@ -25,8 +25,7 @@ final class Router */ private function __construct( private readonly array $routes, - ) { - } + ) {} /** * @param array $routes @@ -86,10 +85,10 @@ public static function assert(array $routes, array $assertions): void // Must be matched if ($assertion::class === Attribute\AssertRouteSuccess::class) { $found or $fails[] = \sprintf( - '> Should be matched -> %s `%s`.', - $assertion->method->value, - $assertion->path, - ); + '> Should be matched -> %s `%s`.', + $assertion->method->value, + $assertion->path, + ); if (!$found || $assertion->args === null) { continue; @@ -219,8 +218,8 @@ public function match(Method $method, string $path): ?callable $rr = $route->route; /** @psalm-suppress ArgumentTypeCoercion */ $match = match ($rr::class) { - Attribute\StaticRoute::class => $path === (string)$rr->path, - Attribute\RegexpRoute::class => \preg_match((string)$rr->regexp, $path, $matches) === 1 + Attribute\StaticRoute::class => $path === (string) $rr->path, + Attribute\RegexpRoute::class => \preg_match((string) $rr->regexp, $path, $matches) === 1 ? \array_filter($matches, '\is_string', \ARRAY_FILTER_USE_KEY) : false, default => throw new \LogicException(\sprintf( diff --git a/src/Proto/Frame.php b/src/Proto/Frame.php index 1fa0b6ee..17016efc 100644 --- a/src/Proto/Frame.php +++ b/src/Proto/Frame.php @@ -16,8 +16,7 @@ abstract class Frame implements \Stringable, \JsonSerializable public function __construct( public readonly ProtoType $type, public readonly DateTimeImmutable $time = new DateTimeImmutable(), - ) { - } + ) {} abstract public static function fromString(string $payload, DateTimeImmutable $time): static; @@ -26,7 +25,7 @@ abstract public static function fromString(string $payload, DateTimeImmutable $t */ public function getSize(): int { - return \strlen((string)$this); + return \strlen((string) $this); } final public function jsonSerialize(): array diff --git a/src/Proto/Frame/Binary.php b/src/Proto/Frame/Binary.php index 7addce0d..5fe22432 100644 --- a/src/Proto/Frame/Binary.php +++ b/src/Proto/Frame/Binary.php @@ -25,7 +25,7 @@ public function __construct( public function getSize(): int { - return (int)$this->stream->getSize(); + return (int) $this->stream->getSize(); } /** diff --git a/src/Proto/Frame/Http.php b/src/Proto/Frame/Http.php index 4d27dffb..8778fa30 100644 --- a/src/Proto/Frame/Http.php +++ b/src/Proto/Frame/Http.php @@ -27,9 +27,9 @@ public function __construct( public readonly ServerRequestInterface $request, DateTimeImmutable $time = new DateTimeImmutable(), ) { - $this->cachedSize = \max(0, (int)$request->getBody()->getSize() + \array_reduce( + $this->cachedSize = \max(0, (int) $request->getBody()->getSize() + \array_reduce( \iterator_to_array($this->iterateUploadedFiles(), false), - static fn(int $carry, PsrUploadedFile $file): int => $carry + (int)$file->getSize(), + static fn(int $carry, PsrUploadedFile $file): int => $carry + (int) $file->getSize(), 0, )); parent::__construct(type: ProtoType::HTTP, time: $time); @@ -43,8 +43,8 @@ public function __toString(): string return Json::encode([ 'headers' => $this->request->getHeaders(), 'method' => $this->request->getMethod(), - 'uri' => (string)$this->request->getUri(), - 'body' => (string)$this->request->getBody(), + 'uri' => (string) $this->request->getUri(), + 'body' => (string) $this->request->getBody(), 'serverParams' => $this->request->getServerParams(), 'cookies' => $this->request->getCookieParams(), 'queryParams' => $this->request->getQueryParams(), @@ -60,7 +60,7 @@ public static function fromString(string $payload, DateTimeImmutable $time): sta $request = new ServerRequest( $payload['method'] ?? 'GET', $payload['uri'] ?? '/', - (array)($payload['headers'] ?? []), + (array) ($payload['headers'] ?? []), $payload['body'] ?? '', $payload['protocolVersion'] ?? '1.1', $payload['serverParams'] ?? [], diff --git a/src/Proto/Frame/Sentry/EnvelopeItem.php b/src/Proto/Frame/Sentry/EnvelopeItem.php index aaa0f4dd..e9e1e221 100644 --- a/src/Proto/Frame/Sentry/EnvelopeItem.php +++ b/src/Proto/Frame/Sentry/EnvelopeItem.php @@ -15,8 +15,7 @@ final class EnvelopeItem implements \Stringable, \JsonSerializable public function __construct( public readonly array $headers, public readonly mixed $payload, - ) { - } + ) {} /** * @throws \JsonException diff --git a/src/Proto/Server/Request.php b/src/Proto/Server/Request.php index 72cc23d4..69881929 100644 --- a/src/Proto/Server/Request.php +++ b/src/Proto/Server/Request.php @@ -25,8 +25,7 @@ public function __construct( public readonly string $uuid, public readonly string $payload, private readonly \Closure $payloadParser, - ) { - } + ) {} /** * @return iterable diff --git a/src/Proto/Server/Version/V1.php b/src/Proto/Server/Version/V1.php index 348a94f2..c68cfe5d 100644 --- a/src/Proto/Server/Version/V1.php +++ b/src/Proto/Server/Version/V1.php @@ -55,7 +55,7 @@ public function decode(string $payload): Request } /** @var positive-int $protocol */ - $protocol = (int)$protocol; + $protocol = (int) $protocol; \assert($protocol > 0); // UUID diff --git a/src/Sender/Console/FrameHandler.php b/src/Sender/Console/FrameHandler.php index 7c153e58..1c623ab4 100644 --- a/src/Sender/Console/FrameHandler.php +++ b/src/Sender/Console/FrameHandler.php @@ -21,8 +21,7 @@ final class FrameHandler implements HandlerInterface public function __construct( private readonly OutputInterface $output, - ) { - } + ) {} public function handle(Frame $frame): void { diff --git a/src/Sender/Console/Renderer/Binary.php b/src/Sender/Console/Renderer/Binary.php index 792fd530..2eb70446 100644 --- a/src/Sender/Console/Renderer/Binary.php +++ b/src/Sender/Console/Renderer/Binary.php @@ -55,7 +55,7 @@ final class Binary implements Renderer public function __construct( public readonly int $printBytes = 512, - ) { } + ) {} public function isSupport(Frame $frame): bool { @@ -113,7 +113,8 @@ public function render(OutputInterface $output, Frame $frame): void \preg_replace( \array_keys(self::BYTE_REPLACES), \array_values(self::BYTE_REPLACES), - \substr($read, $offset, 16)), + \substr($read, $offset, 16) + ), )); $offset += 16; } diff --git a/src/Sender/Console/Renderer/Http.php b/src/Sender/Console/Renderer/Http.php index 701353f5..8a7882b5 100644 --- a/src/Sender/Console/Renderer/Http.php +++ b/src/Sender/Console/Renderer/Http.php @@ -47,7 +47,7 @@ private function renderData(OutputInterface $output, Frame\Http $frame): void Common::renderMetadata($output, [ 'Time' => $frame->time, - 'URI' => (string)$request->getUri(), + 'URI' => (string) $request->getUri(), ]); if ($request->getQueryParams() !== []) { @@ -89,9 +89,9 @@ private function renderData(OutputInterface $output, Frame\Http $frame): void Files::renderFile( $output, - (string)$file->getClientFilename(), + (string) $file->getClientFilename(), $size, - (string)$file->getClientMediaType(), + (string) $file->getClientMediaType(), Field: \sprintf("%s[%s]", $name, $subName) ); } diff --git a/src/Sender/Console/Renderer/Monolog.php b/src/Sender/Console/Renderer/Monolog.php index ed995ffa..33f2e740 100644 --- a/src/Sender/Console/Renderer/Monolog.php +++ b/src/Sender/Console/Renderer/Monolog.php @@ -18,8 +18,7 @@ final class Monolog implements Renderer { public function __construct( private readonly TemplateRenderer $renderer, - ) { - } + ) {} public function isSupport(Frame $frame): bool { diff --git a/src/Sender/Console/Renderer/Plain.php b/src/Sender/Console/Renderer/Plain.php index dddd3bc8..a0dd2f5d 100644 --- a/src/Sender/Console/Renderer/Plain.php +++ b/src/Sender/Console/Renderer/Plain.php @@ -17,8 +17,7 @@ final class Plain implements Renderer { public function __construct( private readonly TemplateRenderer $renderer, - ) { - } + ) {} public function isSupport(Frame $frame): bool { @@ -32,7 +31,7 @@ public function render(OutputInterface $output, Frame $frame): void [ 'date' => $frame->time->format('Y-m-d H:i:s.u'), 'channel' => \strtoupper($frame->type->value), - 'body' => \htmlspecialchars((string)$frame), + 'body' => \htmlspecialchars((string) $frame), ] ); } diff --git a/src/Sender/Console/Renderer/Sentry/Exceptions.php b/src/Sender/Console/Renderer/Sentry/Exceptions.php index 89e46285..b408cc6f 100644 --- a/src/Sender/Console/Renderer/Sentry/Exceptions.php +++ b/src/Sender/Console/Renderer/Sentry/Exceptions.php @@ -39,7 +39,7 @@ public static function render(OutputInterface $output, mixed $exceptions): void // Exception type $output->writeln(\sprintf( '%s', - isset($exception['type']) ? $exception['type'] : 'Exception', + $exception['type'] ?? 'Exception', )); isset($exception['value']) and $output->writeln($exception['value']); @@ -70,7 +70,7 @@ private static function renderTrace(OutputInterface $output, array $frames, bool isset($frame[$key]) && \is_scalar($frame[$key]) ? $frame[$key] : $default; $i = \count($frames) ; - $numPad = \strlen((string)($i - 1)) + 2; + $numPad = \strlen((string) ($i - 1)) + 2; // Skipped frames $vendorLines = []; $isFirst = true; @@ -140,7 +140,7 @@ private static function renderCodeSnippet(OutputInterface $output, array $frame, $content = []; try { - $startLine = (int)$frame['lineno']; + $startLine = (int) $frame['lineno']; if (isset($frame['pre_context']) && \is_array($frame['pre_context'])) { foreach ($frame['pre_context'] as $row) { if (!\is_string($row)) { @@ -169,7 +169,7 @@ private static function renderCodeSnippet(OutputInterface $output, array $frame, } Common::hr($output, 'white', padding: $padding); - $strPad = \strlen((string)($startLine + \count($content) - 1)); + $strPad = \strlen((string) ($startLine + \count($content) - 1)); $paddingStr = \str_repeat(' ', $padding); foreach ($content as $line => $row) { $output->writeln( @@ -177,7 +177,7 @@ private static function renderCodeSnippet(OutputInterface $output, array $frame, '%s%s▕%s', $paddingStr, $line === $contextLine ? 'red' : 'gray', - \str_pad((string)($startLine + $line), $strPad, ' ', \STR_PAD_LEFT), + \str_pad((string) ($startLine + $line), $strPad, ' ', \STR_PAD_LEFT), $line === $contextLine ? 'red' : 'blue', \substr($row, $minPadding) ) diff --git a/src/Sender/Console/Renderer/Sentry/Header.php b/src/Sender/Console/Renderer/Sentry/Header.php index 30fb3e52..2dfefbed 100644 --- a/src/Sender/Console/Renderer/Sentry/Header.php +++ b/src/Sender/Console/Renderer/Sentry/Header.php @@ -22,7 +22,7 @@ public static function renderMessageHeader(OutputInterface $output, array $messa /** @var mixed $timeValue */ $timeValue = $message['sent_at'] ?? $message['timestamp'] ?? 'now'; try { - $time = new DateTimeImmutable(\is_numeric($timeValue) ? "@$timeValue" : (string)$timeValue); + $time = new DateTimeImmutable(\is_numeric($timeValue) ? "@$timeValue" : (string) $timeValue); } catch (\Throwable) { $time = new DateTimeImmutable(); } @@ -34,10 +34,10 @@ public static function renderMessageHeader(OutputInterface $output, array $messa // Metadata from context if (isset($message['contexts']) && \is_array($message['contexts'])) { $context = $message['contexts']; - isset($context['runtime']) and $meta['Runtime'] = \implode(' ', (array)$context['runtime']); - isset($context['os']) and $meta['OS'] = \implode(' ', (array)$context['os']); + isset($context['runtime']) and $meta['Runtime'] = \implode(' ', (array) $context['runtime']); + isset($context['os']) and $meta['OS'] = \implode(' ', (array) $context['os']); } - isset($message['sdk']) and $meta['SDK'] = \implode(' ', (array)$message['sdk']); + isset($message['sdk']) and $meta['SDK'] = \implode(' ', (array) $message['sdk']); Common::renderMetadata($output, $meta); @@ -74,7 +74,7 @@ private static function pullTagsFromMessage(array $message, array $tags): array $result = []; foreach ($tags as $key => $alias) { if (isset($message[$key]) && \is_string($message[$key])) { - $result[$alias] ??= \implode(' ', (array)($message[$key])); + $result[$alias] ??= \implode(' ', (array) ($message[$key])); } } diff --git a/src/Sender/Console/Renderer/SentryEnvelope.php b/src/Sender/Console/Renderer/SentryEnvelope.php index 5b7c169a..ef49850b 100644 --- a/src/Sender/Console/Renderer/SentryEnvelope.php +++ b/src/Sender/Console/Renderer/SentryEnvelope.php @@ -36,7 +36,7 @@ public function render(OutputInterface $output, Frame $frame): void ++$i; try { $type = $item->headers['type'] ?? null; - Common::renderHeader2($output, "Item $i", green: (string)$type); + Common::renderHeader2($output, "Item $i", green: (string) $type); Header::renderMessageHeader($output, $item->payload); $this->renderItem($output, $item); diff --git a/src/Sender/Console/Renderer/TemplateRenderer.php b/src/Sender/Console/Renderer/TemplateRenderer.php index d0b98bff..f6210f3f 100644 --- a/src/Sender/Console/Renderer/TemplateRenderer.php +++ b/src/Sender/Console/Renderer/TemplateRenderer.php @@ -15,15 +15,14 @@ final class TemplateRenderer public function __construct( private readonly HtmlRenderer $renderer, private readonly TemplateEngine $templateEngine, - ) { - } + ) {} public function render(string $template, array $data = []): void { /** @psalm-suppress InternalMethod */ $this->renderer->render( $this->templateEngine->render($template, $data), - 0 + 0, ); } } diff --git a/src/Sender/Console/Renderer/VarDumper.php b/src/Sender/Console/Renderer/VarDumper.php index 5be5ebb7..847cb017 100644 --- a/src/Sender/Console/Renderer/VarDumper.php +++ b/src/Sender/Console/Renderer/VarDumper.php @@ -51,11 +51,10 @@ public function render(OutputInterface $output, Frame $frame): void private function getDescriber(): DumpDescriptorInterface { - return new class() implements DumpDescriptorInterface { + return new class implements DumpDescriptorInterface { public function __construct( private CliDumper $dumper = new CliDumper(), - ) { - } + ) {} /** * @psalm-suppress RiskyTruthyFalsyComparison, MixedArrayAccess, MixedArgument @@ -67,7 +66,7 @@ public function describe(OutputInterface $output, Data $data, array $context, in $this->dumper->setColors($output->isDecorated()); $meta = []; - $meta['Time'] = (new DateTimeImmutable())->setTimestamp((int)$context['timestamp']); + $meta['Time'] = (new DateTimeImmutable())->setTimestamp((int) $context['timestamp']); try { if (isset($context['source'])) { diff --git a/src/Sender/Console/Support/Common.php b/src/Sender/Console/Support/Common.php index cf2c7870..39fe205d 100644 --- a/src/Sender/Console/Support/Common.php +++ b/src/Sender/Console/Support/Common.php @@ -61,9 +61,12 @@ public static function renderHighlightedLine(OutputInterface $output, string $li */ public static function renderMetadata(OutputInterface $output, array $data): void { - $maxHeaderLength = \max(0, ...\array_map( - static fn(string|int $key): int => \strlen((string) $key), - \array_keys($data)), + $maxHeaderLength = \max( + 0, + ...\array_map( + static fn(string|int $key): int => \strlen((string) $key), + \array_keys($data) + ), ); /** @var mixed $value */ @@ -71,7 +74,7 @@ public static function renderMetadata(OutputInterface $output, array $data): voi // Align headers to the right self::renderHeader( $output, - \str_pad((string)$head, $maxHeaderLength, ' ', \STR_PAD_LEFT), + \str_pad((string) $head, $maxHeaderLength, ' ', \STR_PAD_LEFT), $value, keyColor: Color::White, valueColor: Color::Gray, @@ -95,7 +98,7 @@ public static function renderTags(OutputInterface $output, array $tags): void foreach ($tags as $name => $value) { if (\is_string($name)) { $currentLen = \strlen($name) + \strlen($value) + 5; // 4 paddings and 1 margin - $tag = \sprintf(' %s: %s ', $name, $value,); + $tag = \sprintf(' %s: %s ', $name, $value, ); } else { $currentLen = \strlen($value) + 3; // 2 paddings and 1 margin $tag = \sprintf(' %s ', $value); @@ -130,7 +133,7 @@ public static function hr(OutputInterface $output, string $color = 'gray', int $ public static function renderHeaders(OutputInterface $output, array $headers): void { foreach ($headers as $head => $value) { - self::renderHeader($output, (string)$head, $value); + self::renderHeader($output, (string) $head, $value); } } @@ -154,7 +157,7 @@ public static function renderHeader( $value instanceof DateTimeInterface => $value->format('u') === '000000' ? $value->format('Y-m-d H:i:s') : $value->format('Y-m-d H:i:s.u'), - \is_scalar($value) || $value instanceof \Stringable => (string)$value, + \is_scalar($value) || $value instanceof \Stringable => (string) $value, default => \print_r($item, true), }; diff --git a/src/Sender/Console/Support/Files.php b/src/Sender/Console/Support/Files.php index 800d791b..0817a63b 100644 --- a/src/Sender/Console/Support/Files.php +++ b/src/Sender/Console/Support/Files.php @@ -69,7 +69,7 @@ public static function normalizeSize(?int $size): ?string } $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; - $power = (int)\floor(\log($size, 1024)); + $power = (int) \floor(\log($size, 1024)); $float = $power > 0 ? \round($size / (1024 ** $power), 2) : $size; \assert($power >= 0 && $power <= 5); diff --git a/src/Sender/ConsoleSender.php b/src/Sender/ConsoleSender.php index 58cdb975..bfd98e93 100644 --- a/src/Sender/ConsoleSender.php +++ b/src/Sender/ConsoleSender.php @@ -45,8 +45,7 @@ public static function create(OutputInterface $output): self public function __construct( private readonly FrameHandler $handler, - ) { - } + ) {} public function send(iterable $frames): void { diff --git a/src/Sender/FileSender.php b/src/Sender/FileSender.php index 08364807..aab03017 100644 --- a/src/Sender/FileSender.php +++ b/src/Sender/FileSender.php @@ -19,7 +19,7 @@ public function __construct( string $path = 'runtime', ) { $this->path = \rtrim($path, '/\\'); - if (!\is_dir($path) && !mkdir($path, 0777, true) && !is_dir($path)) { + if (!\is_dir($path) && !mkdir($path, 0o777, true) && !is_dir($path)) { throw new \RuntimeException(sprintf('Directory "%s" was not created', $path)); } } @@ -27,12 +27,12 @@ public function __construct( public function send(iterable $frames): void { $data = \implode( - "\n", - \array_map( - static fn(Frame $frame): string => $frame->__toString(), - \is_array($frames) ? $frames : \iterator_to_array($frames), - ), - ) . "\n"; + "\n", + \array_map( + static fn(Frame $frame): string => $frame->__toString(), + \is_array($frames) ? $frames : \iterator_to_array($frames), + ), + ) . "\n"; $fileName = 'dump-' . (new DateTimeImmutable())->format('Y-m-d-H-i-s-v') . '.log'; \file_put_contents("{$this->path}/{$fileName}", $data, \FILE_APPEND); diff --git a/src/Sender/Frontend/ConnectionPool.php b/src/Sender/Frontend/ConnectionPool.php index 68e7cf23..c950f7e2 100644 --- a/src/Sender/Frontend/ConnectionPool.php +++ b/src/Sender/Frontend/ConnectionPool.php @@ -35,12 +35,11 @@ final class ConnectionPool implements IteratorAggregate, Processable public function __construct( private readonly Logger $logger, private RPC $rpc, - ) { - } + ) {} public function addStream(StreamClient $stream): void { - $key = (int)\array_key_last($this->streams) + 1; + $key = (int) \array_key_last($this->streams) + 1; $this->streams[$key] = $stream; $this->fibers[] = new Fiber(function () use ($key, $stream) { try { @@ -79,7 +78,7 @@ public function getIterator(): Traversable public function send(Frame $frame): void { - $data = (string)$frame; + $data = (string) $frame; foreach ($this->streams as $stream) { $stream->sendData($data); } @@ -97,7 +96,7 @@ private function processSocket(StreamClient $stream): void } // Ping-pong - $frame->opcode === Opcode::Ping and $stream->sendData((string)Frame::pong($frame->content)); + $frame->opcode === Opcode::Ping and $stream->sendData((string) Frame::pong($frame->content)); // Pong using `{}` message if ($frame->content === '{}') { @@ -111,10 +110,10 @@ private function processSocket(StreamClient $stream): void continue; } - $response = new Response(\is_numeric($payload['id'] ?? null) ? (int)$payload['id'] : 0); + $response = new Response(\is_numeric($payload['id'] ?? null) ? (int) $payload['id'] : 0); // On connected start periodic ping using `{}` message - if (isset($payload['connect'])){ + if (isset($payload['connect'])) { $response->connect = new Connect(Uuid::uuid4()); $pingTimer = new Timer($response->connect->ping); diff --git a/src/Sender/Frontend/Event.php b/src/Sender/Frontend/Event.php index ad05deac..b032a067 100644 --- a/src/Sender/Frontend/Event.php +++ b/src/Sender/Frontend/Event.php @@ -24,8 +24,7 @@ public function __construct( public readonly float $timestamp, public readonly ?string $projectId = null, public readonly ?ArrayAccess $assets = null, - ) { - } + ) {} public function jsonSerialize(): array { diff --git a/src/Sender/Frontend/Event/Asset.php b/src/Sender/Frontend/Event/Asset.php index b6b88135..80156823 100644 --- a/src/Sender/Frontend/Event/Asset.php +++ b/src/Sender/Frontend/Event/Asset.php @@ -14,6 +14,5 @@ abstract class Asset */ public function __construct( public readonly string $uuid, - ) { - } + ) {} } diff --git a/src/Sender/Frontend/EventStorage.php b/src/Sender/Frontend/EventStorage.php index dc31f14d..7f46bb67 100644 --- a/src/Sender/Frontend/EventStorage.php +++ b/src/Sender/Frontend/EventStorage.php @@ -13,7 +13,6 @@ * @implements IteratorAggregate */ final class EventStorage implements IteratorAggregate, Countable - { /** * Events. Will be sorted by timestamp in descending order when requested via the {@see getIterator()} method. @@ -24,8 +23,7 @@ final class EventStorage implements IteratorAggregate, Countable public function __construct( private readonly Config $config = new Config(), - ) { - } + ) {} public function add(Event $event): void { diff --git a/src/Sender/Frontend/Http/EventAssets.php b/src/Sender/Frontend/Http/EventAssets.php index 442d5114..52c6a72b 100644 --- a/src/Sender/Frontend/Http/EventAssets.php +++ b/src/Sender/Frontend/Http/EventAssets.php @@ -120,7 +120,7 @@ public function attachment(string $eventId, string $attachId): ?Response "attachment; filename=\"%s\"", \rawurlencode($attachment->file->getClientFilename() ?? 'unnamed'), ), - 'Content-Length' => (string)$attachment->file->getSize(), + 'Content-Length' => (string) $attachment->file->getSize(), 'Cache-Control' => 'no-cache', ], $attachment->file->getStream(), diff --git a/src/Sender/Frontend/Http/RequestHandler.php b/src/Sender/Frontend/Http/RequestHandler.php index abe0a9da..ef43d33c 100644 --- a/src/Sender/Frontend/Http/RequestHandler.php +++ b/src/Sender/Frontend/Http/RequestHandler.php @@ -23,8 +23,7 @@ final class RequestHandler implements RequestHandlernterace { public function __construct( private readonly Sender\Frontend\ConnectionPool $connectionPool, - ) { - } + ) {} public function handle(StreamClient $streamClient, ServerRequestInterface $request, callable $next): \Generator { diff --git a/src/Sender/Frontend/Http/Router.php b/src/Sender/Frontend/Http/Router.php index c5872c6a..6fe37918 100644 --- a/src/Sender/Frontend/Http/Router.php +++ b/src/Sender/Frontend/Http/Router.php @@ -49,7 +49,7 @@ public function handle(ServerRequestInterface $request, callable $next): Respons $params = $request->getQueryParams(); } else { /** @var mixed $params */ - $params = Json::decode((string)$request->getBody()); + $params = Json::decode((string) $request->getBody()); \is_array($params) or $params = []; } } catch (\Throwable) { diff --git a/src/Sender/Frontend/Http/StaticFiles.php b/src/Sender/Frontend/Http/StaticFiles.php index 4e7cb389..d41e4194 100644 --- a/src/Sender/Frontend/Http/StaticFiles.php +++ b/src/Sender/Frontend/Http/StaticFiles.php @@ -69,7 +69,7 @@ public function handle(ServerRequestInterface $request, callable $next): Respons $headers = [ 'Link' => \array_map( - static fn (string $css): string => \sprintf('<%s>; rel=preload; as=style', $css), + static fn(string $css): string => \sprintf('<%s>; rel=preload; as=style', $css), $this->earlyResponse, ), ]; diff --git a/src/Sender/Frontend/Mapper/HttpRequest.php b/src/Sender/Frontend/Mapper/HttpRequest.php index a355c11f..0fbf6742 100644 --- a/src/Sender/Frontend/Mapper/HttpRequest.php +++ b/src/Sender/Frontend/Mapper/HttpRequest.php @@ -33,7 +33,7 @@ public function map(HttpFrame $frame): Event 'uri' => $uri, 'headers' => $request->getHeaders(), 'body' => $request->getParsedBody() === null - ? (string)$request->getBody() + ? (string) $request->getBody() : '', 'query' => $request->getQueryParams(), 'post' => $request->getParsedBody() ?? [], @@ -59,7 +59,7 @@ static function (UploadedFileInterface $attachment) use ($assets, $uuid): array ), ], ], - timestamp: (float)$frame->time->format('U.u'), + timestamp: (float) $frame->time->format('U.u'), assets: $assets, ); } diff --git a/src/Sender/Frontend/Mapper/Monolog.php b/src/Sender/Frontend/Mapper/Monolog.php index 91171ce4..63ec7b3f 100644 --- a/src/Sender/Frontend/Mapper/Monolog.php +++ b/src/Sender/Frontend/Mapper/Monolog.php @@ -19,7 +19,7 @@ public function map(MonologFrame $frame): Event uuid: Uuid::uuid4(), type: 'monolog', payload: $frame->message, - timestamp: (float)$frame->time->format('U.u'), + timestamp: (float) $frame->time->format('U.u'), ); } } diff --git a/src/Sender/Frontend/Mapper/SentryEnvelope.php b/src/Sender/Frontend/Mapper/SentryEnvelope.php index 55f2106d..052e624d 100644 --- a/src/Sender/Frontend/Mapper/SentryEnvelope.php +++ b/src/Sender/Frontend/Mapper/SentryEnvelope.php @@ -25,7 +25,7 @@ public function map(SentryFrame $frame): Event uuid: Uuid::uuid4(), type: 'sentry', payload: $frame->items[0]->payload, - timestamp: (float)$frame->time->format('U.u'), + timestamp: (float) $frame->time->format('U.u'), ); } } diff --git a/src/Sender/Frontend/Mapper/SentryStore.php b/src/Sender/Frontend/Mapper/SentryStore.php index 157f4f22..d4b644e5 100644 --- a/src/Sender/Frontend/Mapper/SentryStore.php +++ b/src/Sender/Frontend/Mapper/SentryStore.php @@ -19,7 +19,7 @@ public function map(SentryFrame $frame): Event uuid: Uuid::uuid4(), type: 'sentry', payload: $frame->message, - timestamp: (float)$frame->time->format('U.u'), + timestamp: (float) $frame->time->format('U.u'), ); } } diff --git a/src/Sender/Frontend/Mapper/Smtp.php b/src/Sender/Frontend/Mapper/Smtp.php index 367e770e..8a29f69c 100644 --- a/src/Sender/Frontend/Mapper/Smtp.php +++ b/src/Sender/Frontend/Mapper/Smtp.php @@ -36,7 +36,7 @@ public function map(SmtpFrame $frame): Event 'bcc' => $message->getBcc(), 'text' => $message->getMessage(MessageFormat::Plain)?->getValue() ?? '', 'html' => $message->getMessage(MessageFormat::Html)?->getValue() ?? '', - 'raw' => (string)$message->getBody(), + 'raw' => (string) $message->getBody(), 'attachments' => \array_map( static function (File $attachment) use ($assets, $uuid): array { $asset = new Event\AttachedFile( @@ -57,7 +57,7 @@ static function (File $attachment) use ($assets, $uuid): array { $message->getAttachments(), ), ], - timestamp: (float)$frame->time->format('U.u'), + timestamp: (float) $frame->time->format('U.u'), assets: $assets, ); } diff --git a/src/Sender/Frontend/Mapper/VarDump.php b/src/Sender/Frontend/Mapper/VarDump.php index f8d41a2e..2a5453ee 100644 --- a/src/Sender/Frontend/Mapper/VarDump.php +++ b/src/Sender/Frontend/Mapper/VarDump.php @@ -29,7 +29,7 @@ public function map(VarDumperFrame $frame): Event ], 'context' => $payload[1], ], - timestamp: (float)$frame->time->format('U.u'), + timestamp: (float) $frame->time->format('U.u'), ); } @@ -61,7 +61,7 @@ private function convertToPrimitive(Data $data): string|null { if (\in_array($data->getType(), ['string', 'boolean'])) { /** @psalm-suppress PossiblyInvalidCast */ - return (string)$data->getValue(); + return (string) $data->getValue(); } return (new HtmlDumper())->dump($data, true); diff --git a/src/Sender/Frontend/Message/Connect.php b/src/Sender/Frontend/Message/Connect.php index a3e9ef26..b60ed83a 100644 --- a/src/Sender/Frontend/Message/Connect.php +++ b/src/Sender/Frontend/Message/Connect.php @@ -16,8 +16,7 @@ public function __construct( public readonly string $client, public readonly int $ping = 25, public readonly bool $pong = true, - ) { - } + ) {} public function jsonSerialize(): array { @@ -25,7 +24,7 @@ public function jsonSerialize(): array 'client' => $this->client, 'version' => Info::VERSION, 'subs' => [ - 'events' => (object)[], + 'events' => (object) [], ], 'ping' => $this->ping, 'pong' => $this->pong, diff --git a/src/Sender/Frontend/Message/EventCollection.php b/src/Sender/Frontend/Message/EventCollection.php index f1df08b2..6090b13b 100644 --- a/src/Sender/Frontend/Message/EventCollection.php +++ b/src/Sender/Frontend/Message/EventCollection.php @@ -18,8 +18,7 @@ final class EventCollection implements \JsonSerializable public function __construct( public readonly array $events, public readonly array $meta = [], - ) { - } + ) {} public function jsonSerialize(): array { diff --git a/src/Sender/Frontend/Message/Push.php b/src/Sender/Frontend/Message/Push.php index 808a1ffb..a0c68126 100644 --- a/src/Sender/Frontend/Message/Push.php +++ b/src/Sender/Frontend/Message/Push.php @@ -15,8 +15,7 @@ public function __construct( public readonly string $event, public readonly string $channel, public readonly mixed $data, - ) { - } + ) {} public function jsonSerialize(): array { diff --git a/src/Sender/Frontend/Message/Response.php b/src/Sender/Frontend/Message/Response.php index a1c1017b..c59a160d 100644 --- a/src/Sender/Frontend/Message/Response.php +++ b/src/Sender/Frontend/Message/Response.php @@ -15,8 +15,7 @@ public function __construct( public readonly string|int $id, public ?Rpc $rpc = null, public ?Connect $connect = null, - ) { - } + ) {} public function jsonSerialize(): array { diff --git a/src/Sender/Frontend/Message/Rpc.php b/src/Sender/Frontend/Message/Rpc.php index e6773af0..2ce3b167 100644 --- a/src/Sender/Frontend/Message/Rpc.php +++ b/src/Sender/Frontend/Message/Rpc.php @@ -13,8 +13,7 @@ final class Rpc implements JsonSerializable { public function __construct( public readonly mixed $data, - ) { - } + ) {} public function jsonSerialize(): array { diff --git a/src/Sender/Frontend/Message/Settings.php b/src/Sender/Frontend/Message/Settings.php index faff1b80..990410a5 100644 --- a/src/Sender/Frontend/Message/Settings.php +++ b/src/Sender/Frontend/Message/Settings.php @@ -14,15 +14,14 @@ final class Settings implements JsonSerializable { public function __construct( public readonly string $number = Info::VERSION, - ) { - } + ) {} public function jsonSerialize(): array { return [ 'auth' => [ 'enabled' => false, - 'login_url' => '/auth/sso/login' + 'login_url' => '/auth/sso/login', ], 'version' => Info::VERSION, ]; diff --git a/src/Sender/Frontend/Message/Success.php b/src/Sender/Frontend/Message/Success.php index 3489da74..ee5ef47b 100644 --- a/src/Sender/Frontend/Message/Success.php +++ b/src/Sender/Frontend/Message/Success.php @@ -14,8 +14,7 @@ final class Success implements JsonSerializable public function __construct( public readonly int $code = 200, public readonly bool $status = true, - ) { - } + ) {} public function jsonSerialize(): array { diff --git a/src/Sender/Frontend/Message/Version.php b/src/Sender/Frontend/Message/Version.php index 62f58789..9837920c 100644 --- a/src/Sender/Frontend/Message/Version.php +++ b/src/Sender/Frontend/Message/Version.php @@ -14,8 +14,7 @@ final class Version implements JsonSerializable { public function __construct( public readonly string $number = Info::VERSION, - ) { - } + ) {} public function jsonSerialize(): array { diff --git a/src/Sender/Frontend/Service.php b/src/Sender/Frontend/Service.php index 39c1afb8..1c47e604 100644 --- a/src/Sender/Frontend/Service.php +++ b/src/Sender/Frontend/Service.php @@ -23,8 +23,7 @@ final class Service public function __construct( private readonly Logger $logger, private readonly EventStorage $eventsStorage, - ) { - } + ) {} #[StaticRoute(Method::Get, 'api/version')] public function version(): Version diff --git a/src/Sender/FrontendSender.php b/src/Sender/FrontendSender.php index ff41bb25..c819de3d 100644 --- a/src/Sender/FrontendSender.php +++ b/src/Sender/FrontendSender.php @@ -32,8 +32,7 @@ private function __construct( private readonly ConnectionPool $connectionPool, private readonly Frontend\EventStorage $framesStorage, private readonly FrameHandler $handler, - ) { - } + ) {} /** * @param iterable $frames diff --git a/src/Sender/SocketSender.php b/src/Sender/SocketSender.php index 5e132c01..764b97b5 100644 --- a/src/Sender/SocketSender.php +++ b/src/Sender/SocketSender.php @@ -105,9 +105,9 @@ abstract protected function makePackage(string $payload): string; protected function preparePayload(iterable $frames): string { return '[' . \implode(',', \array_map( - static fn (Frame $frame): string => Json::encode($frame), - \is_array($frames) ? $frames : \iterator_to_array($frames), - )) . ']'; + static fn(Frame $frame): string => Json::encode($frame), + \is_array($frames) ? $frames : \iterator_to_array($frames), + )) . ']'; } /** diff --git a/src/Service/Config/ConfigAttribute.php b/src/Service/Config/ConfigAttribute.php index 435be89f..504597b3 100644 --- a/src/Service/Config/ConfigAttribute.php +++ b/src/Service/Config/ConfigAttribute.php @@ -7,6 +7,4 @@ /** * @internal */ -interface ConfigAttribute -{ -} +interface ConfigAttribute {} diff --git a/src/Service/Config/Env.php b/src/Service/Config/Env.php index a8633324..44a8bd68 100644 --- a/src/Service/Config/Env.php +++ b/src/Service/Config/Env.php @@ -12,6 +12,5 @@ final class Env implements ConfigAttribute { public function __construct( public string $name, - ) { - } + ) {} } diff --git a/src/Service/Config/InputArgument.php b/src/Service/Config/InputArgument.php index ff863541..3c000e9a 100644 --- a/src/Service/Config/InputArgument.php +++ b/src/Service/Config/InputArgument.php @@ -12,6 +12,5 @@ final class InputArgument implements ConfigAttribute { public function __construct( public string $name, - ) { - } + ) {} } diff --git a/src/Service/Config/InputOption.php b/src/Service/Config/InputOption.php index 26705348..10e023cb 100644 --- a/src/Service/Config/InputOption.php +++ b/src/Service/Config/InputOption.php @@ -12,6 +12,5 @@ final class InputOption implements ConfigAttribute { public function __construct( public string $name, - ) { - } + ) {} } diff --git a/src/Service/Config/XPath.php b/src/Service/Config/XPath.php index 136c93e6..219258a2 100644 --- a/src/Service/Config/XPath.php +++ b/src/Service/Config/XPath.php @@ -13,6 +13,5 @@ final class XPath implements ConfigAttribute public function __construct( public string $path, public int $key = 0, - ) { - } + ) {} } diff --git a/src/Service/Container.php b/src/Service/Container.php index 9613e511..9e7375bb 100644 --- a/src/Service/Container.php +++ b/src/Service/Container.php @@ -88,7 +88,7 @@ public function make(string $class, array $arguments = []): object try { $result = $this->injector->make($class, \array_merge((array) $binding, $arguments)); } catch (\Throwable $e) { - throw new class(previous: $e) extends \RuntimeException implements NotFoundExceptionInterface {}; + throw new class (previous: $e) extends \RuntimeException implements NotFoundExceptionInterface {}; } } diff --git a/src/Socket/Client.php b/src/Socket/Client.php index 494eba8c..daca1bf5 100644 --- a/src/Socket/Client.php +++ b/src/Socket/Client.php @@ -16,7 +16,7 @@ */ final class Client implements Destroyable { - /** @var string[] */ + /** @var string[] */ private array $writeQueue = []; /** @var string */ @@ -110,9 +110,7 @@ public function process(): void } while (true); } - protected function onInit(): void - { - } + protected function onInit(): void {} /** * @param callable(string): void $callable If non-static callable, it will be bound to the current instance. diff --git a/src/Socket/Exception/ClientDisconnected.php b/src/Socket/Exception/ClientDisconnected.php index 7690d944..be4c54c4 100644 --- a/src/Socket/Exception/ClientDisconnected.php +++ b/src/Socket/Exception/ClientDisconnected.php @@ -7,6 +7,4 @@ /** * @internal */ -class ClientDisconnected extends \RuntimeException -{ -} +class ClientDisconnected extends \RuntimeException {} diff --git a/src/Socket/Exception/ServerStopped.php b/src/Socket/Exception/ServerStopped.php index cd88d13a..19463665 100644 --- a/src/Socket/Exception/ServerStopped.php +++ b/src/Socket/Exception/ServerStopped.php @@ -7,6 +7,4 @@ /** * @internal */ -class ServerStopped extends \RuntimeException -{ -} +class ServerStopped extends \RuntimeException {} diff --git a/src/Socket/Server.php b/src/Socket/Server.php index 994c5113..a80e8725 100644 --- a/src/Socket/Server.php +++ b/src/Socket/Server.php @@ -95,7 +95,7 @@ public function process(): void try { /** @psalm-suppress MixedArgument */ $client = Client::init($socket, $this->payloadSize); - $key = (int)\array_key_last($this->clients) + 1; + $key = (int) \array_key_last($this->clients) + 1; $this->clients[$key] = $client; $this->clientInflector !== null and ($this->clientInflector)($client, $key); $this->fibers[$key] = new Fiber($client->process(...)); diff --git a/src/Support/Caster/EnumValue.php b/src/Support/Caster/EnumValue.php index 76feddbe..ad6a6f89 100644 --- a/src/Support/Caster/EnumValue.php +++ b/src/Support/Caster/EnumValue.php @@ -13,7 +13,5 @@ public function __construct( public readonly string $class, public readonly string $name, public readonly int $value, - ) { - } + ) {} } - diff --git a/src/Support/TemplateEngine.php b/src/Support/TemplateEngine.php index fb709c79..a97d6a5d 100644 --- a/src/Support/TemplateEngine.php +++ b/src/Support/TemplateEngine.php @@ -12,8 +12,7 @@ final class TemplateEngine { public function __construct( private readonly string $templateDir - ) { - } + ) {} public function render(string $template, array $data = []): string { diff --git a/src/Test/Mock/StreamClientMock/DisconnectCommand.php b/src/Test/Mock/StreamClientMock/DisconnectCommand.php index 2d62e79d..8bc32ae6 100644 --- a/src/Test/Mock/StreamClientMock/DisconnectCommand.php +++ b/src/Test/Mock/StreamClientMock/DisconnectCommand.php @@ -7,6 +7,4 @@ /** * @internal */ -class DisconnectCommand -{ -} +class DisconnectCommand {} diff --git a/src/Traffic/Dispatcher/Monolog.php b/src/Traffic/Dispatcher/Monolog.php index 25cb1edc..d0db5c44 100644 --- a/src/Traffic/Dispatcher/Monolog.php +++ b/src/Traffic/Dispatcher/Monolog.php @@ -27,7 +27,7 @@ public function dispatch(StreamClient $stream): iterable } yield new Frame\Monolog( - (array)\json_decode($line, true, 512, JSON_THROW_ON_ERROR), + (array) \json_decode($line, true, 512, JSON_THROW_ON_ERROR), $stream->getCreatedAt(), ); } diff --git a/src/Traffic/Message/Headers.php b/src/Traffic/Message/Headers.php index 5c25892a..14c2f301 100644 --- a/src/Traffic/Message/Headers.php +++ b/src/Traffic/Message/Headers.php @@ -99,7 +99,7 @@ private function setHeaders(array $headers): void if (\is_int($header)) { // If a header name was set to a numeric string, PHP will cast the key to an int. // We must cast it back to a string in order to comply with validation. - $header = (string)$header; + $header = (string) $header; } $value = $this->validateAndTrimHeader($header, $value); @@ -146,13 +146,13 @@ private function validateAndTrimHeader(string $header, mixed $values): array if (!\is_array($values)) { // This is simple, just one value. if ((!\is_numeric($values) && !\is_string($values)) || 1 !== \preg_match( - "@^[ \t\x21-\x7E\x80-\xFF]*$@", - (string)$values, - )) { + "@^[ \t\x21-\x7E\x80-\xFF]*$@", + (string) $values, + )) { throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings'); } - return [\trim((string)$values, " \t")]; + return [\trim((string) $values, " \t")]; } if (empty($values)) { @@ -165,13 +165,13 @@ private function validateAndTrimHeader(string $header, mixed $values): array $returnValues = []; foreach ($values as $v) { if ((!\is_numeric($v) && !\is_string($v)) || 1 !== \preg_match( - "@^[ \t\x21-\x7E\x80-\xFF]*$@D", - (string)$v, - )) { + "@^[ \t\x21-\x7E\x80-\xFF]*$@D", + (string) $v, + )) { throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings'); } - $returnValues[] = \trim((string)$v, " \t"); + $returnValues[] = \trim((string) $v, " \t"); } return $returnValues; diff --git a/src/Traffic/Message/Multipart/Field.php b/src/Traffic/Message/Multipart/Field.php index 80c74563..8fc50c11 100644 --- a/src/Traffic/Message/Multipart/Field.php +++ b/src/Traffic/Message/Multipart/Field.php @@ -38,8 +38,8 @@ public static function fromArray(array $data): self public function jsonSerialize(): array { return parent::jsonSerialize() + [ - 'value' => $this->value, - ]; + 'value' => $this->value, + ]; } public function getValue(): string diff --git a/src/Traffic/Message/Multipart/Part.php b/src/Traffic/Message/Multipart/Part.php index 0e958b5d..fcf3d0e6 100644 --- a/src/Traffic/Message/Multipart/Part.php +++ b/src/Traffic/Message/Multipart/Part.php @@ -52,7 +52,7 @@ public static function create(array $headers): Part ? ($matches['a'] ?: $matches['b']) : null; $fileName = $fileName !== null ? \html_entity_decode($fileName) : null; - $isFile = (string)$fileName !== '' + $isFile = (string) $fileName !== '' || \preg_match('/text\\/.++/', self::findHeader($headers, 'Content-Type')[0] ?? 'text/plain') !== 1; return match ($isFile) { diff --git a/src/Traffic/Message/Smtp.php b/src/Traffic/Message/Smtp.php index 896b3bad..b9eba8a2 100644 --- a/src/Traffic/Message/Smtp.php +++ b/src/Traffic/Message/Smtp.php @@ -133,7 +133,7 @@ public function getProtocol(): array */ public function getSender(): array { - $addrs = \array_unique(\array_merge((array)($this->protocol['FROM'] ?? []), $this->getHeader('From'))); + $addrs = \array_unique(\array_merge((array) ($this->protocol['FROM'] ?? []), $this->getHeader('From'))); return \array_map([$this, 'parseContact'], $addrs); } @@ -189,7 +189,8 @@ public function getMessage(MessageFormat $type): ?Field return null; } - private function parseContact(string $line): Contact { + private function parseContact(string $line): Contact + { if (\preg_match('/^\s*(?.*)\s*<(?.*)>\s*$/', $line, $matches) === 1) { return new Contact($matches['name'] ?: null, $matches['email'] ?: null); } diff --git a/src/Traffic/Message/Smtp/Contact.php b/src/Traffic/Message/Smtp/Contact.php index 20210602..8f6130e9 100644 --- a/src/Traffic/Message/Smtp/Contact.php +++ b/src/Traffic/Message/Smtp/Contact.php @@ -14,6 +14,5 @@ final class Contact public function __construct( public readonly ?string $name, public readonly ?string $email, - ) { - } + ) {} } diff --git a/src/Traffic/Parser/Http.php b/src/Traffic/Parser/Http.php index e541946e..a1f2cf5e 100644 --- a/src/Traffic/Parser/Http.php +++ b/src/Traffic/Parser/Http.php @@ -124,7 +124,7 @@ private function parseBody(StreamClient $stream, ServerRequestInterface $request // Guess length $length = $request->hasHeader('Content-Length') ? $request->getHeaderLine('Content-Length') : null; - $length = \is_numeric($length) ? (int)$length : null; + $length = \is_numeric($length) ? (int) $length : null; $request = $request->withBody($this->createBody($stream, $length)); $request->getBody()->rewind(); diff --git a/src/Traffic/Websocket/Frame.php b/src/Traffic/Websocket/Frame.php index e4c0e7ac..8ddc5da4 100644 --- a/src/Traffic/Websocket/Frame.php +++ b/src/Traffic/Websocket/Frame.php @@ -116,8 +116,7 @@ public function __construct( public readonly Opcode $opcode, public readonly bool $fin = true, public readonly bool $rsv1 = false, - ) { - } + ) {} public static function text(string $content): self { diff --git a/src/Traffic/Websocket/StreamReader.php b/src/Traffic/Websocket/StreamReader.php index f05e3d35..6452da9f 100644 --- a/src/Traffic/Websocket/StreamReader.php +++ b/src/Traffic/Websocket/StreamReader.php @@ -21,7 +21,7 @@ final class StreamReader public static function readFrames(iterable $chunks): Generator { $parser = self::frameParser(); - $reader = (static fn () => yield from $chunks)(); + $reader = (static fn() => yield from $chunks)(); $buffer = ''; $isFirst = true; /** @var \Closure(int<1, max>): ?non-empty-string $read */ @@ -69,14 +69,14 @@ private static function frameParser(): Generator while (true) { // Read first byte $c = \ord(yield 1); - $fin = (bool)($c & 128); + $fin = (bool) ($c & 128); $opcode = Opcode::from($c & 0x0f); - $rsv1 = (bool)($c & 64); + $rsv1 = (bool) ($c & 64); // Read second byte $c = \ord(yield 1); $len = $c & 127; - $isMask = (bool)($c & 128); + $isMask = (bool) ($c & 128); // Parse length if ($len === 126) { diff --git a/src/functions.php b/src/functions.php index 0566d2f5..8da7c851 100644 --- a/src/functions.php +++ b/src/functions.php @@ -39,4 +39,3 @@ function trap(mixed ...$values): TrapHandle /** @psalm-suppress MixedAssignment */ AbstractCloner::$defaultCasters[EnumValue::class] ??= [ProtobufCaster::class, 'castEnum']; } - From 277c3cb75a5a9dd10026676d3dfcd9947a1adcd5 Mon Sep 17 00:00:00 2001 From: chachanagov Date: Tue, 7 May 2024 23:39:59 +0800 Subject: [PATCH 038/106] Auto-pusher for code style fixer (#70) * Added CS Fixer, corrected code for compliance with PER 2.0 * fix github workflow * Fix rule for anonymous classes Revert src/Test/Proto * Fix rule for anonymous classes Revert src/Test/Proto * Revert src/Test/Proto/Metadata/Message.php * Automatically apply php-cs-fixer changes * Test CS fixer * Apply php-cs-fixer changes --------- Co-authored-by: chachanagov --- .github/workflows/cs-fixser.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/cs-fixser.yml b/.github/workflows/cs-fixser.yml index 9647d544..d17e5ecc 100644 --- a/.github/workflows/cs-fixser.yml +++ b/.github/workflows/cs-fixser.yml @@ -72,3 +72,10 @@ jobs: - name: Check Coding Standards run: vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff -vvv --using-cache=no + + - uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Apply php-cs-fixer changes + branch: ${{ github.head_ref }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 173f3f38751831923b72e7cc5bad14e76e0b725b Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 02:57:13 +0300 Subject: [PATCH 039/106] ci: auto release support by release-please action --- .github/.commitlint.config.mjs | 44 ++++++++++++++++ .github/.cz.config.js | 65 ++++++++++++++++++++++++ .github/.release-please-config.json | 72 +++++++++++++++++++++++++++ .github/.release-please-manifest.json | 3 ++ .github/workflows/create-release.yml | 27 ++++++++++ 5 files changed, 211 insertions(+) create mode 100644 .github/.commitlint.config.mjs create mode 100644 .github/.cz.config.js create mode 100644 .github/.release-please-config.json create mode 100644 .github/.release-please-manifest.json create mode 100644 .github/workflows/create-release.yml diff --git a/.github/.commitlint.config.mjs b/.github/.commitlint.config.mjs new file mode 100644 index 00000000..49f1f956 --- /dev/null +++ b/.github/.commitlint.config.mjs @@ -0,0 +1,44 @@ +// More info: https://github.com/wayofdev/npm-shareable-configs/blob/master/packages/commitlint-config/src/index.js +const automaticCommitPattern = /^chore\(release\):.*\[skip ci]/ + +export default { + extends: ['@commitlint/config-conventional'], + /* + This resolves a linting conflict between commitlint's body-max-line-length + due to @semantic-release/git putting release notes in the commit body + https://github.com/semantic-release/git/issues/331 + */ + ignores: [(commitMessage) => automaticCommitPattern.test(commitMessage)], + rules: { + 'body-leading-blank': [1, 'always'], + 'body-max-line-length': [2, 'always', 120], + 'footer-leading-blank': [1, 'always'], + 'footer-max-line-length': [2, 'always', 120], + 'header-max-length': [2, 'always', 100], + 'scope-case': [2, 'always', 'lower-case'], + 'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']], + 'subject-empty': [2, 'never'], + 'subject-full-stop': [2, 'never', '.'], + 'type-case': [2, 'always', 'lower-case'], + 'type-empty': [2, 'never'], + 'type-enum': [ + 2, + 'always', + [ + 'feat', // New feature + 'fix', // Bug fix + 'perf', // Performance improvement + 'docs', // Documentation changes + 'style', // Code style update (formatting, missing semi colons, etc) + 'deps', // Dependency updates + 'refactor', // Code refactoring + 'ci', // Continuous integration changes + 'test', // Adding missing tests + 'revert', // Revert to a previous commit + 'build', // Changes that affect the build system + 'chore', // Other changes that don't modify src or test files + 'security', // Security improvements + ], + ], + }, +} diff --git a/.github/.cz.config.js b/.github/.cz.config.js new file mode 100644 index 00000000..5f951c6c --- /dev/null +++ b/.github/.cz.config.js @@ -0,0 +1,65 @@ +// @see https://cz-git.qbb.sh/config/#configure-template +module.exports = { + alias: { fd: 'docs: fix typos' }, + messages: { + type: 'Select the type of change that you\'re committing:', + scope: 'Denote the SCOPE of this change (optional):', + customScope: 'Denote the SCOPE of this change:', + subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n', + body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n', + breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n', + footerPrefixesSelect: 'Select the ISSUES type of changeList by this change (optional):', + customFooterPrefix: 'Input ISSUES prefix:', + footer: 'List any ISSUES by this change. E.g.: #31, #34:\n', + generatingByAI: 'Generating your AI commit subject...', + generatedSelectByAI: 'Select suitable subject by AI generated:', + confirmCommit: 'Are you sure you want to proceed with the commit above?' + }, + types: [ + { value: 'feat', name: 'feat: A new feature', emoji: ':sparkles:' }, + { value: 'fix', name: 'fix: A bug fix', emoji: ':bug:' }, + { value: 'perf', name: 'perf: A code change that improves performance', emoji: ':zap:' }, + { value: 'docs', name: 'docs: Documentation only changes', emoji: ':memo:' }, + { value: 'style', name: 'style: Changes that do not affect the meaning of the code', emoji: ':lipstick:' }, + { value: 'deps', name: 'deps: A dependency update', emoji: ':package:' }, + { value: 'refactor', name: 'refactor: A code change that neither fixes a bug nor adds a feature', emoji: ':recycle:' }, + { value: 'ci', name: 'ci: Changes to our CI configuration files and scripts', emoji: ':ferris_wheel:' }, + { value: 'test', name: 'test: Adding missing tests or correcting existing tests', emoji: ':white_check_mark:' }, + { value: 'revert', name: 'revert: Reverts a previous commit', emoji: ':rewind:' }, + { value: 'build', name: 'build: Changes that affect the build system or external dependencies', emoji: ':package:' }, + { value: 'chore', name: 'chore: Other changes that don\'t modify src or test files', emoji: ':hammer:' }, + { value: 'security', name: 'security: A code change that fixes a security issue', emoji: ':lock:' } + ], + useEmoji: false, + emojiAlign: 'center', + useAI: false, + aiNumber: 1, + themeColorCode: '', + scopes: [], + allowCustomScopes: true, + allowEmptyScopes: true, + customScopesAlign: 'bottom', + customScopesAlias: 'custom', + emptyScopesAlias: 'empty', + upperCaseSubject: false, + markBreakingChangeMode: false, + allowBreakingChanges: ['feat', 'fix'], + breaklineNumber: 100, + breaklineChar: '|', + skipQuestions: [], + issuePrefixes: [{ value: 'closed', name: 'closed: ISSUES has been processed' }], + customIssuePrefixAlign: 'top', + emptyIssuePrefixAlias: 'skip', + customIssuePrefixAlias: 'custom', + allowCustomIssuePrefix: true, + allowEmptyIssuePrefix: true, + confirmColorize: true, + maxHeaderLength: Infinity, + maxSubjectLength: Infinity, + minSubjectLength: 0, + scopeOverrides: undefined, + defaultBody: '', + defaultIssues: '', + defaultScope: '', + defaultSubject: '' +} diff --git a/.github/.release-please-config.json b/.github/.release-please-config.json new file mode 100644 index 00000000..d89aa956 --- /dev/null +++ b/.github/.release-please-config.json @@ -0,0 +1,72 @@ +{ + "release-type": "php", + "packages": { + ".": { + "package-name": "trap", + "changelog-path": "/CHANGELOG.md" + } + }, + "include-component-in-tag": false, + "changelog-sections": [ + { + "type": "feat", + "section": "Features", + "hidden": false + }, + { + "type": "fix", + "section": "Bug Fixes", + "hidden": false + }, + { + "type": "perf", + "section": "Performance Improvements", + "hidden": false + }, + { + "type": "docs", + "section": "Documentation", + "hidden": false + }, + { + "type": "deps", + "section": "Dependencies", + "hidden": false + }, + { + "type": "refactor", + "section": "Code Refactoring", + "hidden": false + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System", + "hidden": true + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + }, + { + "type": "chore", + "section": "Miscellaneous", + "hidden": true + }, + { + "type": "style", + "section": "Styles", + "hidden": true + }, + { + "type": "revert", + "section": "Reverts", + "hidden": true + } + ] +} diff --git a/.github/.release-please-manifest.json b/.github/.release-please-manifest.json new file mode 100644 index 00000000..a4f6ddce --- /dev/null +++ b/.github/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.6.0" +} diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 00000000..db77c6d4 --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,27 @@ +--- + +# https://github.com/wayofdev/gh-actions/blob/master/.github/workflows/create-release.yml +# https://github.com/google-github-actions/release-please-action#release-types-supported +# https://github.com/googleapis/release-please/blob/main/docs/customizing.md + +on: # yamllint disable-line rule:truthy + push: + branches: + - master + +name: 📦 Create release + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: 🎉 Create release + uses: google-github-actions/release-please-action@v4 + id: release + with: + token: ${{ secrets.GITHUB_TOKEN }} + config-file: .github/.release-please-config.json + manifest-file: .github/.release-please-manifest.json + target-branch: master + +... From ebbe101e77f11a3be9379d8f98669c6211722e8e Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 16:45:03 +0300 Subject: [PATCH 040/106] chore: change version file --- .github/.commitlint.config.mjs | 1 + .github/.release-please-config.json | 5 +++++ .github/workflows/create-release.yml | 2 +- .github/.release-please-manifest.json => src/version.json | 0 4 files changed, 7 insertions(+), 1 deletion(-) rename .github/.release-please-manifest.json => src/version.json (100%) diff --git a/.github/.commitlint.config.mjs b/.github/.commitlint.config.mjs index 49f1f956..8b68c159 100644 --- a/.github/.commitlint.config.mjs +++ b/.github/.commitlint.config.mjs @@ -34,6 +34,7 @@ export default { 'refactor', // Code refactoring 'ci', // Continuous integration changes 'test', // Adding missing tests + 'tests', // Adding missing tests 'revert', // Revert to a previous commit 'build', // Changes that affect the build system 'chore', // Other changes that don't modify src or test files diff --git a/.github/.release-please-config.json b/.github/.release-please-config.json index d89aa956..afcbefa3 100644 --- a/.github/.release-please-config.json +++ b/.github/.release-please-config.json @@ -43,6 +43,11 @@ "section": "Tests", "hidden": true }, + { + "type": "tests", + "section": "Tests", + "hidden": true + }, { "type": "build", "section": "Build System", diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index db77c6d4..eef7915a 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -21,7 +21,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} config-file: .github/.release-please-config.json - manifest-file: .github/.release-please-manifest.json + manifest-file: src/version.json target-branch: master ... diff --git a/.github/.release-please-manifest.json b/src/version.json similarity index 100% rename from .github/.release-please-manifest.json rename to src/version.json From f7b9210ad152347963956d3bfcd56e2206a44a67 Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 17:18:30 +0300 Subject: [PATCH 041/106] feat: use latest version from auto-incrementing file --- src/Command/Run.php | 2 +- src/Info.php | 17 ++++++++++++++++- src/Sender/Frontend/Message/Connect.php | 2 +- src/Sender/Frontend/Message/Settings.php | 11 +++++++---- src/Sender/Frontend/Message/Version.php | 9 ++++++--- src/Sender/RemoteSender.php | 4 +++- 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/Command/Run.php b/src/Command/Run.php index acbd4460..519d2762 100644 --- a/src/Command/Run.php +++ b/src/Command/Run.php @@ -81,7 +81,7 @@ protected function execute( ): int { try { // Print intro - $output->writeln(\sprintf('%s v%s', Info::NAME, Info::VERSION)); + $output->writeln(\sprintf('%s v%s', Info::NAME, Info::version())); $output->write(Info::LOGO_CLI_COLOR . "\n", true, OutputInterface::OUTPUT_RAW); /** @var non-empty-string[] $senders */ diff --git a/src/Info.php b/src/Info.php index 59846fac..bb6913e0 100644 --- a/src/Info.php +++ b/src/Info.php @@ -10,7 +10,6 @@ class Info { public const NAME = 'Buggregator Trap'; - public const VERSION = '1.7.0'; public const LOGO_CLI_COLOR = << $this->client, - 'version' => Info::VERSION, + 'version' => Info::version(), 'subs' => [ 'events' => (object) [], ], diff --git a/src/Sender/Frontend/Message/Settings.php b/src/Sender/Frontend/Message/Settings.php index 990410a5..19f62fe0 100644 --- a/src/Sender/Frontend/Message/Settings.php +++ b/src/Sender/Frontend/Message/Settings.php @@ -12,9 +12,12 @@ */ final class Settings implements JsonSerializable { - public function __construct( - public readonly string $number = Info::VERSION, - ) {} + public readonly string $number; + + public function __construct() + { + $this->number = Info::version(); + } public function jsonSerialize(): array { @@ -23,7 +26,7 @@ public function jsonSerialize(): array 'enabled' => false, 'login_url' => '/auth/sso/login', ], - 'version' => Info::VERSION, + 'version' => $this->number, ]; } } diff --git a/src/Sender/Frontend/Message/Version.php b/src/Sender/Frontend/Message/Version.php index 9837920c..c463007a 100644 --- a/src/Sender/Frontend/Message/Version.php +++ b/src/Sender/Frontend/Message/Version.php @@ -12,9 +12,12 @@ */ final class Version implements JsonSerializable { - public function __construct( - public readonly string $number = Info::VERSION, - ) {} + public readonly string $number; + + public function __construct() + { + $this->number = Info::version(); + } public function jsonSerialize(): array { diff --git a/src/Sender/RemoteSender.php b/src/Sender/RemoteSender.php index 6279903f..10509487 100644 --- a/src/Sender/RemoteSender.php +++ b/src/Sender/RemoteSender.php @@ -14,12 +14,14 @@ final class RemoteSender extends SocketSender { private string $uuid; + private readonly string $clientVersion; + public function __construct( string $uuid = null, string $host = '127.0.0.1', int $port = 9912, - private readonly string $clientVersion = Info::VERSION, ) { + $this->clientVersion = Info::version(); $this->uuid = $uuid ?? Uuid::generate(); parent::__construct($host, $port); From 1f49bb8e0249a01d7080ea6bedeea6b6e6ae80b2 Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 17:39:32 +0300 Subject: [PATCH 042/106] fix: use .build directory for all development tools --- .php-cs-fixer.php | 2 +- phpunit.xml | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index c5221478..228e80a3 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -21,4 +21,4 @@ ->in([__DIR__ . '/src']) ->exclude(['Test/Proto/']), ) - ->setCacheFile('.cache/.php-cs-fixer.cache'); + ->setCacheFile(__DIR__ . '/.build/php-cs-fixer/php-cs-fixer.cache'); diff --git a/phpunit.xml b/phpunit.xml index 0d718405..a0ba0721 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,17 +1,13 @@ From 3d1738fff0e0784a67930340bee7d493ffeacba5 Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 17:39:54 +0300 Subject: [PATCH 043/106] feat: add initial Makefile --- Makefile | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..1247a3b0 --- /dev/null +++ b/Makefile @@ -0,0 +1,279 @@ +-include .env + +# BuildKit enables higher performance docker builds and caching possibility +# to decrease build times and increase productivity for free. +# https://docs.docker.com/compose/environment-variables/envvars/ +export DOCKER_BUILDKIT ?= 1 + +# Docker binary to use, when executing docker tasks +DOCKER ?= docker + +# Binary to use, when executing docker-compose tasks +DOCKER_COMPOSE ?= $(DOCKER) compose + +# Support image with all needed binaries, like envsubst, mkcert, wait4x +SUPPORT_IMAGE ?= wayofdev/build-deps:alpine-latest + +APP_RUNNER ?= $(DOCKER_COMPOSE) run --rm --no-deps app +APP_COMPOSER ?= $(APP_RUNNER) composer + +BUILDER_PARAMS ?= $(DOCKER) run --rm -i \ + --env-file ./.env \ + --env COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \ + --env COMPOSER_AUTH="$(COMPOSER_AUTH)" + +BUILDER ?= $(BUILDER_PARAMS) $(SUPPORT_IMAGE) +BUILDER_WIRED ?= $(BUILDER_PARAMS) --network project.$(COMPOSE_PROJECT_NAME) $(SUPPORT_IMAGE) + +# Shorthand envsubst command, executed through build-deps +ENVSUBST ?= $(BUILDER) envsubst + +# Yamllint docker image +YAML_LINT_RUNNER ?= $(DOCKER) run --rm $$(tty -s && echo "-it" || echo) \ + -v $(PWD):/data \ + cytopia/yamllint:latest \ + -f colored . + +ACTION_LINT_RUNNER ?= $(DOCKER) run --rm $$(tty -s && echo "-it" || echo) \ + -v $(shell pwd):/repo \ + --workdir /repo \ + rhysd/actionlint:latest \ + -color + +MARKDOWN_LINT_RUNNER ?= $(DOCKER) run --rm $$(tty -s && echo "-it" || echo) \ + -v $(shell pwd):/app \ + --workdir /app \ + davidanson/markdownlint-cli2-rules:latest + +PHIVE_RUNNER ?= $(DOCKER_COMPOSE) run --rm --no-deps app + +EXPORT_VARS = '\ + $${COMPOSE_PROJECT_NAME} \ + $${COMPOSER_AUTH}' + + +# +# Self documenting Makefile code +# ------------------------------------------------------------------------------------ +ifneq ($(TERM),) + BLACK := $(shell tput setaf 0) + RED := $(shell tput setaf 1) + GREEN := $(shell tput setaf 2) + YELLOW := $(shell tput setaf 3) + LIGHTPURPLE := $(shell tput setaf 4) + PURPLE := $(shell tput setaf 5) + BLUE := $(shell tput setaf 6) + WHITE := $(shell tput setaf 7) + RST := $(shell tput sgr0) +else + BLACK := "" + RED := "" + GREEN := "" + YELLOW := "" + LIGHTPURPLE := "" + PURPLE := "" + BLUE := "" + WHITE := "" + RST := "" +endif +MAKE_LOGFILE = /tmp/buggregator-trap.log +MAKE_CMD_COLOR := $(BLUE) + +default: all + +help: ## Show this menu + @echo 'Management commands for package:' + @echo 'Usage:' + @echo ' ${MAKE_CMD_COLOR}make${RST} Setups dependencies for fresh-project, like composer install, git hooks and others...' + @grep -E '^[a-zA-Z_0-9%-]+:.*?## .*$$' Makefile | awk 'BEGIN {FS = ":.*?## "}; {printf " ${MAKE_CMD_COLOR}make %-21s${RST} %s\n", $$1, $$2}' + @echo + @echo ' 📑 Logs are stored in $(MAKE_LOGFILE)' + @echo + @echo ' 📦 Package trap (github.com/buggregator/trap)' + @echo ' 🤠 Makefile Author Andrij Orlenko (github.com/lotyp)' + @echo ' 🏢 ${YELLOW}Org 🐞 buggregator (github.com/buggregator)${RST}' + @echo +.PHONY: help + +.EXPORT_ALL_VARIABLES: + +# +# Default action +# Defines default command when `make` is executed without additional parameters +# ------------------------------------------------------------------------------------ +all: env prepare install hooks phive up +.PHONY: all + +# +# System Actions +# ------------------------------------------------------------------------------------ +env: ## Generate .env file from example, use `make env force=true`, to force re-create file +ifeq ($(FORCE),true) + @echo "${YELLOW}Force re-creating .env file from example...${RST}" + $(ENVSUBST) $(EXPORT_VARS) < ./.env.example > ./.env +else ifneq ("$(wildcard ./.env)","") + @echo "" + @echo "${YELLOW}The .env file already exists! Use FORCE=true to re-create.${RST}" +else + @echo "Creating .env file from example" + $(ENVSUBST) $(EXPORT_VARS) < ./.env.example > ./.env +endif +.PHONY: env + +prepare: + mkdir -p .build/php-cs-fixer +.PHONY: prepare + +# +# Docker Actions +# ------------------------------------------------------------------------------------ +up: # Creates and starts containers, defined in docker-compose and override file + $(DOCKER_COMPOSE) up --remove-orphans -d +.PHONY: up + +down: # Stops and removes containers of this project + $(DOCKER_COMPOSE) down --remove-orphans --volumes +.PHONY: down + +restart: down up ## Runs down and up commands +.PHONY: restart + +clean: ## Stops containers if required and removes from system + $(DOCKER_COMPOSE) rm --force --stop +.PHONY: clean + +ps: ## List running project containers + $(DOCKER_COMPOSE) ps +.PHONY: ps + +logs: ## Show project docker logs with follow up mode enabled + $(DOCKER_COMPOSE) logs -f +.PHONY: logs + +pull: ## Pull and update docker images in this project + $(DOCKER_COMPOSE) pull +.PHONY: pull + +ssh: ## Login inside running docker container + $(APP_RUNNER) sh +.PHONY: ssh + +# +# Composer +# ------------------------------------------------------------------------------------ +install: ## Installs composer dependencies + $(APP_COMPOSER) install +.PHONY: install + +update: ## Updates composer dependencies by running composer update command + $(APP_COMPOSER) update +.PHONY: update + +phive: ## Installs dependencies with phive + $(APP_RUNNER) /usr/local/bin/phive install --trust-gpg-keys 0x033E5F8D801A2F8D +.PHONY: phive + +# +# Code Quality, Git, Linting +# ------------------------------------------------------------------------------------ +hooks: ## Install git hooks from pre-commit-config + pre-commit install + pre-commit install --hook-type commit-msg + pre-commit autoupdate +.PHONY: hooks + +lint: lint-yaml lint-actions lint-md lint-php lint-stan lint-composer lint-audit ## Runs all linting commands +.PHONY: lint + +lint-yaml: ## Lints yaml files inside project + @$(YAML_LINT_RUNNER) | tee -a $(MAKE_LOGFILE) +.PHONY: lint-yaml + +lint-actions: ## Lint all github actions + @$(ACTION_LINT_RUNNER) | tee -a $(MAKE_LOGFILE) +.PHONY: lint-actions + +lint-md: ## Lint all markdown files using markdownlint-cli2 + @$(MARKDOWN_LINT_RUNNER) --fix "**/*.md" "!CHANGELOG.md" "!vendor" | tee -a $(MAKE_LOGFILE) +.PHONY: lint-md + +lint-md-dry: ## Lint all markdown files using markdownlint-cli2 in dry-run mode + @$(MARKDOWN_LINT_RUNNER) "**/*.md" "!CHANGELOG.md" "!vendor" | tee -a $(MAKE_LOGFILE) +.PHONY: lint-md-dry + +lint-php: prepare ## Fixes code to follow coding standards using php-cs-fixer + $(APP_COMPOSER) cs:fix +.PHONY: lint-php + +lint-diff: prepare ## Runs php-cs-fixer in dry-run mode and shows diff which will by applied + $(APP_COMPOSER) cs:diff +.PHONY: lint-diff + +lint-stan: ## Runs phpstan – static analysis tool + $(APP_COMPOSER) stan +.PHONY: lint-stan + +lint-stan-ci: ## Runs phpstan – static analysis tool with github output (CI mode) + $(APP_COMPOSER) stan:ci +.PHONY: lint-stan-ci + +lint-stan-baseline: ## Runs phpstan to update its baseline + $(APP_COMPOSER) stan:baseline +.PHONY: lint-stan-baseline + +lint-psalm: ## Runs vimeo/psalm – static analysis tool + $(APP_COMPOSER) psalm +.PHONY: lint-psalm + +lint-psalm-ci: ## Runs vimeo/psalm – static analysis tool with github output (CI mode) + $(APP_COMPOSER) psalm:ci +.PHONY: lint-psalm-ci + +lint-psalm-baseline: ## Runs vimeo/psalm to update its baseline + $(APP_COMPOSER) psalm:baseline +.PHONY: lint-psalm-baseline + +lint-deps: ## Runs composer-require-checker – checks for dependencies that are not used + $(APP_RUNNER) .phive/composer-require-checker check \ + --config-file=/app/composer-require-checker.json \ + --verbose +.PHONY: lint-deps + +lint-composer: ## Normalize composer.json and composer.lock files + $(APP_COMPOSER) normalize +.PHONY: lint-composer + +lint-audit: ## Runs security checks for composer dependencies + $(APP_COMPOSER) audit +.PHONY: lint-security + +refactor: ## Runs rector – code refactoring tool + $(APP_COMPOSER) refactor +.PHONY: refactor + +# +# Testing +# ------------------------------------------------------------------------------------ +infect: ## Runs mutation tests with infection/infection + $(APP_COMPOSER) infect +.PHONY: infect + +infect-ci: ## Runs infection – mutation testing framework with github output (CI mode) + $(APP_COMPOSER) infect:ci +.PHONY: lint-infect-ci + +test: ## Run project php-unit and pest tests + $(APP_COMPOSER) test +.PHONY: test + +test-cc: ## Run project php-unit and pest tests in coverage mode and build report + $(APP_COMPOSER) test:cc +.PHONY: test-cc + +# +# Release +# ------------------------------------------------------------------------------------ +commit: + czg commit --config="./.github/.cz.config.js" +.PHONY: commit + From a39cdd90f401e1c20b9e831bb01f5721edf4a921 Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 17:49:45 +0300 Subject: [PATCH 044/106] fix: return experimental, if version is not found --- src/Info.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Info.php b/src/Info.php index bb6913e0..5d076900 100644 --- a/src/Info.php +++ b/src/Info.php @@ -26,19 +26,20 @@ class Info CONSOLE; public const TRAP_ROOT = __DIR__ . '/..'; + private const VERSION = 'experimental'; public static function version(): string { $versionPath = self::TRAP_ROOT . '/src/version.json'; if (!file_exists($versionPath)) { - throw new \RuntimeException('Version file not found.'); + return self::VERSION; } /** @var array $versionData */ $versionData = json_decode(file_get_contents($versionPath), true); if (!isset($versionData['.']) || !is_string($versionData['.'])) { - throw new \RuntimeException('Version key not found or is not a string in JSON.'); + return self::VERSION; } return $versionData['.']; } From 681820e8ebcaecd4c45ea5789edf09c4875dda9d Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 18:16:22 +0300 Subject: [PATCH 045/106] chore: simplify version function --- src/Info.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Info.php b/src/Info.php index 5d076900..ee97a046 100644 --- a/src/Info.php +++ b/src/Info.php @@ -31,16 +31,18 @@ class Info public static function version(): string { $versionPath = self::TRAP_ROOT . '/src/version.json'; - if (!file_exists($versionPath)) { + $versionContents = file_get_contents($versionPath); + + if ($versionContents === false) { return self::VERSION; } - /** @var array $versionData */ - $versionData = json_decode(file_get_contents($versionPath), true); + $versionData = json_decode($versionContents, true); - if (!isset($versionData['.']) || !is_string($versionData['.'])) { + if (!is_array($versionData) || !isset($versionData['.']) || !is_string($versionData['.'])) { return self::VERSION; } + return $versionData['.']; } } From 46b2460edef7f6239df6d6b2e00b49e82b1d5cdd Mon Sep 17 00:00:00 2001 From: lotyp Date: Fri, 10 May 2024 18:38:34 +0300 Subject: [PATCH 046/106] fix: cache version variable --- src/Info.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Info.php b/src/Info.php index ee97a046..24c5b85b 100644 --- a/src/Info.php +++ b/src/Info.php @@ -28,21 +28,31 @@ class Info public const TRAP_ROOT = __DIR__ . '/..'; private const VERSION = 'experimental'; + private static ?string $cachedVersion = null; + public static function version(): string { + if (self::$cachedVersion !== null) { + return self::$cachedVersion; + } + $versionPath = self::TRAP_ROOT . '/src/version.json'; $versionContents = file_get_contents($versionPath); if ($versionContents === false) { - return self::VERSION; + self::$cachedVersion = self::VERSION; + return self::$cachedVersion; } $versionData = json_decode($versionContents, true); if (!is_array($versionData) || !isset($versionData['.']) || !is_string($versionData['.'])) { - return self::VERSION; + self::$cachedVersion = self::VERSION; + return self::$cachedVersion; } - return $versionData['.']; + self::$cachedVersion = $versionData['.']; + + return self::$cachedVersion; } } From d86a1e04d5512f32adfa643d0ac43f3c888aa64a Mon Sep 17 00:00:00 2001 From: lotyp Date: Sat, 11 May 2024 22:40:12 +0300 Subject: [PATCH 047/106] feat: add docker compose support Added base PHP image to run local development and PHAR builds ci: added default markdownlint config feat: add box-project/box, composer-require-checker and composer-normalize PHARs feat: Makefile now builds trap PHAR locally using `make phar` command --- .env.example | 6 ++++++ .markdownlint.json | 7 +++++++ Makefile | 19 ++++++++++++++++--- box.json.dist | 22 ++++++++++++++++++++++ docker-compose.yaml | 24 ++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 .env.example create mode 100644 .markdownlint.json create mode 100644 box.json.dist create mode 100644 docker-compose.yaml diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..148f1607 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# https://docs.docker.com/compose/reference/envvars/#compose_project_name +# With custom namespace provided, it will be used to prefix all services +# in Docker network for current project +COMPOSE_PROJECT_NAME=trap + +XDEBUG_MODE=coverage diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 00000000..77f382df --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint/main/schema/markdownlint-config-schema.json", + "line-length": false, + "no-inline-html": false, + "first-line-h1": false, + "no-duplicate-heading": false +} diff --git a/Makefile b/Makefile index 1247a3b0..d49ca9ee 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,10 @@ # https://docs.docker.com/compose/environment-variables/envvars/ export DOCKER_BUILDKIT ?= 1 +# User ID and Group ID to use inside docker containers +HOST_UID ?= $(shell id -u) +HOST_GID ?= $(shell id -g) + # Docker binary to use, when executing docker tasks DOCKER ?= docker @@ -49,8 +53,9 @@ PHIVE_RUNNER ?= $(DOCKER_COMPOSE) run --rm --no-deps app EXPORT_VARS = '\ $${COMPOSE_PROJECT_NAME} \ - $${COMPOSER_AUTH}' - + $${COMPOSER_AUTH} \ + $${HOST_UID} \ + $${HOST_GID}' # # Self documenting Makefile code @@ -170,9 +175,17 @@ update: ## Updates composer dependencies by running composer update command .PHONY: update phive: ## Installs dependencies with phive - $(APP_RUNNER) /usr/local/bin/phive install --trust-gpg-keys 0x033E5F8D801A2F8D + $(APP_RUNNER) /usr/local/bin/phive install --trust-gpg-keys 0xC00543248C87FB13,0x033E5F8D801A2F8D,0x2DF45277AEF09A2F .PHONY: phive +phar: + $(APP_RUNNER) sh -c "git config --global --add safe.directory /app \ + && .phive/box validate \ + && .phive/box compile \ + && .phive/box info .build/phar/trap.phar \ + && .build/phar/trap.phar" +.PHONY: phar + # # Code Quality, Git, Linting # ------------------------------------------------------------------------------------ diff --git a/box.json.dist b/box.json.dist new file mode 100644 index 00000000..5fe1f90c --- /dev/null +++ b/box.json.dist @@ -0,0 +1,22 @@ +{ + "$schema": "https://raw.githubusercontent.com/box-project/box/main/res/schema.json", + "compactors": [ + "KevinGH\\Box\\Compactor\\Json", + "KevinGH\\Box\\Compactor\\Php" + ], + "compression": "GZ", + "git": "git", + "directories": [ + "resources", + "src", + "vendor" + ], + "files": [ + "trap.xml", + "bin/trap", + "LICENSE.md", + "composer.json", + "composer.lock" + ], + "output": ".build/phar/trap.phar" +} diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..623965f8 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,24 @@ +--- + +version: '3.9' + +services: + app: + image: wayofdev/php-dev:8.3-cli-alpine-latest + container_name: ${COMPOSE_PROJECT_NAME}-app + restart: on-failure + networks: + - default + volumes: + - ./:/app:rw + - ~/.composer:/.composer + env_file: + - .env + environment: + PHIVE_HOME: /app/.phive + +networks: + default: + name: project.${COMPOSE_PROJECT_NAME} + +... From 711748641752e7fd18e95402a8900ce6f4b5e577 Mon Sep 17 00:00:00 2001 From: lotyp Date: Sat, 11 May 2024 22:55:33 +0300 Subject: [PATCH 048/106] ci: use composer-normalize PHAR to lint composer.json --- Makefile | 3 +-- composer.json | 63 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index d49ca9ee..4c1891f9 100644 --- a/Makefile +++ b/Makefile @@ -253,7 +253,7 @@ lint-deps: ## Runs composer-require-checker – checks for dependencies that are .PHONY: lint-deps lint-composer: ## Normalize composer.json and composer.lock files - $(APP_COMPOSER) normalize + $(APP_RUNNER) .phive/composer-normalize normalize .PHONY: lint-composer lint-audit: ## Runs security checks for composer dependencies @@ -289,4 +289,3 @@ test-cc: ## Run project php-unit and pest tests in coverage mode and build repor commit: czg commit --config="./.github/.cz.config.js" .PHONY: commit - diff --git a/composer.json b/composer.json index 582aeced..73c71224 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,20 @@ { "name": "buggregator/trap", - "type": "library", - "license": "BSD-3-Clause", "description": "A simple and powerful tool for debugging PHP applications.", - "homepage": "https://buggregator.dev/", + "license": "BSD-3-Clause", + "type": "library", "keywords": [ - "debug", "cli", "console", "sentry", "smtp", "dump", "binary dump", "websockets", "server", "helper", "dev" + "debug", + "cli", + "console", + "sentry", + "smtp", + "dump", + "binary dump", + "websockets", + "server", + "helper", + "dev" ], "authors": [ { @@ -17,22 +26,7 @@ "homepage": "https://github.com/butschster" } ], - "autoload": { - "psr-4": { - "Buggregator\\Trap\\": "src/" - }, - "files": [ - "src/functions.php" - ] - }, - "autoload-dev": { - "psr-4": { - "Buggregator\\Trap\\Tests\\": "tests/" - } - }, - "bin": [ - "bin/trap" - ], + "homepage": "https://buggregator.dev/", "funding": [ { "type": "github", @@ -47,11 +41,6 @@ "url": "https://patreon.com/butschster" } ], - "config": { - "sort-packages": true - }, - "minimum-stability": "dev", - "prefer-stable": true, "require": { "php": ">=8.1", "ext-sockets": "*", @@ -77,6 +66,30 @@ "ext-simplexml": "To load trap.xml", "roxblnfk/unpoly": "If you want to remove unnecessary PHP polyfills depend on PHP version." }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "Buggregator\\Trap\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Buggregator\\Trap\\Tests\\": "tests/" + } + }, + "bin": [ + "bin/trap" + ], + "config": { + "allow-plugins": { + "ergebnis/composer-normalize": true + }, + "sort-packages": true + }, "scripts": { "cs-check": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run", "cs-fix": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php -vvv --using-cache=no" From a108476c674fac90f08e0576fb480946cc211293 Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 02:58:22 +0300 Subject: [PATCH 049/106] refactor: github workflows --- .editorconfig | 3 + .github/FUNDING.yml | 2 + .github/workflows/build-release.yml | 127 + .github/workflows/cs-fixser.yml | 81 - .github/workflows/lint-php-files.yml | 84 + .github/workflows/testing.yml | 6 +- .gitignore | 1 - .php-cs-fixer.php => .php-cs-fixer.php.dist | 0 .yamllint.yaml | 56 + composer.json | 4 +- composer.lock | 4855 +++++++++++++++++++ 11 files changed, 5134 insertions(+), 85 deletions(-) create mode 100644 .github/workflows/build-release.yml delete mode 100644 .github/workflows/cs-fixser.yml create mode 100644 .github/workflows/lint-php-files.yml rename .php-cs-fixer.php => .php-cs-fixer.php.dist (100%) create mode 100644 .yamllint.yaml create mode 100644 composer.lock diff --git a/.editorconfig b/.editorconfig index 50dc6257..55de6f89 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,3 +16,6 @@ end_of_line = crlf [*.md] trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index d86a3eab..0c47a2f6 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,5 @@ +--- + # These are supported funding model platforms patreon: roxblnfk diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 00000000..6b3aeeab --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,127 @@ +--- + +on: # yamllint disable-line rule:truthy + push: + tags: + - '*.*.*' + +name: 📦 Build PHAR release + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 4 + strategy: + matrix: + php-version: + - '8.2' + dependencies: + - locked + env: + TRAP_PHAR: ".build/phar/trap.phar" + TRAP_PHAR_SIGNATURE: ".build/phar/trap.phar.asc" + GPG_KEYS: ".build/phar/keys.asc" + GPG_KEYS_ENCRYPTED: "phar/keys.asc.gpg" + steps: + - name: 📦 Check out the codebase + uses: actions/checkout@v4.1.5 + + - name: 🛠️ Setup PHP + uses: shivammathur/setup-php@2.30.4 + with: + php-version: ${{ matrix.php-version }} + extensions: none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets + ini-values: error_reporting=E_ALL + coverage: none + tools: phive + + - name: 🛠️ Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: 🤖 Validate composer.json and composer.lock + run: composer validate --ansi --strict + + - name: 🔍 Get composer cache directory + uses: wayofdev/gh-actions/actions/composer/get-cache-directory@v3.1.0 + + - name: ♻️ Restore cached dependencies installed with composer + uses: actions/cache@v4.0.2 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('composer.lock') }} + restore-keys: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- + + - name: 📥 Install "${{ matrix.dependencies }}" dependencies with composer + uses: wayofdev/gh-actions/actions/composer/install@v3.1.0 + with: + dependencies: ${{ matrix.dependencies }} + + - name: 📥 Install dependencies with phive + uses: wayofdev/gh-actions/actions/phive/install@v3.1.0 + with: + phive-home: '.phive' + trust-gpg-keys: '0xC00543248C87FB13,0x033E5F8D801A2F8D,0x2DF45277AEF09A2F' + + - name: 🔍 Validate configuration for box-project/box + run: .phive/box validate box.json.dist --ansi + + - name: 🤖 Compile trap.phar with box-project/box + run: .phive/box compile --ansi + + - name: 💥 Show info about trap.phar with box-project/box + run: .phive/box info ${{ env.TRAP_PHAR }} --ansi + + - name: 🤔 Run trap.phar help command + run: ${{ env.TRAP_PHAR }} --help + + - name: 🔍 Show gpg version + run: gpg --version + + - name: 🔑 Decrypt keys.asc.gpg with gpg + run: gpg --batch --output ${{ env.GPG_KEYS }} --passphrase \"${{ secrets.GPG_DECRYPT_PASSPHRASE }}\" --yes --decrypt ${{ env.GPG_KEYS_ENCRYPTED }} + + - name: 📥 Import keys from keys.asc with gpg + run: gpg --batch --import ${{ env.GPG_KEYS }} + + - name: 🔐 Sign trap.phar with gpg + run: gpg --armor --local-user \"${{ secrets.GPG_LOCAL_USER }}\" --output ${{ env.TRAP_PHAR_SIGNATURE }} --passphrase \"${{ secrets.GPG_KEY_PASSPHRASE }}\" --pinentry-mode loopback --yes --detach-sig ${{ env.TRAP_PHAR }} + + - name: ❎ Remove decrypted keys.asc + run: rm ${{ env.GPG_KEYS }} + + - name: 🏷️ Determine tag + run: "echo \"RELEASE_TAG=${GITHUB_REF#refs/tags/}\" >> $GITHUB_ENV" + + - name: 📤 Upload release assets + uses: actions/github-script@v7.0.1 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + script: | + const fs = require("fs"); + + const files = [ + { + name: "trap.phar", + path: process.env.TRAP_PHAR, + }, + { + name: "trap.phar.asc", + path: process.env.TRAP_PHAR_SIGNATURE, + }, + ]; + + for (const file of files) { + try { + await github.rest.repos.uploadReleaseAsset({ + data: fs.readFileSync(file.path), + name: file.name, + origin: process.env.RELEASE_UPLOAD_URL, + owner: context.repo.owner, + release_id: process.env.RELEASE_ID, + repo: context.repo.repo, + }); + } catch (error) { + core.setFailed(error.message); + } + } diff --git a/.github/workflows/cs-fixser.yml b/.github/workflows/cs-fixser.yml deleted file mode 100644 index d17e5ecc..00000000 --- a/.github/workflows/cs-fixser.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: CS-Fixer - -on: - pull_request: - paths-ignore: - - 'docs/**' - - 'bin/**' - - 'resources/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' - - 'psalm.xml' - - push: - paths-ignore: - - 'docs/**' - - 'bin/**' - - 'resources/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' - - 'psalm.xml' - -jobs: - cs: - name: PHP ${{ matrix.php }}-${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - php: [ 8.2 ] - os: [ ubuntu-latest ] - steps: - - name: Set Git To Use LF - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - - name: Setup PHP ${{ matrix.php }} - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - tools: composer:v2 - - - name: Check Out Code - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Validate composer.json and composer.lock - run: composer validate --strict - - - name: Get Composer Cache Directory - id: composer-cache - run: | - echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache Composer Dependencies - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: php-${{ matrix.php }}-${{ matrix.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: | - php-${{ matrix.php }}-${{ matrix.os }}-composer- - - - name: Install Composer Dependencies - run: composer install --prefer-dist --no-interaction - - - name: Check Coding Standards - run: vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff -vvv --using-cache=no - - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: Apply php-cs-fixer changes - branch: ${{ github.head_ref }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint-php-files.yml b/.github/workflows/lint-php-files.yml new file mode 100644 index 00000000..eb2c5a76 --- /dev/null +++ b/.github/workflows/lint-php-files.yml @@ -0,0 +1,84 @@ +--- + +on: # yamllint disable-line rule:truthy + pull_request: + paths: + - 'src/**' + - 'tests/**' + - 'bin/trap' + - '.php-cs-fixer.php.dist' + push: + paths: + - 'src/**' + - 'tests/**' + - 'bin/trap' + - '.php-cs-fixer.php.dist' + +name: 🧹 Fix PHP coding standards + +jobs: + coding-standards: + runs-on: ubuntu-latest + timeout-minutes: 4 + strategy: + matrix: + php-version: + - '8.2' + dependencies: + - locked + permissions: + contents: write + steps: + - name: ⚙️ Set git to use LF line endings + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - name: 🛠️ Setup PHP + uses: shivammathur/setup-php@2.30.4 + with: + php-version: ${{ matrix.php-version }} + extensions: none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets + ini-values: error_reporting=E_ALL + coverage: none + + - name: 📦 Check out the codebase + uses: actions/checkout@v4.1.5 + + - name: 🛠️ Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: 🤖 Validate composer.json and composer.lock + run: composer validate --ansi --strict + + - name: 🔍 Get composer cache directory + uses: wayofdev/gh-actions/actions/composer/get-cache-directory@v3.1.0 + + - name: ♻️ Restore cached dependencies installed with composer + uses: actions/cache@v4.0.2 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('composer.lock') }} + restore-keys: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- + + - name: 📥 Install "${{ matrix.dependencies }}" dependencies with composer + uses: wayofdev/gh-actions/actions/composer/install@v3.1.0 + with: + dependencies: ${{ matrix.dependencies }} + + - name: 🛠️ Prepare environment + run: make prepare + + - name: 🚨 Run coding standards task + run: composer cs:diff + env: + PHP_CS_FIXER_IGNORE_ENV: true + + - name: 📤 Commit and push changed files back to GitHub + uses: stefanzweifel/git-auto-commit-action@v5.0.1 + with: + commit_message: 'style(php-cs-fixer): lint php files and fix coding standards' + branch: ${{ github.head_ref }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index d8e8773b..1b0660e1 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -1,6 +1,10 @@ +--- + name: Testing -on: [push, pull_request] +on: # yamllint disable-line rule:truthy + - push + - pull_request jobs: unit: diff --git a/.gitignore b/.gitignore index 4a5884fa..319b1c02 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,5 @@ !/.github/ /runtime/ /vendor/ -/composer.lock /.env *.log diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php.dist similarity index 100% rename from .php-cs-fixer.php rename to .php-cs-fixer.php.dist diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 00000000..40909417 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,56 @@ +--- + +extends: default + +ignore: | + .build/ + vendor/ + bin/ + +rules: + braces: + # Defaults + # min-spaces-inside: 0 + # max-spaces-inside: 0 + + # Keep 0 min-spaces to not error on empty {} collection definitions + min-spaces-inside: 0 + + # Allow one space inside braces to improve code readability + max-spaces-inside: 1 + + brackets: + # Defaults + # min-spaces-inside: 0 + # max-spaces-inside: 0 + + # Keep 0 min-spaces to not error on empty [] collection definitions + min-spaces-inside: 0 + + # Allow one space inside braces to improve code readability + max-spaces-inside: 1 + + colons: + # Defaults + # min-spaces-before: 0 + # max-spaces-after: 1 + + # Allow multiple spaces after a colon to allow indentation of YAML + # dictionary values + max-spaces-after: -1 + + commas: + # Defaults + # max-spaces-after: 1 + + # Allow multiple spaces after a comma to allow indentation of YAML + # dictionary values + max-spaces-after: -1 + + comments: + require-starting-space: true + min-spaces-from-content: 1 + + line-length: disable + +... diff --git a/composer.json b/composer.json index 73c71224..f07a894e 100644 --- a/composer.json +++ b/composer.json @@ -91,7 +91,7 @@ "sort-packages": true }, "scripts": { - "cs-check": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run", - "cs-fix": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php -vvv --using-cache=no" + "cs:diff": "php vendor/bin/php-cs-fixer fix --dry-run -v --diff", + "cs:fix": "php vendor/bin/php-cs-fixer fix -v" } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..f6c8dc47 --- /dev/null +++ b/composer.lock @@ -0,0 +1,4855 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "55588a179045985f5d6bc466b35f17c7", + "packages": [ + { + "name": "clue/stream-filter", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/clue/stream-filter.git", + "reference": "049509fef80032cb3f051595029ab75b49a3c2f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/stream-filter/zipball/049509fef80032cb3f051595029ab75b49a3c2f7", + "reference": "049509fef80032cb3f051595029ab75b49a3c2f7", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "Clue\\StreamFilter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "A simple and modern approach to stream filtering in PHP", + "homepage": "https://github.com/clue/stream-filter", + "keywords": [ + "bucket brigade", + "callback", + "filter", + "php_user_filter", + "stream", + "stream_filter_append", + "stream_filter_register" + ], + "support": { + "issues": "https://github.com/clue/stream-filter/issues", + "source": "https://github.com/clue/stream-filter/tree/v1.7.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2023-12-20T15:40:13+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/58c4c58cf23df7f498daeb97092e34f5259feb6a", + "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.0.4" + }, + "require-dev": { + "ergebnis/phpstan-rules": "^2.2.0", + "illuminate/console": "^11.0.0", + "laravel/pint": "^1.14.0", + "mockery/mockery": "^1.6.7", + "pestphp/pest": "^2.34.1", + "phpstan/phpstan": "^1.10.59", + "phpstan/phpstan-strict-rules": "^1.5.2", + "symfony/var-dumper": "^7.0.4", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-03-06T16:17:14+00:00" + }, + { + "name": "nyholm/psr7", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/aa5fc277a4f5508013d571341ade0c3886d4d00e", + "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2023-11-13T09:31:12+00:00" + }, + { + "name": "php-http/message", + "version": "1.16.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/message.git", + "reference": "5997f3289332c699fa2545c427826272498a2088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message/zipball/5997f3289332c699fa2545c427826272498a2088", + "reference": "5997f3289332c699fa2545c427826272498a2088", + "shasum": "" + }, + "require": { + "clue/stream-filter": "^1.5", + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.6", + "ext-zlib": "*", + "guzzlehttp/psr7": "^1.0 || ^2.0", + "laminas/laminas-diactoros": "^2.0 || ^3.0", + "php-http/message-factory": "^1.0.2", + "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", + "slim/slim": "^3.0" + }, + "suggest": { + "ext-zlib": "Used with compressor/decompressor streams", + "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", + "laminas/laminas-diactoros": "Used with Diactoros Factories", + "slim/slim": "Used with Slim Framework PSR-7 implementation" + }, + "type": "library", + "autoload": { + "files": [ + "src/filters.php" + ], + "psr-4": { + "Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "HTTP Message related tools", + "homepage": "http://php-http.org", + "keywords": [ + "http", + "message", + "psr-7" + ], + "support": { + "issues": "https://github.com/php-http/message/issues", + "source": "https://github.com/php-http/message/tree/1.16.1" + }, + "time": "2024-03-07T13:22:09+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "symfony/console", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "c981e0e9380ce9f146416bde3150c79197ce9986" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/c981e0e9380ce9f146416bde3150c79197ce9986", + "reference": "c981e0e9380ce9f146416bde3150c79197ce9986", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/e405b5424dc2528e02e31ba26b83a79fd4eb8f63", + "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "d1627b66fd87c8b4d90cabe5671c29d575690924" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d1627b66fd87c8b4d90cabe5671c29d575690924", + "reference": "d1627b66fd87c8b4d90cabe5671c29d575690924", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "yiisoft/injector", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/injector.git", + "reference": "0dc0127a7542341bdaabda7b85204e992938b83e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/injector/zipball/0dc0127a7542341bdaabda7b85204e992938b83e", + "reference": "0dc0127a7542341bdaabda7b85204e992938b83e", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "require-dev": { + "maglnet/composer-require-checker": "^3.8|^4.2", + "phpbench/phpbench": "^1.1", + "phpunit/phpunit": "^9.5", + "psr/container": "^1.0|^2.0", + "rector/rector": "^0.18.12", + "roave/infection-static-analysis-plugin": "^1.16", + "spatie/phpunit-watcher": "^1.23", + "vimeo/psalm": "^4.30|^5.7", + "yiisoft/test-support": "^1.2" + }, + "suggest": { + "psr/container": "For automatic resolving of dependencies" + }, + "type": "library", + "autoload": { + "psr-4": { + "Yiisoft\\Injector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR-11 compatible injector. Executes a callable and makes an instances by injecting dependencies from a given DI container.", + "homepage": "https://www.yiiframework.com/", + "keywords": [ + "PSR-11", + "dependency injection", + "di", + "injector", + "reflection" + ], + "support": { + "chat": "https://t.me/yii3en", + "forum": "https://www.yiiframework.com/forum/", + "irc": "irc://irc.freenode.net/yii", + "issues": "https://github.com/yiisoft/injector/issues?state=open", + "source": "https://github.com/yiisoft/injector", + "wiki": "https://www.yiiframework.com/wiki/" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + } + ], + "time": "2023-12-20T09:39:03+00:00" + } + ], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "react/promise": "^2", + "vimeo/psalm": "^3.12" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T18:52:26+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-13T18:00:56+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.3", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-03-19T10:26:25+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-31T09:50:34+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "dereuromark/composer-prefer-lowest", + "version": "0.1.10", + "source": { + "type": "git", + "url": "https://github.com/dereuromark/composer-prefer-lowest.git", + "reference": "9290203cfc25d4a2d0605cb3119bb227aa1195d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dereuromark/composer-prefer-lowest/zipball/9290203cfc25d4a2d0605cb3119bb227aa1195d6", + "reference": "9290203cfc25d4a2d0605cb3119bb227aa1195d6", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "ext-json": "*", + "php": ">=7.3" + }, + "require-dev": { + "fig-r/psr2r-sniffer": "dev-master", + "phpunit/phpunit": "^9.5" + }, + "bin": [ + "bin/validate-prefer-lowest" + ], + "type": "library", + "autoload": { + "psr-4": { + "ComposerPreferLowest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Scherer", + "email": "euromark@web.de" + } + ], + "description": "Checks prefer-lowest more strictly. Add-on for CI.", + "support": { + "issues": "https://github.com/dereuromark/composer-prefer-lowest/issues", + "source": "https://github.com/dereuromark/composer-prefer-lowest/tree/0.1.10" + }, + "time": "2021-11-02T02:20:22+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.2", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + }, + "time": "2022-03-02T22:36:06+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-02-07T09:43:46+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.56.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "69c6168ae8bc96dc656c7f6c7271120a68ae5903" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/69c6168ae8bc96dc656c7f6c7271120a68ae5903", + "reference": "69c6168ae8bc96dc656c7f6c7271120a68ae5903", + "shasum": "" + }, + "require": { + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3 || ^2.0", + "infection/infection": "^0.27.11", + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", + "phpunit/phpunit": "^9.6 || ^10.5.5 || ^11.0.2", + "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.56.1" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2024-05-10T11:31:15+00:00" + }, + { + "name": "google/protobuf", + "version": "v3.25.3", + "source": { + "type": "git", + "url": "https://github.com/protocolbuffers/protobuf-php.git", + "reference": "983a87f4f8798a90ca3a25b0f300b8fda38df643" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/983a87f4f8798a90ca3a25b0f300b8fda38df643", + "reference": "983a87f4f8798a90ca3a25b0f300b8fda38df643", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": ">=5.0.0" + }, + "suggest": { + "ext-bcmath": "Need to support JSON deserialization" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Protobuf\\": "src/Google/Protobuf", + "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "proto library for PHP", + "homepage": "https://developers.google.com/protocol-buffers/", + "keywords": [ + "proto" + ], + "support": { + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v3.25.3" + }, + "time": "2024-02-15T21:11:49+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.4.1", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" + }, + "time": "2024-01-31T06:18:54+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.19.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.1" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + }, + "time": "2024-03-17T08:10:35+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "298d2febfe79d03fe714eb871d5538da55205b1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/298d2febfe79d03fe714eb871d5538da55205b1a", + "reference": "298d2febfe79d03fe714eb871d5538da55205b1a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.0" + }, + "time": "2024-04-09T21:13:58+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.29.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc", + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0" + }, + "time": "2024-05-06T12:04:23+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.14", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-12T15:33:41+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3", + "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.5", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-04-24T06:32:35+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "roxblnfk/unpoly", + "version": "1.8.3", + "source": { + "type": "git", + "url": "https://github.com/roxblnfk/unpoly.git", + "reference": "eca190338be6e19b094ed533b1271949dab0b450" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/roxblnfk/unpoly/zipball/eca190338be6e19b094ed533b1271949dab0b450", + "reference": "eca190338be6e19b094ed533b1271949dab0b450", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "replace": { + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*", + "symfony/polyfill-php74": "*", + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*", + "symfony/polyfill-php82": "*", + "symfony/polyfill-php83": "*" + }, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aleksei Gagarin (roxblnfk)", + "homepage": "https://github.com/roxblnfk" + } + ], + "description": "Remove unnecessary PHP polyfills", + "keywords": [ + "cleaning", + "php", + "polyfill", + "polyfill-php" + ], + "support": { + "issues": "https://github.com/roxblnfk/unpoly/issues", + "source": "https://github.com/roxblnfk/unpoly/tree/1.8.3" + }, + "funding": [ + { + "url": "https://patreon.com/roxblnfk", + "type": "patreon" + } + ], + "time": "2024-02-06T20:29:06+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-14T13:18:12+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f56b220fe2db1ade4c88098d83413ebdfc3bf876", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.3.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-05-01T10:20:27+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/db2a7fab994d67d92356bb39c367db115d9d30f9", + "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/cc168be6fbdcdf3401f50ae863ee3818ed4338f5", + "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/4d58f0f4fe95a30d7b538d71197135483560b97c", + "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-28T11:44:19+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/23cc173858776ad451e31f053b1c9f47840b2cfa", + "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "symfony/process", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/3839e56b94dd1dbd13235d27504e66baf23faba0", + "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/41a7a24aa1dc82adf46a06bc292d1923acfe6b84", + "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "vimeo/psalm", + "version": "5.24.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "462c80e31c34e58cc4f750c656be3927e80e550e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/462c80e31c34e58cc4f750c656be3927e80e550e", + "reference": "462c80e31c34e58cc4f750c656be3927e80e550e", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.16", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "nikic/php-parser": "4.17.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^2.0", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" + }, + "time": "2024-05-01T19:32:08+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.1", + "ext-sockets": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} From a7d40732fee2d8872a2cd39f88cd3f2e8d40e671 Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 12:38:58 +0300 Subject: [PATCH 050/106] ci: use lowest platform version --- composer.json | 6 ++ composer.lock | 256 +++++++++++++++++++++++++------------------------- 2 files changed, 136 insertions(+), 126 deletions(-) diff --git a/composer.json b/composer.json index f07a894e..7c6f3a76 100644 --- a/composer.json +++ b/composer.json @@ -88,6 +88,12 @@ "allow-plugins": { "ergebnis/composer-normalize": true }, + "audit": { + "abandoned": "report" + }, + "platform": { + "php": "8.1.27" + }, "sort-packages": true }, "scripts": { diff --git a/composer.lock b/composer.lock index f6c8dc47..3765c756 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "55588a179045985f5d6bc466b35f17c7", + "content-hash": "1b45d0065dda2e44626f6f9655b77db6", "packages": [ { "name": "clue/stream-filter", @@ -74,32 +74,33 @@ }, { "name": "nunomaduro/termwind", - "version": "v2.0.1", + "version": "v1.15.1", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a" + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/58c4c58cf23df7f498daeb97092e34f5259feb6a", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": "^8.2", - "symfony/console": "^7.0.4" + "php": "^8.0", + "symfony/console": "^5.3.0|^6.0.0" }, "require-dev": { - "ergebnis/phpstan-rules": "^2.2.0", - "illuminate/console": "^11.0.0", - "laravel/pint": "^1.14.0", - "mockery/mockery": "^1.6.7", - "pestphp/pest": "^2.34.1", - "phpstan/phpstan": "^1.10.59", - "phpstan/phpstan-strict-rules": "^1.5.2", - "symfony/var-dumper": "^7.0.4", + "ergebnis/phpstan-rules": "^1.0.", + "illuminate/console": "^8.0|^9.0", + "illuminate/support": "^8.0|^9.0", + "laravel/pint": "^1.0.0", + "pestphp/pest": "^1.21.0", + "pestphp/pest-plugin-mock": "^1.0", + "phpstan/phpstan": "^1.4.6", + "phpstan/phpstan-strict-rules": "^1.1.0", + "symfony/var-dumper": "^5.2.7|^6.0.0", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -108,9 +109,6 @@ "providers": [ "Termwind\\Laravel\\TermwindServiceProvider" ] - }, - "branch-alias": { - "dev-2.x": "2.x-dev" } }, "autoload": { @@ -142,7 +140,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.0.1" + "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" }, "funding": [ { @@ -158,7 +156,7 @@ "type": "github" } ], - "time": "2024-03-06T16:17:14+00:00" + "time": "2023-02-08T01:06:31+00:00" }, { "name": "nyholm/psr7", @@ -470,46 +468,47 @@ }, { "name": "symfony/console", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c981e0e9380ce9f146416bde3150c79197ce9986" + "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c981e0e9380ce9f146416bde3150c79197ce9986", - "reference": "c981e0e9380ce9f146416bde3150c79197ce9986", + "url": "https://api.github.com/repos/symfony/console/zipball/a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", + "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -543,7 +542,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.0.7" + "source": "https://github.com/symfony/console/tree/v6.4.7" }, "funding": [ { @@ -559,7 +558,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1031,20 +1030,20 @@ }, { "name": "symfony/string", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63" + "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e405b5424dc2528e02e31ba26b83a79fd4eb8f63", - "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63", + "url": "https://api.github.com/repos/symfony/string/zipball/ffeb9591c61f65a68d47f77d12b83fa530227a69", + "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -1054,11 +1053,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -1097,7 +1096,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.7" + "source": "https://github.com/symfony/string/tree/v6.4.7" }, "funding": [ { @@ -1113,36 +1112,38 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "d1627b66fd87c8b4d90cabe5671c29d575690924" + "reference": "7a9cd977cd1c5fed3694bee52990866432af07d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d1627b66fd87c8b4d90cabe5671c29d575690924", - "reference": "d1627b66fd87c8b4d90cabe5671c29d575690924", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7a9cd977cd1c5fed3694bee52990866432af07d7", + "reference": "7a9cd977cd1c5fed3694bee52990866432af07d7", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<5.4" }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4" }, "bin": [ "Resources/bin/var-dump-server" @@ -1180,7 +1181,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.0.7" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.7" }, "funding": [ { @@ -1196,7 +1197,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "yiisoft/injector", @@ -3110,29 +3111,29 @@ }, { "name": "roxblnfk/unpoly", - "version": "1.8.3", + "version": "1.8.1.1", "source": { "type": "git", "url": "https://github.com/roxblnfk/unpoly.git", - "reference": "eca190338be6e19b094ed533b1271949dab0b450" + "reference": "ec84dc56c97320acde5ab8875a39de36fdce36a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/roxblnfk/unpoly/zipball/eca190338be6e19b094ed533b1271949dab0b450", - "reference": "eca190338be6e19b094ed533b1271949dab0b450", + "url": "https://api.github.com/repos/roxblnfk/unpoly/zipball/ec84dc56c97320acde5ab8875a39de36fdce36a9", + "reference": "ec84dc56c97320acde5ab8875a39de36fdce36a9", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.1" }, "replace": { + "symfony/polyfill-php70": "*", + "symfony/polyfill-php71": "*", "symfony/polyfill-php72": "*", "symfony/polyfill-php73": "*", "symfony/polyfill-php74": "*", "symfony/polyfill-php80": "*", - "symfony/polyfill-php81": "*", - "symfony/polyfill-php82": "*", - "symfony/polyfill-php83": "*" + "symfony/polyfill-php81": "*" }, "type": "metapackage", "notification-url": "https://packagist.org/downloads/", @@ -3154,7 +3155,7 @@ ], "support": { "issues": "https://github.com/roxblnfk/unpoly/issues", - "source": "https://github.com/roxblnfk/unpoly/tree/1.8.3" + "source": "https://github.com/roxblnfk/unpoly/tree/1.8.1.1" }, "funding": [ { @@ -3162,7 +3163,7 @@ "type": "patreon" } ], - "time": "2024-02-06T20:29:06+00:00" + "time": "2024-02-06T20:26:09+00:00" }, { "name": "sebastian/cli-parser", @@ -4150,24 +4151,24 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9" + "reference": "d84384f3f67de3cb650db64d685d70395dacfc3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/db2a7fab994d67d92356bb39c367db115d9d30f9", - "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d84384f3f67de3cb650db64d685d70395dacfc3f", + "reference": "d84384f3f67de3cb650db64d685d70395dacfc3f", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/dependency-injection": "<5.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -4176,13 +4177,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4210,7 +4211,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.7" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.7" }, "funding": [ { @@ -4226,7 +4227,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4306,23 +4307,23 @@ }, { "name": "symfony/filesystem", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5" + "reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/cc168be6fbdcdf3401f50ae863ee3818ed4338f5", - "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/78dde75f8f6dbbca4ec436a4b0087f7af02076d4", + "reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8", - "symfony/process": "^6.4|^7.0" + "symfony/process": "^5.4|^6.4" }, "type": "library", "autoload": { @@ -4350,7 +4351,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.0.7" + "source": "https://github.com/symfony/filesystem/tree/v6.4.7" }, "funding": [ { @@ -4366,27 +4367,27 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "symfony/finder", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c" + "reference": "511c48990be17358c23bf45c5d71ab85d40fb764" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/4d58f0f4fe95a30d7b538d71197135483560b97c", - "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c", + "url": "https://api.github.com/repos/symfony/finder/zipball/511c48990be17358c23bf45c5d71ab85d40fb764", + "reference": "511c48990be17358c23bf45c5d71ab85d40fb764", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -4414,7 +4415,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.0.7" + "source": "https://github.com/symfony/finder/tree/v6.4.7" }, "funding": [ { @@ -4430,24 +4431,24 @@ "type": "tidelift" } ], - "time": "2024-04-28T11:44:19+00:00" + "time": "2024-04-23T10:36:43+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa" + "reference": "9a3c92b490716ba6771f5beced13c6eda7183eed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/23cc173858776ad451e31f053b1c9f47840b2cfa", - "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9a3c92b490716ba6771f5beced13c6eda7183eed", + "reference": "9a3c92b490716ba6771f5beced13c6eda7183eed", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -4481,7 +4482,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.0.7" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.7" }, "funding": [ { @@ -4497,24 +4498,24 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "symfony/process", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0" + "reference": "cdb1c81c145fd5aa9b0038bab694035020943381" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3839e56b94dd1dbd13235d27504e66baf23faba0", - "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0", + "url": "https://api.github.com/repos/symfony/process/zipball/cdb1c81c145fd5aa9b0038bab694035020943381", + "reference": "cdb1c81c145fd5aa9b0038bab694035020943381", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -4542,7 +4543,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.0.7" + "source": "https://github.com/symfony/process/tree/v6.4.7" }, "funding": [ { @@ -4558,24 +4559,24 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "symfony/stopwatch", - "version": "v7.0.7", + "version": "v6.4.7", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84" + "reference": "ffec95ba269e541eb2232126c0c20f83086b5c68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/41a7a24aa1dc82adf46a06bc292d1923acfe6b84", - "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/ffec95ba269e541eb2232126c0c20f83086b5c68", + "reference": "ffec95ba269e541eb2232126c0c20f83086b5c68", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -4604,7 +4605,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.0.7" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.7" }, "funding": [ { @@ -4620,7 +4621,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:22:46+00:00" }, { "name": "theseer/tokenizer", @@ -4851,5 +4852,8 @@ "ext-sockets": "*" }, "platform-dev": [], + "platform-overrides": { + "php": "8.1.27" + }, "plugin-api-version": "2.6.0" } From 47f4cc8548bcd660deeb792476ab0138b8eeff62 Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 12:49:02 +0300 Subject: [PATCH 051/106] ci: use cs:fix instead of cs:diff --- .github/workflows/lint-php-files.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-php-files.yml b/.github/workflows/lint-php-files.yml index eb2c5a76..51053b39 100644 --- a/.github/workflows/lint-php-files.yml +++ b/.github/workflows/lint-php-files.yml @@ -71,7 +71,7 @@ jobs: run: make prepare - name: 🚨 Run coding standards task - run: composer cs:diff + run: composer cs:fix env: PHP_CS_FIXER_IGNORE_ENV: true From 29f776daac943588836c957dd9cdf33252e86649 Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 13:33:03 +0300 Subject: [PATCH 052/106] ci: refactor security workflow --- .github/workflows/lint-php-files.yml | 7 ++- .github/workflows/security.yml | 89 +++++++++++++--------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/lint-php-files.yml b/.github/workflows/lint-php-files.yml index 51053b39..1f625a6c 100644 --- a/.github/workflows/lint-php-files.yml +++ b/.github/workflows/lint-php-files.yml @@ -18,10 +18,15 @@ name: 🧹 Fix PHP coding standards jobs: coding-standards: - runs-on: ubuntu-latest timeout-minutes: 4 + runs-on: ${{ matrix.os }} + concurrency: + cancel-in-progress: true + group: coding-standards-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} strategy: matrix: + os: + - ubuntu-latest php-version: - '8.2' dependencies: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 3adb5889..ad220aaf 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -1,65 +1,60 @@ -name: Security +name: 🔐 Security analysis on: pull_request: - paths-ignore: - - 'docs/**' - - 'bin/**' - - 'resources/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' - - 'psalm.xml' - push: - paths-ignore: - - 'docs/**' - - 'bin/**' - - 'resources/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' - - 'psalm.xml' jobs: - security: - name: Security Checks (PHP ${{ matrix.php }}, OS ${{ matrix.os }}) + security-analysis: + timeout-minutes: 4 runs-on: ${{ matrix.os }} + concurrency: + cancel-in-progress: true + group: security-analysis-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} strategy: - fail-fast: false + fail-fast: true matrix: - # Note: This workflow requires only the LATEST version of PHP - php: [ 8.2 ] - os: [ ubuntu-latest ] + os: + - ubuntu-latest + php-version: + - '8.2' + dependencies: + - locked steps: - - name: Set up PHP ${{ matrix.php }} - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: dom, sockets, grpc, curl + - name: 📦 Check out the codebase + uses: actions/checkout@v4.1.4 - - name: Check Out Code - uses: actions/checkout@v4 + - name: 🛠️ Setup PHP + uses: shivammathur/setup-php@2.30.4 with: - fetch-depth: 1 + php-version: ${{ matrix.php-version }} + extensions: none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets + ini-values: error_reporting=E_ALL + coverage: none + + - name: 🛠️ Setup problem matchers + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" - - name: Get Composer Cache Directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: 🤖 Validate composer.json and composer.lock + run: composer validate --ansi --strict - - name: Cache Dependencies - uses: actions/cache@v3 + - name: 🔍 Get composer cache directory + uses: wayofdev/gh-actions/actions/composer/get-cache-directory@v3.0.0 + + - name: ♻️ Restore cached dependencies installed with composer + uses: actions/cache@v4.0.2 with: - path: ${{ steps.composer-cache.outputs.dir }} - key: php-${{ matrix.php }}-${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: php-${{ matrix.php }}-${{ runner.os }}-composer- + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('composer.lock') }} + restore-keys: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- - - name: Install Composer Dependencies - run: composer install --prefer-dist --no-interaction + - name: 📥 Install "${{ matrix.dependencies }}" dependencies + uses: wayofdev/gh-actions/actions/composer/install@v3.0.0 + with: + dependencies: ${{ matrix.dependencies }} - - name: Verify + - name: 🐛 Check installed packages using roave/security-advisories run: composer require --dev roave/security-advisories:dev-latest + + - name: 🐛 Check installed packages for security vulnerability advisories + run: composer audit --ansi From 2bb9ab88e087948b82285e6a37a3d9fcd372e63b Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 14:13:55 +0300 Subject: [PATCH 053/106] style: fix yaml errors --- .github/workflows/build-release.yml | 2 +- .github/workflows/security.yml | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 6b3aeeab..82ad3bbf 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -91,7 +91,7 @@ jobs: run: rm ${{ env.GPG_KEYS }} - name: 🏷️ Determine tag - run: "echo \"RELEASE_TAG=${GITHUB_REF#refs/tags/}\" >> $GITHUB_ENV" + run: 'echo RELEASE_TAG="${GITHUB_REF#refs/tags/}" >> "$GITHUB_ENV"' - name: 📤 Upload release assets uses: actions/github-script@v7.0.1 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index ad220aaf..fe03d7b6 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -1,6 +1,8 @@ +--- + name: 🔐 Security analysis -on: +on: # yamllint disable-line rule:truthy pull_request: push: @@ -22,7 +24,7 @@ jobs: - locked steps: - name: 📦 Check out the codebase - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v4.1.5 - name: 🛠️ Setup PHP uses: shivammathur/setup-php@2.30.4 @@ -39,7 +41,7 @@ jobs: run: composer validate --ansi --strict - name: 🔍 Get composer cache directory - uses: wayofdev/gh-actions/actions/composer/get-cache-directory@v3.0.0 + uses: wayofdev/gh-actions/actions/composer/get-cache-directory@v3.1.0 - name: ♻️ Restore cached dependencies installed with composer uses: actions/cache@v4.0.2 @@ -49,7 +51,7 @@ jobs: restore-keys: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- - name: 📥 Install "${{ matrix.dependencies }}" dependencies - uses: wayofdev/gh-actions/actions/composer/install@v3.0.0 + uses: wayofdev/gh-actions/actions/composer/install@v3.1.0 with: dependencies: ${{ matrix.dependencies }} From 1b3963e2f6832e97336a92510ef8660ec763f807 Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 14:14:11 +0300 Subject: [PATCH 054/106] ci: refactor static analysis workflow --- .github/workflows/psalm.yml | 65 ----- .github/workflows/static-analysis.yml | 69 +++++ composer.json | 14 +- psalm-baseline.xml | 372 +++++--------------------- psalm.xml | 1 - 5 files changed, 143 insertions(+), 378 deletions(-) delete mode 100644 .github/workflows/psalm.yml create mode 100644 .github/workflows/static-analysis.yml diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml deleted file mode 100644 index d2b754ee..00000000 --- a/.github/workflows/psalm.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Psalm - -on: - pull_request: - paths-ignore: - - 'docs/**' - - 'bin/**' - - 'resources/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' - - 'psalm.xml' - - push: - paths-ignore: - - 'docs/**' - - 'bin/**' - - 'resources/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' - - 'psalm.xml' - -jobs: - psalm: - name: Psalm Validation (PHP ${{ matrix.php }}, OS ${{ matrix.os }}) - runs-on: ${{ matrix.os }} - continue-on-error: true - strategy: - fail-fast: false - matrix: - php: [8.2] - os: [ubuntu-latest] - steps: - - name: Set up PHP ${{ matrix.php }} - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: dom - - - name: Check Out Code - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Get Composer Cache Directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache Dependencies - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: php-${{ matrix.php }}-${{ runner.os }}-composer- - - - name: Install Composer Dependencies - run: composer install --prefer-dist --no-interaction - - - name: Run Tests - run: vendor/bin/psalm diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 00000000..06c28e03 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,69 @@ +--- + +on: # yamllint disable-line rule:truthy + pull_request: + paths: + - 'src/**' + - 'tests/**' + - 'bin/trap' + - '.php-cs-fixer.php.dist' + push: + paths: + - 'src/**' + - 'tests/**' + - 'bin/trap' + - '.php-cs-fixer.php.dist' + +name: 🔍 Static analysis + +jobs: + static-analysis: + timeout-minutes: 4 + runs-on: ${{ matrix.os }} + concurrency: + cancel-in-progress: true + group: static-analysis-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + strategy: + fail-fast: true + matrix: + os: + - ubuntu-latest + php-version: + - '8.2' + dependencies: + - locked + steps: + - name: 📦 Check out the codebase + uses: actions/checkout@v4.1.5 + + - name: 🛠️ Setup PHP + uses: shivammathur/setup-php@2.30.4 + with: + php-version: ${{ matrix.php-version }} + extensions: none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets + ini-values: error_reporting=E_ALL + coverage: none + + - name: 🛠️ Setup problem matchers + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: 🤖 Validate composer.json and composer.lock + run: composer validate --ansi --strict + + - name: 🔍 Get composer cache directory + uses: wayofdev/gh-actions/actions/composer/get-cache-directory@v3.1.0 + + - name: ♻️ Restore cached dependencies installed with composer + uses: actions/cache@v4.0.2 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('composer.lock') }} + restore-keys: php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- + + - name: 📥 Install "${{ matrix.dependencies }}" dependencies + uses: wayofdev/gh-actions/actions/composer/install@v3.1.0 + with: + dependencies: ${{ matrix.dependencies }} + + - name: 🔍 Run static analysis using psalm + run: composer psalm:ci diff --git a/composer.json b/composer.json index 7c6f3a76..429e0894 100644 --- a/composer.json +++ b/composer.json @@ -98,6 +98,18 @@ }, "scripts": { "cs:diff": "php vendor/bin/php-cs-fixer fix --dry-run -v --diff", - "cs:fix": "php vendor/bin/php-cs-fixer fix -v" + "cs:fix": "php vendor/bin/php-cs-fixer fix -v", + "infect": "XDEBUG_MODE=coverage php vendor/bin/roave-infection-static-analysis-plugin --configuration=infection.json.dist", + "infect:ci": "XDEBUG_MODE=coverage php vendor/bin/roave-infection-static-analysis-plugin --ansi --configuration=infection.json.dist --logger-github --ignore-msi-with-no-mutations --only-covered", + "psalm": "php vendor/bin/psalm --show-info=true", + "psalm:baseline": "php vendor/bin/psalm --set-baseline=psalm-baseline.xml", + "psalm:ci": "php vendor/bin/psalm --output-format=github --shepherd --show-info=false --stats --threads=4", + "refactor": "php vendor/bin/rector process --config=rector.php", + "refactor:ci": "php vendor/bin/rector process --config=rector.php --dry-run --ansi", + "stan": "php vendor/bin/phpstan analyse --memory-limit=2G", + "stan:baseline": "php vendor/bin/phpstan analyse --generate-baseline --memory-limit=2G --allow-empty-baseline", + "stan:ci": "php vendor/bin/phpstan analyse --memory-limit=2G --error-format=github", + "test": "XDEBUG_MODE=coverage php vendor/bin/phpunit", + "test:cc": "XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage --coverage-clover=.build/phpunit/logs/clover.xml" } } diff --git a/psalm-baseline.xml b/psalm-baseline.xml index f796ef26..d6240690 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,26 +1,26 @@ - + - $offset - $currentPos + - $headers - $itemHeader + + - $headers - $itemHeader - $type + + + - TReturn + method]]> @@ -31,28 +31,21 @@ - $values - $values - $values - $values + + + + - stream->getSize()]]> + stream->getSize()]]> - int + - - request->getBody()->getSize() + \array_reduce( - $this->request->getUploadedFiles(), - static fn(int $carry, array $file): int => $carry + $file['size'], - 0 - )]]> - @@ -79,44 +72,21 @@ - $payload + - - int - - - - - - - request->getUploadedFiles()]]> - array + - - int - - - request->getBody()->getSize()]]> - - \json_decode($payload, true, JSON_THROW_ON_ERROR) + - - - $payload - - - $payload - - - $payload + @@ -124,13 +94,10 @@ - Files::normalizeSize($size) + - - $frame - getCookieParams()]]> getQueryParams()]]> @@ -144,44 +111,40 @@ - $file - $file - $function + + + $output->writeln(]]> + - - $row - $stacktrace + + - $file - $file - $function + + + - renderMessageHeader + - - - - $message - $meta - $tags - - - + + + + + @@ -192,301 +155,88 @@ payload]]> - $type payload['exceptions']]]> - $type + - - $type - - - - - getHeaders()]]> - getHeaders()]]> - - - getClientFilename()]]> - getClientMediaType()]]> - - - - - render - - - - - - - - $context - $controller - $data - $fileLink - - - - + + - - $meta - - $context - - $data + + - $controller - $describer - $fileLink + + + - $payload - [$data, $context] + + - describe + - - dumper->dump($controller, true)]]> - dumper->dump($data, true)]]> - - - $item - - - - \array_keys($data) - - $item + - - - $size - - - - - - - $key - - $value + - - - Termwind::renderUsing($output) - new HtmlRenderer() - - - Termwind::renderUsing($output) - - - new SplQueue() + - - - payloadSize]]> - - - \Closure::bind($callable(...), $this) - \Closure::bind($callable(...), $this) - - - - - socket]]> - socket]]> - - - new \SplQueue() + getClass()]]> - $value - $value + + getName()]]]> getName()]]]> - $value + getName()]]]> - getDescriptorByClassName + - - - - - - - - $value - $values - - - headers[$header]]]> - - - headers[$header]]]> - headers[$new->headerNames[$normalized]]]]> - headers[$header]]]> - headers[$header]]]> - headers[$header]]]> - - - headers[$header]]]> - - - $header - $header - $header - - - string[] - - - - - FieldDataArray - $this->value, - ]]]> - - - - - FileDataArray - $this->fileName, - 'size' => $this->getSize(), - ]]]> - - - - - new File($headers, $name, $fileName), - false => new Field($headers, $name), - }]]> - - - $headers - $headers - $headers - - - static - - - - - $addrs - protocol['BCC'] ?? []]]> - - - protocol]]> - - - $attach - - - $message - - - $attach - $message - - - >]]> - - - $attachments - $messages - - - - - $boundary - $headersBlock - read($blockEnd - $pos + 2)]]> - - - $parts - $result - - - >]]> - array{0: non-empty-string, 1: non-empty-string, 2: non-empty-string} - - - getSize()]]> - - - $value - $value - - - - - $boundary - $headerBlock - - - AbstractCloner::$defaultCasters[EnumValue::class] - AbstractCloner::$defaultCasters[MapField::class] - AbstractCloner::$defaultCasters[Message::class] - AbstractCloner::$defaultCasters[RepeatedField::class] + + + + - AbstractCloner::$defaultCasters[EnumValue::class] - AbstractCloner::$defaultCasters[MapField::class] - AbstractCloner::$defaultCasters[Message::class] - AbstractCloner::$defaultCasters[RepeatedField::class] + + + + - - $value - AbstractCloner::$defaultCasters[EnumValue::class] - AbstractCloner::$defaultCasters[MapField::class] - AbstractCloner::$defaultCasters[Message::class] - AbstractCloner::$defaultCasters[RepeatedField::class] - diff --git a/psalm.xml b/psalm.xml index 1e24d848..a6e9f8b6 100644 --- a/psalm.xml +++ b/psalm.xml @@ -3,7 +3,6 @@ xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" errorLevel="1" - resolveFromConfigFile="true" findUnusedBaselineEntry="false" findUnusedCode="false" errorBaseline="psalm-baseline.xml" From c1579b529e59cc5cb23b5e5fa8edb07854b4cb6d Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 14:16:07 +0300 Subject: [PATCH 055/106] ci: add opcache extension for psalm workflow --- .github/workflows/static-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 06c28e03..d25e8271 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -40,7 +40,7 @@ jobs: uses: shivammathur/setup-php@2.30.4 with: php-version: ${{ matrix.php-version }} - extensions: none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets + extensions: none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets, opcache ini-values: error_reporting=E_ALL coverage: none From 9c004f03ca8c07f944aee3accf7a638048ba5f6c Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 14:18:00 +0300 Subject: [PATCH 056/106] ci: add posix and pctnl extension for psalm workflow --- .github/workflows/static-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index d25e8271..2f61f0f1 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -40,7 +40,7 @@ jobs: uses: shivammathur/setup-php@2.30.4 with: php-version: ${{ matrix.php-version }} - extensions: none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets, opcache + extensions: none, ctype, curl, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, sockets, opcache, pcntl, posix ini-values: error_reporting=E_ALL coverage: none From 852ab889060b96bf834cc1c4736bf54d609fe61e Mon Sep 17 00:00:00 2001 From: lotyp Date: Sun, 12 May 2024 14:30:14 +0300 Subject: [PATCH 057/106] ci(php-cs-fixer): set deault name for config --- .github/workflows/lint-php-files.yml | 4 ++-- .github/workflows/static-analysis.yml | 4 ++-- .php-cs-fixer.php.dist => .php-cs-fixer.dist.php | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename .php-cs-fixer.php.dist => .php-cs-fixer.dist.php (100%) diff --git a/.github/workflows/lint-php-files.yml b/.github/workflows/lint-php-files.yml index 1f625a6c..e00b1897 100644 --- a/.github/workflows/lint-php-files.yml +++ b/.github/workflows/lint-php-files.yml @@ -6,13 +6,13 @@ on: # yamllint disable-line rule:truthy - 'src/**' - 'tests/**' - 'bin/trap' - - '.php-cs-fixer.php.dist' + - '.php-cs-fixer.dist.php' push: paths: - 'src/**' - 'tests/**' - 'bin/trap' - - '.php-cs-fixer.php.dist' + - '.php-cs-fixer.dist.php' name: 🧹 Fix PHP coding standards diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 2f61f0f1..9a505e34 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -6,13 +6,13 @@ on: # yamllint disable-line rule:truthy - 'src/**' - 'tests/**' - 'bin/trap' - - '.php-cs-fixer.php.dist' + - '.php-cs-fixer.dist.php' push: paths: - 'src/**' - 'tests/**' - 'bin/trap' - - '.php-cs-fixer.php.dist' + - '.php-cs-fixer.dist.php' name: 🔍 Static analysis diff --git a/.php-cs-fixer.php.dist b/.php-cs-fixer.dist.php similarity index 100% rename from .php-cs-fixer.php.dist rename to .php-cs-fixer.dist.php From 2d963afb81e1be2fd498b7817a24e2660447c839 Mon Sep 17 00:00:00 2001 From: Andrij Orlenko <94047334+lotyp@users.noreply.github.com> Date: Sun, 12 May 2024 23:53:20 +0300 Subject: [PATCH 058/106] chore: update lint-php-files.yml Co-authored-by: Aleksei Gagarin --- .github/workflows/lint-php-files.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint-php-files.yml b/.github/workflows/lint-php-files.yml index e00b1897..b2e5f4e8 100644 --- a/.github/workflows/lint-php-files.yml +++ b/.github/workflows/lint-php-files.yml @@ -85,5 +85,6 @@ jobs: with: commit_message: 'style(php-cs-fixer): lint php files and fix coding standards' branch: ${{ github.head_ref }} + commit_author: "github-actions " env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 5febbc2bfe9b8cb15bfa5971f43426e37b5e2278 Mon Sep 17 00:00:00 2001 From: lotyp Date: Mon, 13 May 2024 09:54:22 +0300 Subject: [PATCH 059/106] chore: export-ignore local development files --- .gitattributes | 23 +++++++++++++++-------- .github/workflows/build-release.yml | 3 --- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.gitattributes b/.gitattributes index 009223be..26864667 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,12 +1,19 @@ * text=auto -/.github export-ignore -/tests export-ignore -/.* export-ignore -/phpunit.xml* export-ignore -/phpstan.* export-ignore -/psalm.* export-ignore -/infection.* export-ignore -/codecov.* export-ignore +.github export-ignore +.phive export-ignore +phar export-ignore +tests export-ignore +.* export-ignore +box.json.dist export-ignore +composer.lock export-ignore +docker-compose.yaml export-ignore +Makefile export-ignore +phpunit.xml* export-ignore +phpstan.* export-ignore +psalm.* export-ignore +psalm-baseline.xml export-ignore +infection.* export-ignore +codecov.* export-ignore *.http binary diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 82ad3bbf..06c6b089 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -90,9 +90,6 @@ jobs: - name: ❎ Remove decrypted keys.asc run: rm ${{ env.GPG_KEYS }} - - name: 🏷️ Determine tag - run: 'echo RELEASE_TAG="${GITHUB_REF#refs/tags/}" >> "$GITHUB_ENV"' - - name: 📤 Upload release assets uses: actions/github-script@v7.0.1 with: From a8ba929fb6432f6b21b48e79667f7dffcd1301de Mon Sep 17 00:00:00 2001 From: lotyp Date: Mon, 13 May 2024 09:55:39 +0300 Subject: [PATCH 060/106] chore: use single quotes --- .github/workflows/lint-php-files.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-php-files.yml b/.github/workflows/lint-php-files.yml index b2e5f4e8..5f45d0af 100644 --- a/.github/workflows/lint-php-files.yml +++ b/.github/workflows/lint-php-files.yml @@ -85,6 +85,6 @@ jobs: with: commit_message: 'style(php-cs-fixer): lint php files and fix coding standards' branch: ${{ github.head_ref }} - commit_author: "github-actions " + commit_author: 'github-actions ' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 6bfd6240f1d0b2b48d4d62af6250edb2b4ca4d82 Mon Sep 17 00:00:00 2001 From: lotyp Date: Mon, 13 May 2024 10:10:24 +0300 Subject: [PATCH 061/106] chore: remove useless roave security check install in CI --- .github/workflows/security.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index fe03d7b6..7c92c9d6 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -55,8 +55,5 @@ jobs: with: dependencies: ${{ matrix.dependencies }} - - name: 🐛 Check installed packages using roave/security-advisories - run: composer require --dev roave/security-advisories:dev-latest - - name: 🐛 Check installed packages for security vulnerability advisories run: composer audit --ansi From 340c5eb941bc8b805db1cb39cb2bde7259a42911 Mon Sep 17 00:00:00 2001 From: lotyp Date: Mon, 13 May 2024 16:37:10 +0300 Subject: [PATCH 062/106] feat: add support for pest-php --- composer.json | 14 +- composer.lock | 710 ++++++++++++++++++++++++++++++++++++++++++++++++- tests/Pest.php | 9 + 3 files changed, 721 insertions(+), 12 deletions(-) create mode 100644 tests/Pest.php diff --git a/composer.json b/composer.json index 429e0894..0bf9a5dd 100644 --- a/composer.json +++ b/composer.json @@ -58,9 +58,11 @@ "dereuromark/composer-prefer-lowest": "^0.1.10", "friendsofphp/php-cs-fixer": "^3.54", "google/protobuf": "^3.23", - "phpunit/phpunit": "^10.4", + "pestphp/pest": "^2.34", + "phpunit/phpunit": "^10.5", "roxblnfk/unpoly": "^1.8.1", - "vimeo/psalm": "^5.11" + "vimeo/psalm": "^5.11", + "ergebnis/phpunit-slow-test-detector": "^2.14" }, "suggest": { "ext-simplexml": "To load trap.xml", @@ -86,7 +88,8 @@ ], "config": { "allow-plugins": { - "ergebnis/composer-normalize": true + "ergebnis/composer-normalize": true, + "pestphp/pest-plugin": true }, "audit": { "abandoned": "report" @@ -109,7 +112,8 @@ "stan": "php vendor/bin/phpstan analyse --memory-limit=2G", "stan:baseline": "php vendor/bin/phpstan analyse --generate-baseline --memory-limit=2G --allow-empty-baseline", "stan:ci": "php vendor/bin/phpstan analyse --memory-limit=2G --error-format=github", - "test": "XDEBUG_MODE=coverage php vendor/bin/phpunit", - "test:cc": "XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage --coverage-clover=.build/phpunit/logs/clover.xml" + "test": "XDEBUG_MODE=coverage php vendor/bin/pest --exclude-group phpunit-only", + "test:cc": "XDEBUG_MODE=coverage php vendor/bin/pest --coverage --coverage-clover=.build/phpunit/logs/clover.xml --exclude-group phpunit-only", + "test:leak": "XDEBUG_MODE=coverage php vendor/bin/phpunit --group phpunit-only" } } diff --git a/composer.lock b/composer.lock index 3765c756..8288de75 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1b45d0065dda2e44626f6f9655b77db6", + "content-hash": "98c1c3e996ac38aad8748bcf29db8c39", "packages": [ { "name": "clue/stream-filter", @@ -1431,6 +1431,101 @@ ], "time": "2024-04-13T18:00:56+00:00" }, + { + "name": "brianium/paratest", + "version": "v7.3.1", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", + "jean85/pretty-package-versions": "^2.0.5", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "phpunit/php-code-coverage": "^10.1.7", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-timer": "^6.0", + "phpunit/phpunit": "^10.4.2", + "sebastian/environment": "^6.0.1", + "symfony/console": "^6.3.4 || ^7.0.0", + "symfony/process": "^6.3.4 || ^7.0.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "infection/infection": "^0.27.6", + "phpstan/phpstan": "^1.10.40", + "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpstan/phpstan-strict-rules": "^1.5.2", + "squizlabs/php_codesniffer": "^3.7.2", + "symfony/filesystem": "^6.3.1 || ^7.0.0" + }, + "bin": [ + "bin/paratest", + "bin/paratest.bat", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2023-10-31T09:24:17+00:00" + }, { "name": "composer/pcre", "version": "3.1.3", @@ -1782,6 +1877,73 @@ }, "time": "2024-01-30T19:34:25+00:00" }, + { + "name": "ergebnis/phpunit-slow-test-detector", + "version": "2.14.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/phpunit-slow-test-detector.git", + "reference": "9138b0ebffdd2c579eb4b0567ef3bef8c714fdc2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/phpunit-slow-test-detector/zipball/9138b0ebffdd2c579eb4b0567ef3bef8c714fdc2", + "reference": "9138b0ebffdd2c579eb4b0567ef3bef8c714fdc2", + "shasum": "" + }, + "require": { + "php": "~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "phpunit/phpunit": "^6.5.0 || ^7.5.0 || ^8.5.19 || ^9.0.0 || ^10.0.0 || ^11.0.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.42.0", + "ergebnis/license": "^2.4.0", + "ergebnis/php-cs-fixer-config": "^6.25.1", + "fakerphp/faker": "~1.20.0", + "psalm/plugin-phpunit": "~0.19.0", + "psr/container": "~1.0.0", + "rector/rector": "^1.0.4", + "vimeo/psalm": "^5.23.1" + }, + "type": "library", + "extra": { + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\PHPUnit\\SlowTestDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides facilities for detecting slow tests in phpunit/phpunit.", + "homepage": "https://github.com/ergebnis/phpunit-slow-test-detector", + "keywords": [ + "detector", + "extension", + "phpunit", + "slow", + "test" + ], + "support": { + "issues": "https://github.com/ergebnis/phpunit-slow-test-detector/issues", + "security": "https://github.com/ergebnis/phpunit-slow-test-detector/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/phpunit-slow-test-detector" + }, + "time": "2024-04-08T06:35:34+00:00" + }, { "name": "felixfbecker/advanced-json-rpc", "version": "v3.2.1", @@ -1944,6 +2106,77 @@ ], "time": "2024-02-07T09:43:46+00:00" }, + { + "name": "filp/whoops", + "version": "2.15.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^0.9 || ^1.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.15.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2023-11-03T12:00:00+00:00" + }, { "name": "friendsofphp/php-cs-fixer", "version": "v3.56.1", @@ -2081,6 +2314,65 @@ }, "time": "2024-02-15T21:11:49+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + }, + "time": "2024-03-08T09:58:59+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.1", @@ -2247,6 +2539,351 @@ }, "time": "2024-03-17T08:10:35+00:00" }, + { + "name": "nunomaduro/collision", + "version": "v7.10.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.15.3", + "nunomaduro/termwind": "^1.15.1", + "php": "^8.1.0", + "symfony/console": "^6.3.4" + }, + "conflict": { + "laravel/framework": ">=11.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.3.0", + "laravel/framework": "^10.28.0", + "laravel/pint": "^1.13.3", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.13.0", + "pestphp/pest": "^2.23.2", + "phpunit/phpunit": "^10.4.1", + "sebastian/environment": "^6.0.1", + "spatie/laravel-ignition": "^2.3.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-10-11T15:45:01+00:00" + }, + { + "name": "pestphp/pest", + "version": "v2.34.7", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "a7a3e4240e341d0fee1c54814ce18adc26ce5a76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/a7a3e4240e341d0fee1c54814ce18adc26ce5a76", + "reference": "a7a3e4240e341d0fee1c54814ce18adc26ce5a76", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.3.1", + "nunomaduro/collision": "^7.10.0|^8.1.1", + "nunomaduro/termwind": "^1.15.1|^2.0.1", + "pestphp/pest-plugin": "^2.1.1", + "pestphp/pest-plugin-arch": "^2.7.0", + "php": "^8.1.0", + "phpunit/phpunit": "^10.5.17" + }, + "conflict": { + "phpunit/phpunit": ">10.5.17", + "sebastian/exporter": "<5.1.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^2.16.0", + "pestphp/pest-plugin-type-coverage": "^2.8.1", + "symfony/process": "^6.4.0|^7.0.4" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v2.34.7" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-04-05T07:44:17+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b", + "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.1" + }, + "conflict": { + "pestphp/pest": "<2.2.3" + }, + "require-dev": { + "composer/composer": "^2.5.8", + "pestphp/pest": "^2.16.0", + "pestphp/pest-dev-tools": "^2.16.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v2.1.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-08-22T08:40:06+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v2.7.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "d23b2d7498475354522c3818c42ef355dca3fcda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/d23b2d7498475354522c3818c42ef355dca3fcda", + "reference": "d23b2d7498475354522c3818c42ef355dca3fcda", + "shasum": "" + }, + "require": { + "nunomaduro/collision": "^7.10.0|^8.1.0", + "pestphp/pest-plugin": "^2.1.1", + "php": "^8.1", + "ta-tikoma/phpunit-architecture-test": "^0.8.4" + }, + "require-dev": { + "pestphp/pest": "^2.33.0", + "pestphp/pest-dev-tools": "^2.16.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.7.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-01-26T09:46:42+00:00" + }, { "name": "phar-io/manifest", "version": "2.0.4", @@ -2910,16 +3547,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.20", + "version": "10.5.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3" + "reference": "c1f736a473d21957ead7e94fcc029f571895abf5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3", - "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c1f736a473d21957ead7e94fcc029f571895abf5", + "reference": "c1f736a473d21957ead7e94fcc029f571895abf5", "shasum": "" }, "require": { @@ -2991,7 +3628,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.17" }, "funding": [ { @@ -3007,7 +3644,7 @@ "type": "tidelift" } ], - "time": "2024-04-24T06:32:35+00:00" + "time": "2024-04-05T04:39:01+00:00" }, { "name": "psr/event-dispatcher", @@ -4623,6 +5260,65 @@ ], "time": "2024-04-18T09:22:46+00:00" }, + { + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.4", + "source": { + "type": "git", + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18.0 || ^5.0.0", + "php": "^8.1.0", + "phpdocumentor/reflection-docblock": "^5.3.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0" + }, + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], + "support": { + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.4" + }, + "time": "2024-01-05T14:10:56+00:00" + }, { "name": "theseer/tokenizer", "version": "1.2.3", diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 00000000..c7073e51 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,9 @@ +in(__DIR__); From 606f969845dd312a3ca4edb51f0edb3aeec948f2 Mon Sep 17 00:00:00 2001 From: lotyp Date: Mon, 13 May 2024 16:41:38 +0300 Subject: [PATCH 063/106] style(md): lint markdown files --- LICENSE.md | 8 ++++---- README.md | 9 ++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index f3592a62..40070d1f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,18 +2,18 @@ Copyright (c) 2023, Buggregator. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Buggregator nor the names of its +* Neither the name of Buggregator nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/README.md b/README.md index 019bad02..9e072ea5 100644 --- a/README.md +++ b/README.md @@ -158,11 +158,11 @@ vendor/bin/trap -p1025 -p9912 -p9913 -p8000 --ui=8080 ``` Environment variables can also be used to set endpoints: + - `TRAP_TCP_PORTS` - for TCP traffic: `9912,9913,1025,8000` - `TRAP_TCP_HOST` - for the TCP host (default: `127.0.0.1`) - `TRAP_UI_PORT` - for the web interface: `8080` - ### Choosing Your Senders Buggregator Trap provides a variety of "senders" that dictate where the dumps will be sent. Currently, the available @@ -181,7 +181,6 @@ For instance, to simultaneously use the console, file, and server senders, you w vendor/bin/trap -s console -s file -s server ``` - ## Contributing We believe in the power of community-driven development. Here's how you can contribute: @@ -192,7 +191,7 @@ We believe in the power of community-driven development. Here's how you can cont issues labeled "help wanted" [here](https://github.com/buggregator/trap/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). - **Documentation:** Help us improve our [guides and tutorials](https://github.com/buggregator/docs/tree/master/docs) for a smoother user experience. - **Community Support:** Join our [Discord](https://discord.gg/qF3HpXhMEP) and help others get the most out of Buggregator. -- **Spread the Word:** Share your experience with Buggregator on social media and encourage others to contribute. +- **Spread the Word:** Share your experience with Buggregator on social media and encourage others to contribute. - **Donate:** Support our work by becoming a patron or making a one-time donation [![roxblnfk](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Droxblnfk%26type%3Dpatrons&label=roxblnfk&style=flat-square)](https://patreon.com/roxblnfk) [![butschster](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dbutschster%26type%3Dpatrons&label=butschster&style=flat-square)](https://patreon.com/butschster) @@ -200,14 +199,10 @@ We believe in the power of community-driven development. Here's how you can cont **Remember, every great developer was once a beginner. Contributing to open source projects is a step in your journey to becoming a better developer. So, don't hesitate to jump in and start contributing!** - ## License Buggregator Trap is open-sourced software licensed under the BSD-3 license. - - -