phpdot / imap
Enterprise-grade IMAP4rev1/IMAP4rev2 protocol library for PHP
v2.2.0
2026-03-27 00:15 UTC
Requires
- php: >=8.2
- ext-mbstring: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
README
IMAP4rev1/IMAP4rev2 protocol library for PHP. Client and server.
Install
composer require phpdot/imap
Requires PHP 8.2+.
Client
use PHPdot\IMAP\ImapClient; $client = new ImapClient('imap.gmail.com', 993, 'ssl'); $client->connect(); $client->login('user@gmail.com', 'app-password'); $inbox = $client->select('INBOX'); echo $inbox->exists . " messages\n"; $messages = $client->fetch('1:10', ['FLAGS', 'ENVELOPE']); foreach ($messages as $msg) { echo $msg->envelope->subject . "\n"; } $unseen = $client->search('UNSEEN'); $client->store('1:3', '+FLAGS', ['\\Seen']); $folders = $client->listMailboxes(); $status = $client->status('INBOX', ['MESSAGES', 'UNSEEN']); $client->idle(function ($notification) { if ($notification->isNewMessage()) { echo "New mail! " . $notification->number . " messages\n"; } return true; // keep listening, return false to stop }); $client->logout();
Custom transport
use PHPdot\IMAP\ImapClient; // Default: StreamTransport (PHP stream_socket_client) $client = new ImapClient('host', 993, 'ssl'); // Custom: Swoole, ReactPHP, or anything implementing ClientTransportInterface $client = new ImapClient('host', 993, 'ssl', new SwooleTransport());
Server
use PHPdot\IMAP\ImapHandler; use PHPdot\IMAP\Connection\ServerConnection; use PHPdot\IMAP\Connection\ConnectionContext; use PHPdot\IMAP\Result\SelectResult; $handler = new ImapHandler(); $handler->onLogin(function (string $user, string $pass, ConnectionContext $ctx): bool { return $user === 'omar' && $pass === 'secret'; }); $handler->onSelect(function (string $mailbox, ConnectionContext $ctx): SelectResult { return new SelectResult(exists: 172, uidValidity: 38505, uidNext: 4392); }); $handler->onFetch(function ($fetchCommand, ConnectionContext $ctx): array { // query your storage, return list<FetchResult> return []; });
Wire to any runtime
// Swoole $swoole->on('connect', function ($srv, $fd) use ($handler) { $conn = new ServerConnection($handler); $connections[$fd] = $conn; $srv->send($fd, $conn->greeting()); }); $swoole->on('receive', function ($srv, $fd, $r, $data) use (&$connections) { foreach ($connections[$fd]->onData($data) as $response) { $srv->send($fd, $response); } }); // Workerman, ReactPHP, Amp — same pattern
What's Covered
Every command and response from RFC 9051 (IMAP4rev2) and RFC 3501 (IMAP4rev1):
- 35+ commands: LOGIN, AUTHENTICATE, SELECT, FETCH, SEARCH, STORE, COPY, MOVE, APPEND, EXPUNGE, LIST, STATUS, IDLE, ENABLE, NAMESPACE, ID, COMPRESS, QUOTA, all UID variants
- All data types: atoms, quoted strings, literals, literal8, NIL, lists, sequence sets, sections, partials
- All responses: ENVELOPE, BODYSTRUCTURE, FETCH, ESEARCH, BINARY, APPENDUID, COPYUID, FLAGS, CAPABILITY, 37 response codes
- Extensions: CONDSTORE, COMPRESS, QUOTA, ID, SPECIAL-USE, XLIST
Quality
declare(strict_types=1)on every file- PHPStan max level, zero errors, no ignores
- 446 tests, 1156 assertions
- Zero runtime dependencies
- Transport agnostic (works with Swoole, ReactPHP, Workerman, Amp, plain PHP)
License
MIT