From aca3864c4767f448a84ed2f89df12ba823f569f2 Mon Sep 17 00:00:00 2001 From: christopher Date: Tue, 30 Sep 2025 08:52:22 +0200 Subject: [PATCH 1/4] Implement PHPStan up to level 4 --- phpstan.neon | 2 +- src/Connection/ImapConnection.php | 6 ++++-- src/Connection/ImapQueryBuilder.php | 2 ++ src/FlaggableInterface.php | 1 + src/MessageQuery.php | 4 ++-- src/QueriesMessages.php | 1 + src/Support/Str.php | 1 + 7 files changed, 12 insertions(+), 5 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index e1835aa..c44a929 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 3 + level: 4 ignoreErrors: - identifier: new.static diff --git a/src/Connection/ImapConnection.php b/src/Connection/ImapConnection.php index 5a52c2b..83cec10 100644 --- a/src/Connection/ImapConnection.php +++ b/src/Connection/ImapConnection.php @@ -122,6 +122,7 @@ public function connect(string $host, ?int $port = null, array $options = []): v /** * Get the default socket options for the given transport. + * @param 'ssl'|'tls'|'starttls'|'tcp' $transport */ protected function getDefaultSocketOptions(string $transport, array $proxy = [], bool $validateCert = true): array { @@ -595,6 +596,7 @@ public function done(): void /** * Send an IMAP command. + * @param-out string $tag */ public function send(string $name, array $tokens = [], ?string &$tag = null): void { @@ -744,9 +746,9 @@ protected function assertNextResponse(callable $filter, callable $assertion, cal * @template T of Response * * @param callable(T): bool $filter - * @return T + * @return null|T */ - protected function nextResponse(callable $filter): Response + protected function nextResponse(callable $filter): ?Response { if (! $this->parser) { throw new LogicException('No parser instance set'); diff --git a/src/Connection/ImapQueryBuilder.php b/src/Connection/ImapQueryBuilder.php index b2a6ee9..d59ace3 100644 --- a/src/Connection/ImapQueryBuilder.php +++ b/src/Connection/ImapQueryBuilder.php @@ -373,6 +373,7 @@ protected function parseDate(mixed $date): CarbonInterface /** * Build a single expression node from a basic or nested where. + * @param array{type: 'basic'|'nested', boolean: 'AND'|'OR', query: ImapQueryBuilder} $where */ protected function makeExpressionNode(array $where): array { @@ -391,6 +392,7 @@ protected function makeExpressionNode(array $where): array /** * Merge the existing expression with the next expression, respecting the boolean operator. + * @param 'AND'|'OR' $boolean */ protected function mergeExpressions(string $existing, string $next, string $boolean): string { diff --git a/src/FlaggableInterface.php b/src/FlaggableInterface.php index 3051352..b256f83 100644 --- a/src/FlaggableInterface.php +++ b/src/FlaggableInterface.php @@ -120,6 +120,7 @@ public function hasFlag(BackedEnum|string $flag): bool; /** * Add or remove a flag from the message. + * @param '+'|'-' $operation */ public function flag(BackedEnum|string $flag, string $operation, bool $expunge = false): void; } diff --git a/src/MessageQuery.php b/src/MessageQuery.php index 4cdca94..e7f464d 100644 --- a/src/MessageQuery.php +++ b/src/MessageQuery.php @@ -302,8 +302,8 @@ protected function fetch(Collection $messages): array return [ $uid => [ 'flags' => $data->lookup('FLAGS')?->values() ?? [], - 'headers' => $data->lookup('[HEADER]')?->value ?? '', - 'contents' => $data->lookup('[TEXT]')?->value ?? '', + 'headers' => $data->lookup('[HEADER]')->value ?? '', + 'contents' => $data->lookup('[TEXT]')->value ?? '', ], ]; })->all(); diff --git a/src/QueriesMessages.php b/src/QueriesMessages.php index 8e24631..8c7c483 100644 --- a/src/QueriesMessages.php +++ b/src/QueriesMessages.php @@ -42,6 +42,7 @@ trait QueriesMessages /** * The fetch order. + * @var 'asc'|'desc' $fetchOrder */ protected string $fetchOrder = 'desc'; diff --git a/src/Support/Str.php b/src/Support/Str.php index 579bbf7..ee41fe0 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -148,6 +148,7 @@ public static function fromImapUtf7(string $string): string // Direct implementation of IMAP's modified UTF-7 decoding. return preg_replace_callback('/&([^-]*)-?/', function ($matches) { + /** @var array{0: string, 1: string, 2?: string} $matches */ // If it's just an ampersand. if ($matches[1] === '') { return '&'; From 162e3fccef9fe59cec34672edc4b0bd7020c14fc Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Wed, 1 Oct 2025 09:46:50 -0400 Subject: [PATCH 2/4] Run CS fix --- src/Connection/ImapConnection.php | 4 +++- src/Connection/ImapQueryBuilder.php | 6 ++++-- src/FlaggableInterface.php | 3 ++- src/QueriesMessages.php | 3 ++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Connection/ImapConnection.php b/src/Connection/ImapConnection.php index 83cec10..ba6284a 100644 --- a/src/Connection/ImapConnection.php +++ b/src/Connection/ImapConnection.php @@ -122,7 +122,8 @@ public function connect(string $host, ?int $port = null, array $options = []): v /** * Get the default socket options for the given transport. - * @param 'ssl'|'tls'|'starttls'|'tcp' $transport + * + * @param 'ssl'|'tls'|'starttls'|'tcp' $transport */ protected function getDefaultSocketOptions(string $transport, array $proxy = [], bool $validateCert = true): array { @@ -596,6 +597,7 @@ public function done(): void /** * Send an IMAP command. + * * @param-out string $tag */ public function send(string $name, array $tokens = [], ?string &$tag = null): void diff --git a/src/Connection/ImapQueryBuilder.php b/src/Connection/ImapQueryBuilder.php index d59ace3..0496b7d 100644 --- a/src/Connection/ImapQueryBuilder.php +++ b/src/Connection/ImapQueryBuilder.php @@ -373,7 +373,8 @@ protected function parseDate(mixed $date): CarbonInterface /** * Build a single expression node from a basic or nested where. - * @param array{type: 'basic'|'nested', boolean: 'AND'|'OR', query: ImapQueryBuilder} $where + * + * @param array{type: 'basic'|'nested', boolean: 'AND'|'OR', query: ImapQueryBuilder} $where */ protected function makeExpressionNode(array $where): array { @@ -392,7 +393,8 @@ protected function makeExpressionNode(array $where): array /** * Merge the existing expression with the next expression, respecting the boolean operator. - * @param 'AND'|'OR' $boolean + * + * @param 'AND'|'OR' $boolean */ protected function mergeExpressions(string $existing, string $next, string $boolean): string { diff --git a/src/FlaggableInterface.php b/src/FlaggableInterface.php index b256f83..96e0d31 100644 --- a/src/FlaggableInterface.php +++ b/src/FlaggableInterface.php @@ -120,7 +120,8 @@ public function hasFlag(BackedEnum|string $flag): bool; /** * Add or remove a flag from the message. - * @param '+'|'-' $operation + * + * @param '+'|'-' $operation */ public function flag(BackedEnum|string $flag, string $operation, bool $expunge = false): void; } diff --git a/src/QueriesMessages.php b/src/QueriesMessages.php index 8c7c483..660fc22 100644 --- a/src/QueriesMessages.php +++ b/src/QueriesMessages.php @@ -42,7 +42,8 @@ trait QueriesMessages /** * The fetch order. - * @var 'asc'|'desc' $fetchOrder + * + * @var 'asc'|'desc' */ protected string $fetchOrder = 'desc'; From ab9dec074d62c810dfdefbcf62e4035af86be13e Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Wed, 1 Oct 2025 09:47:37 -0400 Subject: [PATCH 3/4] Flip return types --- src/Connection/ImapConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Connection/ImapConnection.php b/src/Connection/ImapConnection.php index ba6284a..b38e63c 100644 --- a/src/Connection/ImapConnection.php +++ b/src/Connection/ImapConnection.php @@ -748,7 +748,7 @@ protected function assertNextResponse(callable $filter, callable $assertion, cal * @template T of Response * * @param callable(T): bool $filter - * @return null|T + * @return T|null */ protected function nextResponse(callable $filter): ?Response { From fb8abb892b143043dfe3b4af460e54f12f6fcee8 Mon Sep 17 00:00:00 2001 From: christopher Date: Thu, 2 Oct 2025 08:10:41 +0200 Subject: [PATCH 4/4] Make loop in assertNextResponse looping, by making sure nextResponse returns null instead of throwing PHPStorm tells me the loop in assertNextResponse is not looping, PHPStan tells me the throw at the end of assertNextResponse ist unreachable --- src/Connection/ImapConnection.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Connection/ImapConnection.php b/src/Connection/ImapConnection.php index b38e63c..629b60a 100644 --- a/src/Connection/ImapConnection.php +++ b/src/Connection/ImapConnection.php @@ -24,6 +24,7 @@ use Exception; use Generator; use LogicException; +use Throwable; class ImapConnection implements ConnectionInterface { @@ -726,8 +727,12 @@ protected function assertTaggedResponse(string $tag, ?callable $exception = null * * @template T of Response * - * @param callable(T): bool $filter + * @param callable(Response): bool $filter + * @param callable(T): bool $assertion + * @param callable(T): Throwable $exception * @return T + * + * @throws ImapResponseException */ protected function assertNextResponse(callable $filter, callable $assertion, callable $exception): Response { @@ -768,7 +773,7 @@ protected function nextResponse(callable $filter): ?Response } } - throw new ImapResponseException('No matching response found'); + return null; } /**