PHPackages                             yaijs/php-ymap - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. yaijs/php-ymap

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

yaijs/php-ymap
==============

Lightweight IMAP reader/flagger for PHP 8.1+

v1.0.3(3mo ago)12MITPHPPHP ^8.1CI passing

Since Dec 9Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/yaijs/php-ymap)[ Packagist](https://packagist.org/packages/yaijs/php-ymap)[ RSS](/packages/yaijs-php-ymap/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (2)Versions (5)Used By (0)

php-ymap
========

[](#php-ymap)

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
-----------------

[](#table-of-contents)

1. [Features](#features)
2. [Requirements](#requirements)
3. [Installation](#installation)
4. [Usage](#usage)
    - [Low-Level Client](#low-level-client)
    - [ImapService Fluent API](#imapservice-fluent-api)
    - [Array / Config-Driven Setup](#array--config-driven-setup)
    - [Connection Options](#connection-options)
5. [Field &amp; Filter Reference](#field--filter-reference)
6. [Performance &amp; Production Readiness](#performance--production-readiness)
    - [Real-World Benchmarks](#real-world-benchmarks)
    - [Memory Optimization with FetchOptions](#memory-optimization-with-fetchoptions)
    - [Shopware 6.7 Integration](#shopware-67-integration)
7. [Advanced Usage](#advanced-usage)
8. [Demo Application](#demo-application)
9. [Error Handling](#error-handling)
10. [Security](#security)
11. [Development &amp; Testing](#development--testing)
12. [Contributing](#contributing)
13. [Troubleshooting](#troubleshooting)
14. [License](#license)

---

[![Packagist Version](https://camo.githubusercontent.com/0bfe91808f4e3138cc06a845a7ad5e97c8f885c67cecb6a9f65786a4b3a96fa2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7961696a732f7068702d796d61702e737667)](https://packagist.org/packages/yaijs/php-ymap)[![PHP Version Require](https://camo.githubusercontent.com/9eeca11e5d40ca4550ee0f716176da40ea00660b45d257f9af96af70aea70601/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f7961696a732f7068702d796d61702e737667)](https://packagist.org/packages/yaijs/php-ymap)[![PHPStan](https://camo.githubusercontent.com/f60d96f7c2579690ab6dfa8918f777fe93a02a92301c661eb38a85861a92b780/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230382d627269676874677265656e2e7376673f7374796c653d666c6174)](https://phpstan.org/)[![AI Approved](https://camo.githubusercontent.com/91d987cc99290d3c521ddff79049d4e93999b01f3249a28cc27458942309f520/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4149253230436f756e63696c2d3525324635253230417070726f7665642d737563636573732e7376673f7374796c653d666c6174)](v1.0.3.md)

> **🏆 v1.0.3 - Socket Mode Stabilized!** php-ymap now runs reliably without `ext-imap`, including restored multipart body parsing and persisted read/answered flags in real inbox workflows. [See what's new →](CHANGELOG.md#103---2026-02-15)

Features
--------

[](#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
- 🚀 **Production-ready** – Memory-safe attachment streaming, tested on Gmail/ok.de/IONOS
- 🧪 **Testable** – Dependency injection support for mocking in unit tests

---

Requirements
------------

[](#requirements)

- PHP 8.1+
- Required extensions: mbstring, iconv, JSON
- Optional extension: `ext-imap` (for explicit `ExtImapConnection` usage)
- Default runtime connector: pure PHP sockets (`SocketsImapConnection`)

---

Installation
------------

[](#installation)

```
composer require yaijs/php-ymap
```

The package ships with PSR‑4 autoloading (`Yai\Ymap\*`) and no global functions.

---

Usage
-----

[](#usage)

### Low-Level Client

[](#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

[](#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'])
    ->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";
}
```

### Array / Config-Driven Setup

[](#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

[](#connection-options)

`ImapService::connect()` (and the `connection` config section) keep `imap_open()`-compatible parameters so existing configs continue to work in both connector modes:

OptionDescription`mailbox`IMAP path, e.g. `{imap.gmail.com:993/imap/ssl}INBOX``username`, `password`Credentials or app password`options``imap_open()` bitmask compatibility field`retries`Retry count compatibility field`parameters`Optional connector parameters (TLS/auth behavior where supported)`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 &amp; Filter Reference
----------------------------

[](#field--filter-reference)

### Available Fields

[](#available-fields)

FieldDescription`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()`.

### Filter Methods

[](#filter-methods)

MethodIMAP 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

[](#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.

---

Performance &amp; Production Readiness
--------------------------------------

[](#performance--production-readiness)

php-ymap has been tested in production environments and optimized for enterprise use, including message queue workers and scheduled tasks.

### Real-World Benchmarks

[](#real-world-benchmarks)

Performance tested across three production IMAP servers in both connector modes.

**ext-imap mode (reference):**

Provider10 msgs25 msgs50 msgs100 msgsAvg/msg**ok.de**1.05s2.25s4.65s7.79s~105ms**IONOS**2.30s5.83s12.57s-~230ms**Gmail**3.43s6.12s11.86s22.62s~226ms**Socket mode (default in v1.0.3):**

Provider10 msgs25 msgs50 msgs100 msgsAvg/msg**ok.de**1.4282s3.0770s5.8331s11.3149s~113ms**IONOS**3.5413s5.8178s16.9986s-~340ms**Gmail**5.7557s13.0100s22.1389s37.4546s~375ms*Note: IONOS mailbox had 61 messages, so no 100-message run in either mode.*

**Key Takeaways:**

- Socket mode remains stable without `ext-imap` across tested providers
- Throughput is lower in socket mode (expected in pure PHP transport)
- Linear scaling up to 100 messages where mailbox size allows it
- Handles 18MB+ datasets efficiently
- Suitable for scheduled tasks and background processing
- Memory-safe with proper `FetchOptions` configuration

### Memory Optimization with FetchOptions

[](#memory-optimization-with-fetchoptions)

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

### Plugin Integration Example

[](#plugin-integration-example)

php-ymap is designed for seamless integration with modern PHP frameworks and DI containers:

```
// In your service configuration (e.g., services.xml, services.yaml)

// 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

---

Advanced Usage
--------------

[](#advanced-usage)

### Working with Attachment Content

[](#working-with-attachment-content)

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',
    'user@example.com',
    '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
    }
}
```

### Including Attachment Content in JSON APIs

[](#including-attachment-content-in-json-apis)

Opt-in when you truly need the payload:

```
$messages = ImapService::create()
    ->connect('{imap.gmail.com:993/imap/ssl}INBOX', 'user@example.com', '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.

---

Demo Application
----------------

[](#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](https://yaijs.github.io/yai/docs/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.

---

### Dependency Injection &amp; Testing

[](#dependency-injection--testing)

php-ymap is built with testability in mind. The connection layer is fully abstracted via `ImapConnectionInterface`, making it easy to mock for tests.

#### Mock Connection for Unit Tests

[](#mock-connection-for-unit-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
```

#### Swap IMAP Transport at Service Level

[](#swap-imap-transport-at-service-level)

```
use Yai\Ymap\ImapService;
use Yai\Ymap\ImapClientInterface;

$service = ImapService::create()
    ->connect('{imap.host:993/imap/ssl}INBOX', 'user@example.com', 'secret')
    ->useClient($container->get(ImapClientInterface::class));
```

You can also call `withClientFactory()` to inject a factory that builds clients per connection config.

#### Connector Selection (PHP 8.4+ Ready)

[](#connector-selection-php-84-ready)

The `ImapConnectionInterface` abstraction keeps php-ymap portable across environments with and without `ext-imap`:

```
use Yai\Ymap\Connection\ExtImapConnection;      // Optional: wraps ext-imap
use Yai\Ymap\Connection\SocketsImapConnection;  // Default: pure PHP socket connector

// Default in v1.0.3+: socket connector
$client = new ImapClient($config); // Uses SocketsImapConnection

// Optional override to native extension connector
$client = new ImapClient($config, connection: new ExtImapConnection());
```

**Current implementations:**

- `SocketsImapConnection` - Pure PHP socket implementation (default)
- `ExtImapConnection` - Wraps native PHP `imap_*` functions (optional)
- Custom implementations welcome via `ImapConnectionInterface`

---

Error Handling
--------------

[](#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
}

// 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).

---

Security
--------

[](#security)

**Important:** Never hardcode IMAP credentials in your source code.

### Secure Credential Management

[](#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

[](#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

[](#additional-security-practices)

- **Limit result sets** to prevent resource exhaustion (`->limit(100)`)
- **Sanitize filenames** before saving attachments to disk (see example below)
- **Validate MIME types** when processing attachments
- **Implement rate limiting** for web-facing IMAP operations
- **Use field selection** to minimize data exposure (`->fields(['uid', 'subject'])`)
- **Stream large attachments** to prevent memory exhaustion attacks

### Secure Attachment Handling

[](#secure-attachment-handling)

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](SECURITY.md).

---

Development &amp; Testing
-------------------------

[](#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
------------

[](#contributing)

Contributions are welcome! Please see [CONTRIBUTING.md](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](SECURITY.md) instead of opening a public issue.

---

Troubleshooting
---------------

[](#troubleshooting)

IssueHint“Can't connect to mailbox”Double-check mailbox path, host firewall, TLS flags, and credentialsGmail authentication failsUse an [App Password](https://support.google.com/accounts/answer/185833); basic auth is blockedEmpty `textBody`Some emails are HTML-only – read `htmlBody` or strip tags yourself (see example app)Self-signed certsProvide stream context via `parameters` (e.g. `['DISABLE_AUTHENTICATOR' => 'PLAIN']`, or TLS context)Need ext-imap anyway`sudo apt install php8.2-imap && sudo phpenmod imap` (optional, not required for default socket mode)---

License
-------

[](#license)

MIT. Portions of the IMAP protocol implementation and message helpers are derived from the MIT-licensed `Webklex/php-imap` and `ddeboer/imap` projects; see `THIRD_PARTY_LICENSES.md` for the preserved upstream notices you must include when redistributing this library or products that bundle it.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance82

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~22 days

Total

4

Last Release

92d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a70df0441c2dc68c5c4d9ae414d4a0a1f4374a0f5ac36bfa8c9499a2779801b5?d=identicon)[eypsilon](/maintainers/eypsilon)

---

Top Contributors

[![eypsilon](https://avatars.githubusercontent.com/u/27016183?v=4)](https://github.com/eypsilon "eypsilon (22 commits)")

---

Tags

example-codefacade-patternimapimap-clientimaplibliveviewphpphp8

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/yaijs-php-ymap/health.svg)

```
[![Health](https://phpackages.com/badges/yaijs-php-ymap/health.svg)](https://phpackages.com/packages/yaijs-php-ymap)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
