From 0a26e6f992e9973c8be3ece43d38f21c1e49a44c Mon Sep 17 00:00:00 2001 From: Dima Jolkin Date: Sun, 5 May 2024 21:22:02 +0300 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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 {