Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Imapengine Laravel Package

directorytree/imapengine

ImapEngine is a modern PHP IMAP library for reading and managing mailboxes with a clean, expressive API. It supports messages, attachments, flags, searches, folders, and Laravel integration, making email automation and inbox syncing simple and reliable.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up.

composer require directorytree/imapengine

There is no ext-imap requirement — the library talks the protocol over sockets, so it works on hosts where the legacy extension is missing or banned. Requirements are PHP 8.x, ext-openssl for TLS, and illuminate/collections (pulled in automatically; it's a first-class dependency, so keep it aligned with your Laravel version — see Gotchas).

Standalone PHP — the smallest useful connection:

use DirectoryTree\ImapEngine\Mailbox;

$mailbox = Mailbox::make([
    'host'       => 'imap.example.com',
    'port'       => 993,
    'encryption' => 'ssl',
    'username'   => config('mail.imap.user'),
    'password'   => config('mail.imap.pass'),
    'timeout'    => 30,
    'debug'      => false,
]);

$inbox = $mailbox->inbox();

Laravel. Publish the package config during install (php artisan vendor:publish, pick the ImapEngine provider's tag) and you get a config file holding named mailbox connections plus a facade/factory to resolve them. Keep credentials in .env, not in config/imap.php, and register one connection per mailbox you actually poll.

Where to look first. In order: the config file (connection shape and defaults), then Mailbox (connect(), inbox(), folders()), then Folder (messages(), idle(), poll(), quota methods), then Message. The query builder hanging off $folder->messages() is where you'll spend 80% of your time — read the docs page on message retrieval and search before writing anything real.

First use case: an inbox list view. Headers only, no bodies, no attachments. Lazy loading (v1.23/1.24) makes this cheap by default:

$messages = $mailbox->inbox()
    ->messages()
    ->withHeaders()          // subject, from, date, message-id
    ->withFlags()            // \Seen, \Flagged, custom keywords
    ->unseen()
    ->get();

foreach ($messages as $message) {
    $rows[] = [
        'uid'     => $message->uid(),
        'subject' => $message->subject(),
        'from'    => $message->from()->name ?? $message->from()->email,
        'date'    => $message->date(),
        'unread'  => ! $message->hasFlag('\\Seen'),
    ];
}

Only call $message->text(), ->html(), or ->attachments() when a user actually opens a message.

Implementation Patterns

Separate list queries from detail queries. This is the single biggest lever. A list view wants withHeaders() + withFlags(); a detail view wants withBody() (and bodyStructure() if you render parts yourself). If you find yourself looping over 200 messages calling ->text(), you've built an N+1 over IMAP round trips. Eager-load on the query or don't load at all.

Incremental sync via UID ranges. Don't re-scan the mailbox each run. Persist the highest UID you've seen per folder, then:

$query = $folder->messages()
    ->withHeaders()
    ->withFlags()
    ->uid($lastSeenUid + 1, INF);   // UID sequence range (v1.16.1)

foreach ($query->get() as $message) { /* persist UID, ingest */ }

UID ranges are also the right tool for backfill, chunked so no single command exceeds server limits. Note the v1.24.1 fix around infinite UID ranges — if you're on an older release, be explicit about your upper bound.

Push work to the server. Subject/from/date criteria, seen/unseen, larger()/smaller() (v1.21), and server-side SORT (v1.22) all mean fewer bytes and fewer round trips. Anything the server can filter, don't filter in PHP. LARGER/SMALLER is how you find the 20MB attachment that's stalling your ingestion pipeline.

Bulk, don't loop. Per-message markRead(), move(), copy(), delete() inside a foreach is a command per message. The query-level bulk operations (v1.21.0) collapse that into range-optimized payloads (v1.25.6):

$folder->messages()
    ->subject('Invoice')
    ->before('2025-01-01')
    ->markRead();          // or ->flag(...), ->delete(), ->move($archive), ->copy(...)

Use the per-message methods only for single-item user actions.

Ingestion pipeline shape. Query → filter server-side (size, date, sender) → for each message, walk attachments(), save() (or stream contents()) to object storage, record filename, contentType, contentDisposition, size → then move the message to a processed folder or flag it. Keep the whole thing in a queued job, one message per job, so a poison message doesn't kill the batch. RFC822.SIZE (v1.18) lets you reject oversized messages before downloading parts.

Near-real-time: idle() vs poll(). idle() is the protocol-native option; always pass a timeout callback so you can break out and let the process recycle (v1.16.0). poll() (v1.19.0) is long-polling built on top and is easier to reason about in a worker loop. Either way, run it in a dedicated worker with a supervisor, not inside a web request, and reconnect on exit.

Thread detection uses messageId() and In-Reply-To (now handled as a Message-ID, not an address, per v1.18). In-Reply-To is your cheap grouping key; don't build threading off subject normalization.

Hide the library behind an interface. Define your own MailService / DTOs (InboundMessage, Attachment) and map at the boundary. Nothing in your domain should import DirectoryTree\ImapEngine\*. That buys you the swap to Gmail API / Graph later, and it makes tests trivial because you can fake your own interface.

Testing. Unit-test your adapter against fakes; integration-test the real thing against a disposable Dovecot or a dedicated test mailbox per provider you support. Server behavior, not PHP behavior, is where the bugs live.

Gotchas and Tips

  • Lazy loading is default-on, and that's a footgun as much as a feature. $message->subject() on 500 messages is fine; $message->html() on 500 messages is 500 fetches. Decide explicitly per code path.
  • Read attachment contents once. Attachment reads are streamed — the v1.25.1 fix addressed repeated contents() calls leaving saved files empty. If you need bytes twice, buffer them or save() to disk first and work from the file.
  • Message identity is UID + folder (v1.18.4) — the same UID in two folders is two different messages. But UIDs are only stable within a UIDVALIDITY generation, and a mailbox re-creation invalidates every stored UID. Own that in your schema: store folder + UID + a UIDVALIDITY value you track yourself, and be prepared to full-resync when it changes. The library won't do this bookkeeping for you.
  • Pin exact versions. The changelog is a stream of genuinely important correctness fixes — 8-bit response bytes, literal continuation handling, multi-line responses, RFC 2231 MIME parameter decoding, tokenizer OOM on malformed input. That's a good sign, but it means upgrading can change parsing behavior on your real mail. Pin, read release notes, re-run integration tests on every bump.
  • illuminate/collections version coupling. v1.25.5 swapped firstWhere() for Collection::first(...) specifically to avoid lower-version breakage. If your app pins an old illuminate/collections, check compatibility before adopting — this is a real conflict surface in older Laravel apps.
  • debug => true logs commands. Useful while integrating a new provider, dangerous in production: keep it behind an env flag, scrub logs, and never ship the connection dump anywhere near credential storage.
  • Flags accept BackedEnum|string (typed as of v1.15.4). Prefer an enum in your own code and convert at the boundary; keyword strings are case- and backslash-sensitive (\Seen, not Seen).
  • Server differences are the real support burden. SORT, IDLE, LARGER/SMALLER, quota commands, and BODY[HEADER]-first behavior (v1.18.2 was a server-compat workaround) are all capability-dependent. Feature-detect or gracefully fall back to fetch-order sorting; have a test account on Gmail, M365/Outlook, and Dovecot before you ship.
  • Connection lifecycle in long-running workers. Set a sane timeout, always disconnect() on shutdown, and wrap work in try/catch: connection failures now surface as a proper ImapConnectionFailedException (v1.22.4) rather than suppressed stream warnings — but they'll still blow up a job. Retry with backoff and treat repeated failures as an alertable state, not a silent skip.
  • Bound memory per job. Malformed-token OOM and 8-bit parsing are fixed, but no library saves you from fetching a 40MB message into a request. Use size() filters, process one message per job, and let the queue's memory limit be your safety net.
  • Security history worth knowing. v1.22.3 fixed command-injection in ID parameter escaping. Keep the version current, and never concatenate raw user input into search criteria or folder names — use the builder.
  • This is not a sender. No SMTP. Pair it with your existing mailer; don't try to shoehorn outbound into an IMAP client.
  • Extension points are thin by design: the config array (multiple named connections), Mailbox::make(), and the Laravel facade/factory. Everything else — persistence, attachment storage, retry policy, observability, provider migration — is your layer. Budget for it explicitly; the ~$0 license cost is not the total cost.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style