setFetchOrder() does not sort on the server
Prerequisites
- php-imap version: 6.2.0
- PHP version: 8.3
- IMAP Server: IMAP
Description
The methods $query->setFetchOrder(), $query->setFetchOrderAsc(), and $query->fetchOrderAsc() (and their desc counterparts) do not seem to correctly sort messages as expected for the entire set of messages returned by the IMAP server.
Furthermore, these sorting methods operate only after all messages have been fetched via search() and are processed within the PHP environment inside the curate_messages() method. This results in a severe performance bottleneck when dealing with mailboxes containing thousands of messages, especially when the goal is to retrieve only a small subset (e.g., the 10 newest messages).
Steps to Reproduce
- Use a mailbox with several thousand emails in a folder.
- Attempt to fetch the messages using a specific fetch order:
$messages = $folder->query()->setFetchOrder("desc")->get(); // OR $messages = $folder->query()->fetchOrderDesc()->get(); - Iterate over the returned
$messagesand check thedateproperty.
Expected Behavior
- Messages should be sorted correctly, either ascending or descending, based on their internal date or arrival date, according to the specified fetch order.
- Ideally, the sorting should occur on the IMAP server using IMAP commands like
SORTorSEARCH/SORTto minimize data transfer and memory usage.
Actual Behavior (Sorting Issue)
The sorting is often incorrect.
For example, when fetching in descending order (newest first), the result still contains messages that are much older mixed in with the newest ones.
Example Dates Received (A small part of the returned dates):
2023-06-02 13:13:27
2023-07-03 16:15:39
... (correctly descending)
2024-12-04 16:12:22
2025-01-06 18:19:45
... (correctly descending)
2025-06-02 13:43:00
2014-02-03 08:52:01 <-- **OLD MESSAGES APPEAR HERE**
2014-02-21 11:25:44
2014-02-21 11:26:45
2014-02-24 16:14:57
Performance Issue / Proposed Feature
Current Implementation Analysis
Based on the library code:
- The
$query->get()method callssearch()to get all message UIDs/IDs first:// In src/Query/Query.php:L196 public function get(): MessageCollection { return $this->curate_messages($this->search()); } - The sorting logic is applied later inside
curate_messages()viafetch():// In src/Query/Query.php (Inside curate_messages -> fetch) protected function fetch(Collection $available_messages): array { if ($this->fetch_order === 'desc') { // Reverses the collection AFTER all UIDs have been fetched from the server. $available_messages = $available_messages->reverse(); } // ... then iterates and fetches message bodies/headers. }
This means that even if I only want the 10 newest emails, I currently have to use:
$folder->query()->all()->setFetchBody(false)->get(); // fetches ALL message UIDs/IDs
// AND THEN manually sort and limit in PHP
This is highly inefficient for large mailboxes, as all message UIDs/IDs must be processed in memory, and then the collection is reversed in PHP, which does not guarantee a truly sorted set (as seen in the "Actual Behavior" section).
Question / Feature Request
- Is there a better, server-side way to fetch just the latest N messages?
- Could the library be updated to utilize the IMAP
SORTcommand (as implemented by PHP's built-inimap_sort()function, though without relying on the old PHP IMAP extension)? This would allow for efficient server-side sorting and limiting of results, drastically improving performance.
How do other users handle fetching a small, sorted subset of messages from very large folders with this library?