| Recommend this page to a friend! |
| Info | Example | Demos | Reputation | Support forum | Blog | Links |
| Last Updated | Ratings | Unique User Downloads | Download Rankings | |||||
| 2025-12-18 (2 hours ago) | Not yet rated by the users | Total: Not yet counted | Not yet ranked | |||||
| Version | License | PHP version | Categories | |||
| php-ymap 1.0.1 | MIT/X Consortium ... | 8.1 | Email, Networking, Searching, PHP 8 |
| Description | Author | |
This package can retrieve email messages from an IMAP server. |
composer require yaijs/php-ymap
Quick Start:
<?php
require 'vendor/autoload.php';
use Yai\Ymap\ImapService;
// Connect to IMAP server $imap = new ImapService(
host: 'imap.example.com',
username: '[email protected]',
password: 'your-password',
port: 993,
ssl: true
);
// Fetch unread emails with specific fields $emails = $imap
->unseen()
->limit(10)
->fields(['uid', 'from', 'subject', 'date', 'body'])
->fetch();
// Mark email as read $imap->markAsRead(12345);
// Close connection $imap->close();
Requirements: PHP 8.1 or higher ext-imap ext-iconv ext-json ext-mbstring
Full Documentation: See the GitHub repository README for complete API documentation, advanced examples, and Vercel deployment guide.
<?php declare(strict_types=1); |
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.
> ? v1.0.2 - The AI Council Approved Edition! First IMAP library with unanimous approval from 5+ AI models (Grok 10/10, Gemini 3 Pro, Codex, DeepSeek, Claude). Now with connection abstraction layer (PHP 8.4 ready), memory-safe attachment streaming, and production benchmarks. See what's new ?
composer require yaijs/php-ymap
The package ships with PSR?4 autoloading (Yai\Ymap\*) and no global functions.
use Yai\Ymap\ConnectionConfig;
use Yai\Ymap\ImapClient;
$config = new ConnectionConfig(
'{imap.gmail.com:993/imap/ssl}INBOX',
'[email protected]',
'app-password'
);
$client = new ImapClient($config);
$client->connect();
foreach ($client->getUnreadUids() as $uid) {
$message = $client->fetchMessage($uid);
echo $message->getSubject();
$client->markAsRead($uid);
}
use Yai\Ymap\ImapService;
$messages = ImapService::create()
->connect('{imap.gmail.com:993/imap/ssl}INBOX', '[email protected]', 'app-password')
->fields(['uid', 'subject', 'from', 'date', 'textBody'])
->includeAttachmentContent(false) // enable later if you need binary payloads
->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";
}
use Yai\Ymap\ImapService;
$imap = new ImapService([
'connection' => [
'mailbox' => '{imap.gmail.com:993/imap/ssl}INBOX',
'username' => '[email protected]',
'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();
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',
'[email protected]',
'app-password'
);
echo 'Connection OK!';
} catch (ConnectionException $e) {
echo 'Failed: ' . $e->getMessage();
}
| 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 |
| size | Total message size (bytes) |
Use fields([...]) and/or excludeFields([...]) to tailor responses. uid is injected automatically.
Note on Attachments: The attachments field returns metadata by default. Payloads are decoded lazily to keep memory usage small, and you can opt into array payloads with includeAttachmentContent() or stream the bytes straight to disk with ImapClient::saveAttachmentTo().
| 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') | Result shaping |
| 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']);
$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.
php-ymap has been tested in production environments and optimized for enterprise use, including message queue workers and scheduled tasks.
Performance tested across three production IMAP servers:
| Provider | 10 msgs | 25 msgs | 50 msgs | 100 msgs | Avg/msg | |----------|---------|---------|---------|----------|---------| | ok.de | 1.05s | 2.25s | 4.65s | 7.79s | ~105ms | | IONOS | 2.30s | 5.83s | 12.57s | - | ~230ms | | Gmail | 3.43s | 6.12s | 11.86s | 22.62s | ~226ms |
Key Takeaways:
- Linear scaling up to 100 messages
- Handles 18MB+ datasets efficiently
- Suitable for scheduled tasks and background processing
- Memory-safe with proper FetchOptions configuration
Control exactly what gets loaded into memory to prevent exhaustion in long-running processes:
use Yai\Ymap\FetchOptions;
// Lightweight: Only metadata for inbox listings
$options = new FetchOptions(
includeTextBody: false,
includeHtmlBody: false,
includeAttachmentMetadata: true,
includeAttachmentContent: false // ? Critical for large attachments!
);
$messages = $service->getMessages($options);
Performance Impact: - 60-80% reduction in memory usage for list views - Prevents memory exhaustion with 50MB+ attachments - Ideal for scheduled tasks processing hundreds of emails
php-ymap is designed for seamless integration with modern PHP frameworks and DI containers:
// In your service configuration (e.g., services.xml, services.yaml)
<service id="YourApp\Service\EmailProcessorService">
<argument type="service" id="Yai\Ymap\ImapService"/>
</service>
// In your scheduled task or background job
class EmailProcessorTask
{
public function run(): void
{
$messages = $this->imapService
->fields(['uid', 'subject', 'from', 'preview'])
->limit(20) // Process in batches
->unreadOnly()
->getMessages();
foreach ($messages as $msg) {
// Process message...
$this->imapService->markAsRead($msg['uid']);
}
}
}
Best Practices for Background Processing:
1. Use limit() to process emails in batches (recommend 20-50)
2. Always set includeAttachmentContent: false unless needed
3. Use saveAttachmentTo() for files larger than 5MB
4. Register ImapService in DI container, not static calls
5. Handle ConnectionException gracefully to avoid task crashes
Attachments are decoded lazily so you only pay for what you touch. You can still grab bytes directly or stream them to disk without ever holding them in memory:
use Yai\Ymap\ImapClient;
use Yai\Ymap\ConnectionConfig;
$config = new ConnectionConfig(
'{imap.gmail.com:993/imap/ssl}INBOX',
'[email protected]',
'app-password'
);
$client = new ImapClient($config);
$client->connect();
$message = $client->fetchMessage(12345);
foreach ($message->getAttachments() as $attachment) {
// Stream directly to the filesystem (no giant strings in memory)
$client->saveAttachmentTo(
$message->getUid(),
$attachment,
'/tmp/' . $attachment->getFilename()
);
// Or access the content lazily
if ($attachment->getMimeType() === 'application/pdf') {
processPdf($attachment->getContent());
}
if ($attachment->isInline()) {
$contentId = $attachment->getContentId(); // For referencing in HTML
}
}
Opt-in when you truly need the payload:
$messages = ImapService::create()
->connect('{imap.gmail.com:993/imap/ssl}INBOX', '[email protected]', 'app-password')
->fields(['uid', 'subject', 'attachments'])
->includeAttachmentContent(true) // base64 by default
->getMessages();
foreach ($messages as $msg) {
foreach ($msg['attachments'] as $attachment) {
$binary = base64_decode($attachment['content']);
// ?
}
}
Note: Including attachment content in JSON responses can significantly increase response size. Enable it only when necessary or stream to disk with saveAttachmentTo() for very large files.
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.
php-ymap is built with testability in mind. The connection layer is fully abstracted via ImapConnectionInterface, making it easy to mock for tests.
use Yai\Ymap\ImapClient;
use Yai\Ymap\Connection\ImapConnectionInterface;
// Create a mock connection (PHPUnit example)
$mockConnection = $this->createMock(ImapConnectionInterface::class);
$mockConnection->method('search')
->willReturn([1, 2, 3]);
// Inject into ImapClient
$client = new ImapClient($config, connection: $mockConnection);
// Now $client->searchUIDs() returns mocked data
use Yai\Ymap\ImapService;
use Yai\Ymap\ImapClientInterface;
$service = ImapService::create()
->connect('{imap.host:993/imap/ssl}INBOX', '[email protected]', 'secret')
->useClient($container->get(ImapClientInterface::class));
You can also call withClientFactory() to inject a factory that builds clients per connection config.
The ImapConnectionInterface abstraction prepares php-ymap for PHP 8.4, when ext-imap moves to PECL:
use Yai\Ymap\Connection\ExtImapConnection; // Current: wraps ext-imap
use Yai\Ymap\Connection\SocketImapConnection; // Future: pure PHP (v2.0)
// v1.x: Uses ext-imap by default
$client = new ImapClient($config); // Uses ExtImapConnection
// v2.0: Auto-detect or manual override
$client = new ImapClient($config, connection: new SocketImapConnection());
Current implementations:
- ExtImapConnection - Wraps native PHP imap_* functions (default)
- Custom implementations welcome via ImapConnectionInterface
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
}
// Optional: capture per-message failures instead of silently skipping
$imap->onError(static function (int $uid, \Throwable $e): void {
error_log(sprintf('Failed to fetch UID %d: %s', $uid, $e->getMessage()));
});
ImapService::disconnect() lets you explicitly close the IMAP stream ($imap->disconnect(true) to expunge).
Important: Never hardcode IMAP credentials in your source code.
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', '[email protected]', 'password')
->getMessages();
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'
Always sanitize attachment filenames to prevent path traversal attacks:
function sanitizeFilename(string $filename): string {
// Remove path traversal attempts
$filename = basename($filename);
// Remove dangerous characters
$filename = preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename);
// Prevent hidden files
$filename = ltrim($filename, '.');
return $filename ?: 'attachment.bin';
}
// Use it when saving attachments
foreach ($message->getAttachments() as $attachment) {
$safeName = sanitizeFilename($attachment->getFilename());
$client->saveAttachmentTo($message->getUid(), $attachment, "/secure/path/{$safeName}");
}
Memory Safety:
For attachments larger than your memory_limit, always use saveAttachmentTo() which streams directly to disk without loading into memory.
For detailed security guidelines, vulnerability reporting, and best practices, see SECURITY.md.
composer install
./vendor/bin/phpstan analyse src/
# (optional) ./vendor/bin/phpunit
No additional tooling is required. PHPStan level is configured in phpstan.neon.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines on:
For security vulnerabilities, please see our Security Policy instead of opening a public issue.
| 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 |
MIT
| File | Role | Description | ||
|---|---|---|---|---|
| Data | Auxiliary data | |||
| Data | Auxiliary data | |||
| Data | Auxiliary data | |||
| Data | Auxiliary data | |||
| Lic. | License text | |||
| Data | Auxiliary data | |||
| Data | Auxiliary data | |||
| Doc. | Documentation | |||
| Data | Auxiliary data | |||
| Data | Auxiliary data | |||
| / | example |
| File | Role | Description |
|---|---|---|
| |
Icon | Icon image |
| |
Example | Example script |
| |
Aux. | Auxiliary script |
| / | src |
| File | Role | Description | ||
|---|---|---|---|---|
| |
Class | Class source | ||
| |
Class | Class source | ||
| |
Class | Class source | ||
| |
Class | Class source | ||
| |
Class | Class source | ||
| |
Class | Class source | ||
| |
Class | Class source | ||
| |
Class | Class source | ||
| |
Class | Class source | ||
| / | src | / | Connection |
| File | Role | Description |
|---|---|---|
| |
Class | Class source |
| |
Class | Class source |
| / | src | / | Exceptions |
| File | Role | Description |
|---|---|---|
| |
Class | Class source |
| |
Class | Class source |
| |
Class | Class source |
| / | tests |
| File | Role | Description |
|---|---|---|
| |
Class | Class source |
| |
Class | Class source |
| |
Class | Class source |
| The PHP Classes site has supported package installation using the Composer tool since 2013, as you may verify by reading this instructions page. |
| Version Control | Unique User Downloads | |||||||
| 100% |
|
| Applications that use this package |
If you know an application of this package, send a message to the author to add a link here.