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

v1.0.1 2025-12-10 00:49 UTC

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

  1. Features
  2. Requirements
  3. Installation
  4. Usage
  5. Field & Filter Reference
  6. Demo Application
  7. Error Handling
  8. Security
  9. Development & Testing
  10. Contributing
  11. Troubleshooting
  12. License

Packagist Version PHP Version Require PHPStan

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