directorytree/imapengine
IMAPEngine is a Laravel-friendly PHP package for working with IMAP mailboxes. Connect to servers, list folders, fetch messages and attachments, search and manage mail, and handle common IMAP operations through a clean, high-level API.
Installation:
composer require directorytree/imapengine
Requires PHP 8.1+ and dependencies like symfony/mime and nesbot/carbon.
Basic Connection:
use DirectoryTree\ImapEngine\Client;
use DirectoryTree\ImapEngine\Connection;
$connection = new Connection('imap.example.com', [
'username' => 'user@example.com',
'password' => 'password',
'ssl' => true,
]);
$client = new Client($connection);
First Use Case: Fetching Emails
$inbox = $client->getFolder('INBOX');
$messages = $inbox->search()->all();
foreach ($messages as $message) {
echo $message->subject . "\n";
}
Client: Main entry point for managing connections and folders.Folder: Represents an IMAP folder (e.g., INBOX, Sent).Message: Represents an email with lazy-loaded properties (headers, body, attachments).QueryBuilder: For searching messages with criteria like unread(), from('sender@example.com').$messages = $folder->search()->unread()->limit(10)->get();
foreach ($messages as $message) {
// Headers are loaded on-demand
echo $message->subject; // Triggers lazy load of headers
$message->text; // Triggers lazy load of text body
}
$folder->flag(['uid' => [1, 2, 3]], 'read');
$folder->delete(['uid' => [4, 5]]);
$query = $folder->search()
->unread()
->from('sender@example.com')
->since('2023-01-01')
->largerThan(1024); // Messages > 1KB
$messages = $query->get();
foreach ($message->attachments() as $attachment) {
$attachment->saveTo(storage_path("attachments/{$attachment->filename}"));
}
poll() to wait for new messages in a folder:
$folder->poll(function (Folder $folder) {
$newMessages = $folder->search()->since(now()->subMinute())->get();
// Process $newMessages
}, 30); // Timeout in seconds
SORT capability for efficient sorting:
$messages = $folder->search()
->unread()
->sort('date', 'desc')
->get();
Service Provider:
Bind Client to the container for dependency injection:
$this->app->singleton(Client::class, function ($app) {
$connection = new Connection(config('imap.host'), [
'username' => config('imap.username'),
'password' => config('imap.password'),
'ssl' => config('imap.ssl', true),
]);
return new Client($connection);
});
Queued Processing: Use Laravel queues to process large batches of emails asynchronously:
$messages = $folder->search()->unread()->limit(100)->get();
foreach ($messages as $message) {
ProcessEmail::dispatch($message);
}
Folder to trigger events (e.g., MessageReceived):
$folder->poll(function (Folder $folder) {
$newMessages = $folder->search()->since(now()->subMinute())->get();
event(new MessageReceived($newMessages));
});
ImapEngine's mockable design to test email logic:
$mockConnection = Mockery::mock(Connection::class);
$client = new Client($mockConnection);
Connection Timeouts:
poll() with a timeout or reconnect logic:
try {
$folder->poll($callback, 60);
} catch (ImapConnectionFailedException $e) {
$client->reconnect();
}
Lazy Loading Overhead:
withHeaders(), withText(), or withAttachments() explicitly:
$messages = $folder->search()->withHeaders()->limit(50)->get();
UTF-8 Encoding Issues:
from names) are decoded properly. Use $message->from->name after lazy loading.Attachment Handling:
.eml attachments for nested messages. Use $attachment->isEml() to filter:
$attachments = $message->attachments()->reject(fn ($a) => $a->isEml());
Query Builder Quirks:
sort()) requires the IMAP server to support the SORT extension. Fallback to client-side sorting if needed:
$messages = $folder->search()->sort('date', 'desc')->get()->sortByDesc('date');
Enable Verbose Logging:
$connection = new Connection('imap.example.com', [
'log' => true,
'logFile' => storage_path('logs/imap.log'),
]);
Check Raw Responses:
Spatie\Ray (dev dependency) to inspect raw IMAP responses:
Ray::dd($message->rawHeaders);
Common Exceptions:
ImapConnectionFailedException: Handle reconnection logic.ImapSearchException: Validate search criteria (e.g., since() dates).Custom Message Parsing:
DirectoryTree\ImapEngine\Message to add domain-specific logic:
class CustomMessage extends Message {
public function isPromotion(): bool {
return str_contains($this->subject, 'promo');
}
}
Query Builder Extensions:
$query->hasHeader('X-Custom-Header');
Extend QueryBuilder to support this:
public function hasHeader(string $header): self {
$this->criteria[] = "HEADER {$header}";
return $this;
}
Attachment Processing:
Attachment to add virus scanning or metadata extraction:
class SecureAttachment extends Attachment {
public function scanForViruses(): bool {
// Integrate with ClamAV or similar
}
}
Folder Events:
poll() callbacks or Laravel events.SSL/TLS:
$connection = new Connection('imap.example.com', [
'ssl' => true,
'port' => 993, // Default IMAPS port
]);
Authentication:
authMechanism for OAuth2 or other auth methods:
$connection = new Connection('imap.example.com', [
'authMechanism' => 'XOAUTH2',
'oauthToken' => $googleOAuthToken,
]);
Namespace Handling:
getFolder() with full paths (e.g., 'INBOX.Sent') if the server uses hierarchical folders.Batch Fetching:
$messages = $folder->search()->limit(100)->get();
while ($messages->count() > 0) {
// Process batch
$messages = $folder->search()->offset($messages->last()->uid)->limit(100)->get();
}
Lazy Load Selectively:
$messages = $folder->search()->withHeaders()->withText()->get();
Use UID for Operations:
How can I help you explore Laravel packages today?