From 68ca777150be1b19e4c5218ec6807220cbf496d9 Mon Sep 17 00:00:00 2001 From: Sebastian Krupinski Date: Fri, 25 Sep 2026 11:54:28 -0400 Subject: [PATCH] refactor: unify response-code parsing Signed-off-by: Sebastian Krupinski --- lib/Client/Protocol/Command/AppendCommand.php | 17 +++++++--- .../Command/MessageTransferCommand.php | 28 ++++------------ .../Protocol/Parser/ResponseCodeParser.php | 32 +++++++++++++++++++ 3 files changed, 51 insertions(+), 26 deletions(-) create mode 100644 lib/Client/Protocol/Parser/ResponseCodeParser.php diff --git a/lib/Client/Protocol/Command/AppendCommand.php b/lib/Client/Protocol/Command/AppendCommand.php index e296472..8a870ba 100644 --- a/lib/Client/Protocol/Command/AppendCommand.php +++ b/lib/Client/Protocol/Command/AppendCommand.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace KTXM\ProviderImap\Client\Protocol\Command; +use KTXM\ProviderImap\Client\Protocol\Parser\ResponseCodeParser; use KTXM\ProviderImap\Client\Protocol\StringEncoder; use KTXM\ProviderImap\Client\ImapException; use KTXM\ProviderImap\Client\Protocol\RequestFrame; @@ -26,6 +27,7 @@ use KTXM\ProviderImap\Client\Protocol\SessionState; */ final class AppendCommand implements CommandInterface { + private readonly ResponseCodeParser $responseCodeParser; private readonly string $literal; /** @@ -36,6 +38,8 @@ final class AppendCommand implements CommandInterface string $message, private readonly array $flags = [], ) { + $this->responseCodeParser = new ResponseCodeParser(); + // IMAP literals are octet-counted; normalise to CRLF line endings. $this->literal = (string) preg_replace('/\r\n|\r|\n/', "\r\n", $message); } @@ -87,13 +91,18 @@ final class AppendCommand implements CommandInterface throw new ImapException('APPEND did not receive a tagged completion response.'); } - private function parseAppendUid(string $text): ?int { - if (preg_match('/\[APPENDUID\s+\d+\s+(\d+)\]/i', $text, $matches) === 1) { - return (int) $matches[1]; + $responseCode = $this->responseCodeParser->parse($text); + if ($responseCode === null || $responseCode['name'] !== 'APPENDUID') { + return null; } - return null; + $arguments = $responseCode['arguments']; + if (count($arguments) !== 2 || !ctype_digit($arguments[0]) || !ctype_digit($arguments[1])) { + return null; + } + + return (int) $arguments[1]; } } diff --git a/lib/Client/Protocol/Command/MessageTransferCommand.php b/lib/Client/Protocol/Command/MessageTransferCommand.php index ae4f550..3235045 100644 --- a/lib/Client/Protocol/Command/MessageTransferCommand.php +++ b/lib/Client/Protocol/Command/MessageTransferCommand.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace KTXM\ProviderImap\Client\Protocol\Command; +use KTXM\ProviderImap\Client\Protocol\Parser\ResponseCodeParser; use KTXM\ProviderImap\Client\Protocol\StringEncoder; use KTXM\ProviderImap\Client\Result\MessageTransferResult; use KTXM\ProviderImap\Client\Protocol\Command\Argument\MessageTarget; @@ -22,6 +23,7 @@ use KTXM\ProviderImap\Client\Protocol\SessionState; */ final class MessageTransferCommand implements CommandInterface { + private readonly ResponseCodeParser $responseCodeParser; private readonly string $operation; private readonly SequenceSet $sequenceSet; private readonly IdentifierMode $identifierMode; @@ -31,6 +33,8 @@ final class MessageTransferCommand implements CommandInterface MessageTarget|string|SequenceSet|null $target = null, private readonly string $destinationMailbox = '', ) { + $this->responseCodeParser = new ResponseCodeParser(); + $resolvedTarget = match (true) { $target instanceof MessageTarget => $target, $target instanceof SequenceSet => MessageTarget::sequence($target), @@ -184,7 +188,7 @@ final class MessageTransferCommand implements CommandInterface bool &$tryCreate, ?string &$highestModSeq, ): void { - $responseCode = $this->parseResponseCode($text); + $responseCode = $this->responseCodeParser->parse($text); if ($responseCode === null) { return; } @@ -216,24 +220,4 @@ final class MessageTransferCommand implements CommandInterface 'destinationUids' => $responseCode['arguments'][2], ]; } - - /** - * @return ?array{name:string, arguments:list, text:string} - */ - private function parseResponseCode(string $text): ?array - { - $text = trim($text); - - if (preg_match('/^\[([A-Z0-9.-]+)(?:\s+([^\]]+))?\](?:\s*(.*))?$/i', $text, $matches) !== 1) { - return null; - } - - $arguments = trim($matches[2] ?? ''); - - return [ - 'name' => strtoupper($matches[1]), - 'arguments' => $arguments === '' ? [] : (preg_split('/\s+/', $arguments) ?: []), - 'text' => trim($matches[3] ?? ''), - ]; - } -} \ No newline at end of file +} diff --git a/lib/Client/Protocol/Parser/ResponseCodeParser.php b/lib/Client/Protocol/Parser/ResponseCodeParser.php new file mode 100644 index 0000000..c6e63da --- /dev/null +++ b/lib/Client/Protocol/Parser/ResponseCodeParser.php @@ -0,0 +1,32 @@ +, text:string} + */ + public function parse(string $text): ?array + { + $text = trim($text); + + if (preg_match('/^\[([A-Z0-9.-]+)(?:\s+([^\]]+))?\](?:\s*(.*))?$/i', $text, $matches) !== 1) { + return null; + } + + $arguments = trim($matches[2] ?? ''); + + return [ + 'name' => strtoupper($matches[1]), + 'arguments' => $arguments === '' ? [] : (preg_split('/\s+/', $arguments) ?: []), + 'text' => trim($matches[3] ?? ''), + ]; + } +}