generated from Nodarx/template
fix: message streaming
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -39,7 +39,7 @@ final class FetchResponseParser
|
||||
public function parseMany(ResponseStream $responses): Generator
|
||||
{
|
||||
foreach ($responses as $response) {
|
||||
if ($response instanceof UntaggedResponse && MessageParser::isFetchMessage($response->payload())) {
|
||||
if ($response instanceof UntaggedResponse && MessageParser::isFetchMessage($response->raw())) {
|
||||
yield MessageParser::parse($response->raw());
|
||||
continue;
|
||||
}
|
||||
@@ -55,4 +55,4 @@ final class FetchResponseParser
|
||||
|
||||
throw new ImapException('FETCH did not receive a tagged completion response.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,21 @@ use DateTimeInterface;
|
||||
|
||||
final class MessageParser
|
||||
{
|
||||
public static function isFetchMessage(string $payload): bool
|
||||
public static function isFetchMessage(string $raw): bool
|
||||
{
|
||||
return str_contains(strtoupper($payload), 'FETCH (');
|
||||
return preg_match('/^\*\s+\d+\s+FETCH\s+\(/i', $raw) === 1;
|
||||
}
|
||||
|
||||
public static function parse(string $raw): Message
|
||||
{
|
||||
if (!preg_match('/^\*\s+(\d+)\s+FETCH\s+\((.*)\)$/is', $raw, $matches)) {
|
||||
if (preg_match('/^\*\s+(\d+)\s+FETCH\s+\(/iA', $raw, $matches) !== 1
|
||||
|| !str_ends_with($raw, ')')) {
|
||||
throw new ImapException('Unable to parse FETCH response: ' . $raw);
|
||||
}
|
||||
|
||||
$sequence = (int) $matches[1];
|
||||
$attributes = self::parseAttributes($matches[2]);
|
||||
$offset = strlen($matches[0]);
|
||||
$attributes = self::parseAttributes($raw, $offset, strlen($raw) - 1);
|
||||
$uid = self::toInt($attributes['UID'] ?? null, 'FETCH response is missing UID: ' . $raw);
|
||||
$envelope = is_array($attributes['ENVELOPE'] ?? null) ? $attributes['ENVELOPE'] : null;
|
||||
$bodyStructure = isset($attributes['BODYSTRUCTURE']) ? self::parseBodyPart($attributes['BODYSTRUCTURE'], '') : null;
|
||||
@@ -52,15 +54,13 @@ final class MessageParser
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function parseAttributes(string $payload): array
|
||||
private static function parseAttributes(string $payload, int &$offset, int $end): array
|
||||
{
|
||||
$attributes = [];
|
||||
$offset = 0;
|
||||
$length = strlen($payload);
|
||||
|
||||
while ($offset < $length) {
|
||||
while ($offset < $end) {
|
||||
self::skipWhitespace($payload, $offset);
|
||||
if ($offset >= $length) {
|
||||
if ($offset >= $end) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -784,4 +784,4 @@ final class MessageParser
|
||||
$value,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,16 +56,27 @@ final class ProtocolReader
|
||||
}
|
||||
|
||||
if (str_starts_with($raw, '* ')) {
|
||||
$parts = preg_split('/\s+/', substr($raw, 2), 2) ?: [];
|
||||
$label = strtoupper($parts[0] ?? '');
|
||||
// Keep the payload as a slice of the raw response. FETCH payloads can
|
||||
// contain large message literals, so eagerly splitting here would keep
|
||||
// a second full copy alive while the message is parsed.
|
||||
$labelEnd = 2;
|
||||
while (isset($raw[$labelEnd]) && !ctype_space($raw[$labelEnd])) {
|
||||
$labelEnd++;
|
||||
}
|
||||
$label = strtoupper(substr($raw, 2, $labelEnd - 2));
|
||||
$payloadOffset = $labelEnd;
|
||||
while (isset($raw[$payloadOffset]) && ctype_space($raw[$payloadOffset])) {
|
||||
$payloadOffset++;
|
||||
}
|
||||
$this->logger?->debug('IMAP untagged response received: {raw}', [
|
||||
'label' => $label,
|
||||
'raw' => $raw,
|
||||
]);
|
||||
return new UntaggedResponse(
|
||||
$label,
|
||||
$parts[1] ?? '',
|
||||
'',
|
||||
$raw,
|
||||
$payloadOffset,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -181,4 +192,4 @@ final class ProtocolReader
|
||||
|
||||
return $raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ final class UntaggedResponse implements ResponseInterface
|
||||
private readonly string $label,
|
||||
private readonly string $payload,
|
||||
private readonly string $raw,
|
||||
private readonly ?int $payloadOffset = null,
|
||||
) {}
|
||||
|
||||
public function label(): string
|
||||
@@ -19,6 +20,12 @@ final class UntaggedResponse implements ResponseInterface
|
||||
|
||||
public function payload(): string
|
||||
{
|
||||
// ProtocolReader records an offset for potentially large responses and
|
||||
// only materializes the payload for command parsers that actually need it.
|
||||
if ($this->payloadOffset !== null) {
|
||||
return substr($this->raw, $this->payloadOffset);
|
||||
}
|
||||
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
@@ -27,7 +34,7 @@ final class UntaggedResponse implements ResponseInterface
|
||||
*/
|
||||
public function payloadTokens(): array
|
||||
{
|
||||
$payload = trim($this->payload);
|
||||
$payload = trim($this->payload());
|
||||
|
||||
if ($payload === '') {
|
||||
return [];
|
||||
@@ -40,4 +47,4 @@ final class UntaggedResponse implements ResponseInterface
|
||||
{
|
||||
return $this->raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user