yaijs / php-ymap
Lightweight IMAP reader/flagger for PHP 8.1+
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/yaijs/php-ymap
Requires
- php: ^8.1
- ext-iconv: *
- ext-imap: *
- ext-json: *
- ext-mbstring: *
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2025-12-11 04:04:47 UTC
README
A lightweight fluent IMAP client for PHP 8.1+. Decode bodies, attachments, and headers, filter in one call, toggle flags, and preview everything via the included UI demo.
Table of Contents
- Features
- Requirements
- Installation
- Usage
- Field & Filter Reference
- Demo Application
- Error Handling
- Security
- Development & Testing
- Contributing
- Troubleshooting
- License
Features
- 🔌 Simple connection – configure once with an array or chain fluent setters
- 📬 Full message parsing – text/HTML bodies, decoded attachments, cleaned headers
- 🔍 Flexible filtering – IMAP-level search plus post-fetch “exclude” filters
- 🎯 Field selection – fetch only what you need (UIDs, bodies, addresses, attachments…)
- ✉️ Flag helpers – mark messages read/unread/answered in a single call
- 🧱 Encodings handled – charset conversion and proper multipart parsing baked in
- 🖥️ Demo UI – modern HTML frontend for manual testing and QA
Requirements
- PHP 8.1+
- Extensions: IMAP, mbstring, iconv, JSON
- Enable IMAP on Ubuntu/Debian:
sudo apt install php8.2-imap && sudo phpenmod imap
Installation
composer require yaijs/php-ymap
The package ships with PSR‑4 autoloading (Yai\Ymap\*) and no global functions.
Usage
Low-Level Client
use Yai\Ymap\ConnectionConfig; use Yai\Ymap\ImapClient; $config = new ConnectionConfig( '{imap.gmail.com:993/imap/ssl}INBOX', 'user@example.com', 'app-password' ); $client = new ImapClient($config); $client->connect(); foreach ($client->getUnreadUids() as $uid) { $message = $client->fetchMessage($uid); echo $message->getSubject(); $client->markAsRead($uid); }
ImapService Fluent API
use Yai\Ymap\ImapService; $messages = ImapService::create() ->connect('{imap.gmail.com:993/imap/ssl}INBOX', 'user@example.com', 'app-password') ->fields(['uid', 'subject', 'from', 'date', 'textBody']) ->since('2024-01-01') ->unreadOnly() ->excludeFrom(['noreply@', 'newsletter@']) ->limit(20) ->orderBy('desc') ->getMessages(); foreach ($messages as $msg) { echo "{$msg['subject']} from {$msg['from'][0]['email']}\n"; }
Array / Config-Driven Setup
use Yai\Ymap\ImapService; $imap = new ImapService([ 'connection' => [ 'mailbox' => '{imap.gmail.com:993/imap/ssl}INBOX', 'username' => 'user@example.com', 'password' => 'app-password', 'options' => 0, 'retries' => 3, 'parameters' => [ 'DISABLE_AUTHENTICATOR' => 'GSSAPI', ], ], 'fields' => ['uid', 'subject', 'from', 'date', 'textBody'], 'filters' => [ 'limit' => 10, 'since' => '2024-01-01', 'unread' => true, ], 'exclude' => [ 'from' => ['noreply@', 'newsletter@'], 'subject_contains' => ['Unsubscribe', 'Digest'], ], ]); $messages = $imap->getMessages();
Connection Options
ImapService::connect() (and the connection config section) accept the same parameters that PHP’s imap_open() does:
| Option | Description |
|---|---|
mailbox |
IMAP path, e.g. {imap.gmail.com:993/imap/ssl}INBOX |
username, password |
Credentials or app password |
options |
Bitmask passed to imap_open() |
retries |
Retry count for imap_open() |
parameters |
Associative array passed to imap_open() (set TLS context, disable authenticators, etc.) |
encoding |
Target encoding for decoded bodies (default UTF-8) |
Need a lightweight “Test Credentials” button? Call the static helper:
use Yai\Ymap\ImapService; use Yai\Ymap\Exceptions\ConnectionException; try { ImapService::testConnection( '{imap.gmail.com:993/imap/ssl}INBOX', 'user@example.com', 'app-password' ); echo 'Connection OK!'; } catch (ConnectionException $e) { echo 'Failed: ' . $e->getMessage(); }
Field & Filter Reference
Available Fields
| Field | Description |
|---|---|
uid |
Message UID (always included) |
subject |
Decoded subject |
date, dateRaw |
Formatted string (Y-m-d H:i:s) or original DateTimeImmutable|null |
from, to, cc, bcc, replyTo |
Address arrays (email + optional name) |
textBody, htmlBody |
Plain text and HTML bodies (decoded, concatenated per part) |
preview |
Plain text summary (auto-generated from text or stripped HTML) |
attachments |
Filename, MIME type, size (inline + regular attachments) |
headers |
Normalized header map |
seen, answered |
Boolean flags mirrored from IMAP |
Use fields([...]) and/or excludeFields([...]) to tailor responses. uid is injected automatically.
Note on Attachments: The attachments field returns metadata by default (filename, size, MIME type). Full binary content is automatically fetched and decoded, accessible via $attachment->getContent() when working with Message objects directly. For JSON APIs, you can include base64-encoded content if needed (see Advanced Usage below).
Filter Methods
| Method | IMAP Criteria |
|---|---|
since($date) |
SINCE |
before($date) |
BEFORE (inclusive) |
unreadOnly() / readOnly() |
UNSEEN / SEEN |
from($email) / to($email) |
FROM / TO |
subjectContains($text) |
SUBJECT |
bodyContains($text) |
BODY |
limit($n), `orderBy('asc' |
'desc')` |
answeredOnly(), unansweredOnly() |
ANSWERED / UNANSWERED |
Post-fetch exclusions (evaluated after message parsing) help drop noisy senders or subjects:
$imap->excludeFrom(['noreply@', 'quora.com']) ->excludeSubjectContains(['Unsubscribe', 'Digest']);
Flag Helpers
$imap->markAsRead([1234, 1235]); $imap->markAsUnread(1236); $imap->markAsAnswered(1237); $imap->markAsUnanswered(1238);
Under the hood this proxies to imap_setflag_full() / imap_clearflag_full() using UIDs.
Advanced Usage
Working with Attachment Content
Attachments are automatically fetched and fully decoded. Access binary content directly:
use Yai\Ymap\ImapClient; use Yai\Ymap\ConnectionConfig; $config = new ConnectionConfig( '{imap.gmail.com:993/imap/ssl}INBOX', 'user@example.com', 'app-password' ); $client = new ImapClient($config); $client->connect(); $message = $client->fetchMessage(12345); foreach ($message->getAttachments() as $attachment) { // Save attachment to disk file_put_contents( '/tmp/' . $attachment->getFilename(), $attachment->getContent() ); // Or process directly if ($attachment->getMimeType() === 'application/pdf') { processPdf($attachment->getContent()); } // Check if it's inline (embedded image) if ($attachment->isInline()) { $contentId = $attachment->getContentId(); // For referencing in HTML } }
Including Attachment Content in JSON APIs
For API responses, base64-encode the content:
$messages = $imap->getMessages(); $formatted = array_map(function($msg) { return [ 'subject' => $msg['subject'], 'attachments' => array_map(function($att) { return [ 'filename' => $att['filename'], 'mimeType' => $att['mimeType'], 'size' => $att['size'], 'content' => base64_encode($att['content']), // Include binary content ]; }, $msg['attachments']), ]; }, $messages); echo json_encode($formatted);
Note: Including attachment content in JSON responses can significantly increase response size. Consider fetching attachments on-demand for large files.
Demo Application
Run the bundled dashboard to experiment with filters and see real responses:
cd php-ymap/example php -S localhost:8000 # open http://localhost:8000
The frontend (built with YEH) posts to get.php, which uses ImapService exclusively. The JSON API is a good reference if you want to plug php-ymap into another UI.
Error Handling
use Yai\Ymap\Exceptions\ConnectionException; use Yai\Ymap\Exceptions\MessageFetchException; try { $messages = $imap->getMessages(); } catch (ConnectionException $e) { // Invalid credentials, TLS failure, server unreachable, etc. } catch (MessageFetchException $e) { // Individual message could not be parsed/fetched }
ImapService::disconnect() lets you explicitly close the IMAP stream ($imap->disconnect(true) to expunge).
Security
Important: Never hardcode IMAP credentials in your source code.
Secure Credential Management
use Yai\Ymap\ImapService; // âś“ Good: Use environment variables $messages = ImapService::create() ->connect( getenv('IMAP_MAILBOX'), getenv('IMAP_USER'), getenv('IMAP_PASS') ) ->getMessages(); // âś— Bad: Hardcoded credentials $messages = ImapService::create() ->connect('{imap.gmail.com:993/imap/ssl}INBOX', 'user@example.com', 'password') ->getMessages();
Secure Connections
Always use SSL/TLS when connecting over untrusted networks:
// ✓ Good: SSL enabled '{imap.gmail.com:993/imap/ssl}INBOX' // ⚠️ Warning: Disables certificate validation (development only) '{imap.example.com:993/imap/ssl/novalidate-cert}INBOX'
Additional Security Practices
- Limit result sets to prevent resource exhaustion (
->limit(100)) - Sanitize filenames before saving attachments to disk
- Validate MIME types when processing attachments
- Implement rate limiting for web-facing IMAP operations
- Use field selection to minimize data exposure (
->fields(['uid', 'subject']))
For detailed security guidelines, vulnerability reporting, and best practices, see SECURITY.md.
Development & Testing
composer install
./vendor/bin/phpstan analyse src/
# (optional) ./vendor/bin/phpunit
No additional tooling is required. PHPStan level is configured in phpstan.neon.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines on:
- Code standards and style (PHP 8.1+, strict typing, PHPStan level 8)
- Pull request process
- What to contribute (bug fixes, docs, tests, performance improvements)
- How to report issues
For security vulnerabilities, please see our Security Policy instead of opening a public issue.
Troubleshooting
| Issue | Hint |
|---|---|
| “Can't connect to mailbox” | Double-check mailbox path, host firewall, and that the IMAP extension is enabled |
| Gmail authentication fails | Use an App Password; basic auth is blocked |
Empty textBody |
Some emails are HTML-only – read htmlBody or strip tags yourself (see example app) |
| Self-signed certs | Provide stream context via parameters (e.g. ['DISABLE_AUTHENTICATOR' => 'PLAIN'], or TLS context) |
| Extension missing | sudo apt install php8.2-imap && sudo phpenmod imap |
License
MIT