Skip to content

Commit 6e3a71d

Browse files
committed
Optimize fetch by sending only one request
1 parent ce1f3d6 commit 6e3a71d

File tree

3 files changed

+30
-162
lines changed

3 files changed

+30
-162
lines changed

src/Connection/Responses/MessageResponseParser.php

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,6 @@
66

77
class MessageResponseParser
88
{
9-
/**
10-
* Get the flags from an untagged response.
11-
*
12-
* @return array<string, string[]>
13-
*/
14-
public static function getFlags(UntaggedResponse $response): array
15-
{
16-
$data = $response->tokenAt(3);
17-
18-
$uid = $data->lookup('UID')->value;
19-
$flags = $data->lookup('FLAGS')->values();
20-
21-
return [$uid => $flags];
22-
}
23-
24-
/**
25-
* Get the body header from an untagged response.
26-
*
27-
* @return array<string, string>
28-
*/
29-
public static function getBodyHeader(UntaggedResponse $response): array
30-
{
31-
$data = $response->tokenAt(3);
32-
33-
$uid = $data->lookup('UID')->value;
34-
$headers = $data->lookup('[HEADER]')->value;
35-
36-
return [$uid => $headers];
37-
}
38-
39-
/**
40-
* Get the body text from an untagged response.
41-
*
42-
* @return array<string, string>
43-
*/
44-
public static function getBodyText(UntaggedResponse $response): array
45-
{
46-
$data = $response->tokenAt(3);
47-
48-
$uid = $data->lookup('UID')->value;
49-
$contents = $data->lookup('[TEXT]')->value;
50-
51-
return [$uid => $contents];
52-
}
53-
549
/**
5510
* Get the UID from a tagged move or copy response.
5611
*/

src/MessageQuery.php

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use DirectoryTree\ImapEngine\Collections\ResponseCollection;
77
use DirectoryTree\ImapEngine\Connection\ConnectionInterface;
88
use DirectoryTree\ImapEngine\Connection\ImapQueryBuilder;
9-
use DirectoryTree\ImapEngine\Connection\Responses\MessageResponseParser;
109
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
1110
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
1211
use DirectoryTree\ImapEngine\Enums\ImapFetchIdentifier;
@@ -238,15 +237,14 @@ protected function populate(Collection $uids): MessageCollection
238237

239238
$messages->total($uids->count());
240239

241-
$rawMessages = $this->fetch($uids);
242-
243-
foreach ($rawMessages['uids'] as $uid) {
244-
$flags = $rawMessages['flags'][$uid] ?? [];
245-
$headers = $rawMessages['headers'][$uid] ?? '';
246-
$contents = $rawMessages['contents'][$uid] ?? '';
247-
240+
foreach ($this->fetch($uids) as $uid => $response) {
248241
$messages->push(
249-
$this->newMessage($uid, $flags, $headers, $contents)
242+
$this->newMessage(
243+
$uid,
244+
$response['flags'] ?? [],
245+
$response['headers'] ?? '',
246+
$response['contents'] ?? '',
247+
)
250248
);
251249
}
252250

@@ -267,27 +265,29 @@ protected function fetch(Collection $messages): array
267265
->values()
268266
->all();
269267

270-
$flags = $this->fetchFlags ? $this->connection()
271-
->flags($uids)
272-
->mapWithKeys(MessageResponseParser::getFlags(...))
273-
->all() : [];
274-
275-
$headers = $this->fetchHeaders ? $this->connection()
276-
->bodyHeader($uids, $this->fetchAsUnread)
277-
->mapWithKeys(MessageResponseParser::getBodyHeader(...))
278-
->all() : [];
279-
280-
$contents = $this->fetchBody ? $this->connection()
281-
->bodyText($uids, $this->fetchAsUnread)
282-
->mapWithKeys(MessageResponseParser::getBodyText(...))
283-
->all() : [];
284-
285-
return [
286-
'uids' => $uids,
287-
'flags' => $flags,
288-
'headers' => $headers,
289-
'contents' => $contents,
290-
];
268+
$response = $this->connection()->fetch(array_filter([
269+
$this->fetchFlags ? 'FLAGS' : null,
270+
$this->fetchBody ? $this->fetchAsUnread
271+
? 'BODY.PEEK[TEXT]'
272+
: 'BODY[TEXT]' : null,
273+
$this->fetchHeaders ? $this->fetchAsUnread
274+
? 'BODY.PEEK[HEADER]'
275+
: 'BODY[HEADER]' : null,
276+
]), $uids);
277+
278+
return $response->mapWithKeys(function (UntaggedResponse $response) {
279+
$data = $response->tokenAt(3);
280+
281+
$uid = $data->lookup('UID')->value;
282+
283+
return [
284+
$uid => [
285+
'flags' => $data->lookup('FLAGS')?->values() ?? [],
286+
'headers' => $data->lookup('[HEADER]')?->value ?? '',
287+
'contents' => $data->lookup('[TEXT]')?->value ?? '',
288+
],
289+
];
290+
})->all();
291291
}
292292

293293
/**

tests/Unit/Connection/Responses/MessageResponseParserTest.php

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,9 @@
11
<?php
22

3-
use DirectoryTree\ImapEngine\Connection\Responses\Data\ListData;
43
use DirectoryTree\ImapEngine\Connection\Responses\Data\ResponseCodeData;
54
use DirectoryTree\ImapEngine\Connection\Responses\MessageResponseParser;
65
use DirectoryTree\ImapEngine\Connection\Responses\TaggedResponse;
7-
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
86
use DirectoryTree\ImapEngine\Connection\Tokens\Atom;
9-
use DirectoryTree\ImapEngine\Connection\Tokens\Literal;
10-
11-
it('parses flags from untagged response', function () {
12-
$response = new UntaggedResponse([
13-
new Atom('*'), // Untagged marker
14-
new Atom('1'), // Sequence number (example)
15-
new Atom('FETCH'), // Command type
16-
new ListData([ // Data list
17-
new Atom('UID'),
18-
new Atom('12345'), // The message UID
19-
new Atom('FLAGS'),
20-
new ListData([ // The list of flags
21-
new Atom('\Seen'),
22-
new Atom('\Answered'),
23-
new Atom('$Important'), // Example custom flag
24-
]),
25-
]),
26-
]);
27-
28-
$parsedFlags = MessageResponseParser::getFlags($response);
29-
30-
expect($parsedFlags)->toBe([
31-
'12345' => ['\Seen', '\Answered', '$Important'],
32-
]);
33-
});
34-
35-
it('parses body header from untagged response', function () {
36-
$headerContent = "From: [email protected]\r\nTo: [email protected]\r\nSubject: Test Email Header\r\n";
37-
38-
$response = new UntaggedResponse([
39-
new Atom('*'), // Untagged marker
40-
new Atom('2'), // Sequence number (example)
41-
new Atom('FETCH'), // Command type
42-
new ListData([ // Data list
43-
new Atom('UID'),
44-
new Atom('54321'), // The message UID
45-
new Atom('BODY'),
46-
new Atom('[HEADER]'), // Specifies header part
47-
new Literal($headerContent), // The header content as a literal
48-
]),
49-
]);
50-
51-
$parsedHeader = MessageResponseParser::getBodyHeader($response);
52-
53-
expect($parsedHeader)->toBe(['54321' => $headerContent]);
54-
});
55-
56-
it('parses body text from untagged response', function () {
57-
$textContent = "This is the plain text body of the email.\r\nIt might have multiple lines.\r\n";
58-
59-
$response = new UntaggedResponse([
60-
new Atom('*'), // Untagged marker
61-
new Atom('3'), // Sequence number (example)
62-
new Atom('FETCH'), // Command type
63-
new ListData([ // Data list
64-
new Atom('UID'),
65-
new Atom('98765'), // The message UID
66-
new Atom('BODY'),
67-
new Atom('[TEXT]'), // Specifies text part
68-
new Literal($textContent), // The text content as a literal
69-
]),
70-
]);
71-
72-
$parsedText = MessageResponseParser::getBodyText($response);
73-
74-
expect($parsedText)->toBe(['98765' => $textContent]);
75-
});
76-
77-
it('handles empty flags list correctly', function () {
78-
$response = new UntaggedResponse([
79-
new Atom('*'),
80-
new Atom('4'),
81-
new Atom('FETCH'),
82-
new ListData([
83-
new Atom('UID'),
84-
new Atom('11111'),
85-
new Atom('FLAGS'),
86-
new ListData([]), // Empty flags list
87-
]),
88-
]);
89-
90-
$parsedFlags = MessageResponseParser::getFlags($response);
91-
92-
expect($parsedFlags)->toBe(['11111' => []]);
93-
});
947

958
it('parses UID from tagged COPYUID response', function () {
969
$response = new TaggedResponse([

0 commit comments

Comments
 (0)