Repository navigation
[2.x] Add partial body fetching and chunked attachment streams - #189
Merged
Merged
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Expanding the public interface method signature breaks existing third-party implementations on the 2.x release line.
Review effort: Balanced
Findings: 1
What changed in this PR
Adds ranged IMAP body fetching and disk-backed, incrementally decoded attachment streams.
Changes:
- Adds immutable partial body fetch modifiers and direct ranged section retrieval.
- Streams and decodes attachments in 64 KiB chunks.
- Adds unit, integration, and usage documentation coverage.
| File | Description |
|---|---|
src/Attachment.php |
Streams attachments when saving. |
src/FileMessage.php |
Updates body-part method signature. |
src/Message.php |
Implements ranged body fetching. |
src/MessageData/Body.php |
Adds partial fetch modifiers. |
src/MessageInterface.php |
Expands the body-part API. |
src/Support/LazyBodyPartStream.php |
Adds chunked fetching and decoding. |
src/Testing/FakeMessage.php |
Aligns the test double signature. |
tests/Integration/MessagesTest.php |
Tests live partial fetching. |
tests/Unit/MessageTest.php |
Updates attachment fetch expectations. |
tests/Unit/PartialBodyTest.php |
Tests ranges, streaming, decoding, and saving. |
readme.md |
Documents partial fetching and streaming. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Fetching a small part of a large message or saving an attachment currently requires downloading the entire body section into memory.
This PR adds partial body fetching and chunked attachment streams so applications can retrieve only the bytes they need and save large attachments without holding the complete contents in memory.
Partial body fetching
Body fetch items accept an immutable
partial()modifier, which can be combined withpeek():Individual message sections can also be fetched directly:
Offsets are zero-based and refer to transfer-encoded bytes, before base64 or quoted-printable decoding. Responses retain their offsets, and partial fetches do not replace cached complete body parts. An empty string indicates the end of a section; a missing message or section returns
null.Attachment streaming
Attachments obtained through
attachments(fetch: true)now fetch their contents in 64 KiB chunks as the stream is read. Base64 and quoted-printable decoding preserves state across chunk boundaries. Decoded content is cached in a temporary stream that spills to disk beyond 2 MiB, allowing seeks and repeated reads without fetching the same bytes again.Attachment::save()copies stream chunks directly to the destination:Calling
contents()or casting a stream to a string still loads the requested contents into memory. The decoded stream size remains unknown until the section has been fully read. Missing body parts throw rather than silently producing a truncated attachment.Validated with the full unit suite: 847 tests passing. Formatting and diff checks pass. Live-server integration validation is blocked because the local IMAP server at
localhost:3143refuses connections.