PHP Classes

How to use a PHP IMAP library to search and retrieve messages in a mailbox using the package PHP Ymap: Retrieve email messages from an IMAP server

Recommend this page to a friend!
  Info   Example   Demos   View files Files   Install with Composer Install with Composer   Download Download   Reputation   Support forum   Blog    
Last Updated Ratings Unique User Downloads Download Rankings
2025-12-18 (2 hours ago) RSS 2.0 feedNot yet rated by the usersTotal: Not yet counted Not yet ranked
Version License PHP version Categories
php-ymap 1.0.1MIT/X Consortium ...8.1Email, Networking, Searching, PHP 8
Description 

Author

This package can retrieve email messages from an IMAP server.

It provides a class that provides a fluent interface to retrieve messages in a mailbox accessed using the IMAP protocol.

Currently, it can:

- Fetch messages

- Mark messages as read, as unread, as answered

- Set and clear flags

- Searching for messages

- Get unread messages

- Convert HTML messages to text

Picture of Engin Ypsilon
  Performance   Level  
Name: Engin Ypsilon <contact>
Classes: 8 packages by
Country: Germany Germany
Age: 45
All time rank: 4106221 in Germany Germany
Week rank: 195 Up8 in Germany Germany Up
Innovation award
Innovation award
Nominee: 3x

Winner: 1x

Instructions

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

Example

<?php declare(strict_types=1);

/**
 * IMAP API Endpoint - Powered by ImapService
 */

use Yai\Ymap\ImapService;
use
Yai\Ymap\Exceptions\ConnectionException;

error_reporting(E_ALL);

header('Content-Type: application/json');

require_once
dirname(__DIR__) . '/vendor/autoload.php';

if (
$_SERVER['REQUEST_METHOD'] !== 'POST') {
   
http_response_code(405);
    echo
json_encode(['success' => false, 'error' => 'Method not allowed']);
    exit;
}

$messageUid = isset($_GET['message']) ? (int) $_GET['message'] : null;
if (
null !== $messageUid && $messageUid <= 0) {
   
$messageUid = null;
}
$messageAction = $_GET['action'] ?? null;

$bodyLength = (int) ($_POST['body_length'] ?? 500);
if (
$bodyLength < 100) {
   
$bodyLength = 100;
} elseif (
$bodyLength > 20000) {
   
$bodyLength = 20000;
}

$username = trim($_POST['username'] ?? '');
$password = trim($_POST['password'] ?? '');

if (!
$username || !$password) {
   
http_response_code(400);
    echo
json_encode(['success' => false, 'error' => 'Username and password required']);
    exit;
}

try {
   
$imap = ImapService::create()
        ->
connect(
           
$_POST['mailbox'] ?: '{imap.gmail.com:993/imap/ssl}INBOX',
           
$username,
           
$password
       
)
        ->
fields([
           
'uid',
           
'subject',
           
'from',
           
'to',
           
'cc',
           
'replyTo',
           
'date',
           
'textBody',
           
'htmlBody',
           
'attachments',
           
'seen',
           
'answered',
           
'size',
           
'preview',
        ])
        ->
limit((int) ($_POST['limit'] ?? 10))
        ->
orderBy('desc');

   
// Add optional filters
   
if (!empty($_POST['date_from'])) {
       
$imap->since($_POST['date_from']);
    }
    if (!empty(
$_POST['date_to'])) {
       
$imap->before($_POST['date_to']);
    }
    if ((
$_POST['read_status'] ?? '') === 'UNREAD') {
       
$imap->unreadOnly();
    } elseif ((
$_POST['read_status'] ?? '') === 'READ') {
       
$imap->readOnly();
    }
    if ((
$_POST['answered_status'] ?? '') === 'ANSWERED') {
       
$imap->answeredOnly();
    } elseif ((
$_POST['answered_status'] ?? '') === 'UNANSWERED') {
       
$imap->unansweredOnly();
    }
    if (!empty(
$_POST['search_text'])) {
       
match ($_POST['search_field'] ?? 'SUBJECT') {
           
'FROM' => $imap->from($_POST['search_text']),
           
'TO' => $imap->to($_POST['search_text']),
           
'BODY', 'TEXT' => $imap->bodyContains($_POST['search_text']),
            default =>
$imap->subjectContains($_POST['search_text']),
        };
    }

   
// Exclusion filters from textareas (one pattern per line)
   
if (!empty($_POST['exclude_from'])) {
       
$patterns = array_filter(array_map('trim', explode("\n", $_POST['exclude_from'])));
        if (
$patterns) {
           
$imap->excludeFrom($patterns);
        }
    }
    if (!empty(
$_POST['exclude_subject'])) {
       
$patterns = array_filter(array_map('trim', explode("\n", $_POST['exclude_subject'])));
        if (
$patterns) {
           
$imap->excludeSubjectContains($patterns);
        }
    }

   
$mapAddresses = static function (array $addresses): array {
       
$result = [];

        foreach (
$addresses as $entry) {
            if (
is_array($entry)) {
               
$result[] = [
                   
'email' => (string) ($entry['email'] ?? ''),
                   
'name' => $entry['name'] ?? null,
                ];
                continue;
            }

           
$value = (string) $entry;
            if (
preg_match('/^(.*)<([^>]+)>$/', $value, $matches)) {
               
$result[] = [
                   
'email' => trim($matches[2]),
                   
'name' => trim($matches[1]) ?: null,
                ];
            } else {
               
$result[] = [
                   
'email' => $value,
                   
'name' => null,
                ];
            }
        }

        return
$result;
    };

   
$formatMessage = static function (array $msg) use ($bodyLength, $mapAddresses) {
       
// Use the built-in preview which handles text/HTML fallback and whitespace
       
$body = $msg['preview'] ?? '';
       
$bodyPreview = mb_substr($body, 0, $bodyLength);
       
$isTruncated = mb_strlen($body) > $bodyLength;

       
$size = (int) ($msg['size'] ?? 0);
        if (
$size >= 1048576) {
           
$sizeFormatted = number_format($size / 1048576, 1) . ' MB';
        } elseif (
$size >= 1024) {
           
$sizeFormatted = number_format($size / 1024, 1) . ' KB';
        } elseif (
$size > 0) {
           
$sizeFormatted = $size . ' B';
        } else {
           
$sizeFormatted = '-';
        }

        return [
           
'uid' => $msg['uid'],
           
'subject' => $msg['subject'] ?? '',
           
'from' => $mapAddresses($msg['from'] ?? []),
           
'to' => $mapAddresses($msg['to'] ?? []),
           
'cc' => $mapAddresses($msg['cc'] ?? []),
           
'replyTo' => $mapAddresses($msg['replyTo'] ?? []),
           
'date' => $msg['date'] ? date('M j, Y H:i', strtotime($msg['date'])) : null,
           
'bodyPreview' => $bodyPreview,
           
'bodyFull' => $body,
           
'bodyTruncated' => $isTruncated,
           
'htmlBody' => $msg['htmlBody'] ?? null,
           
'seen' => (bool) ($msg['seen'] ?? false),
           
'answered' => (bool) ($msg['answered'] ?? false),
           
'size' => $size,
           
'sizeFormatted' => $sizeFormatted,
           
'attachments' => array_map(static fn($a) => [
               
'filename' => $a['filename'],
               
'size' => $a['size'],
               
'sizeFormatted' => number_format($a['size'] / 1024, 1) . ' KB',
            ],
$msg['attachments'] ?? []),
        ];
    };

    if (
null !== $messageUid) {
       
$actionMap = [
           
'mark-read' => 'markAsRead',
           
'mark-unread' => 'markAsUnread',
           
'mark-answered' => 'markAsAnswered',
           
'mark-unanswered' => 'markAsUnanswered',
        ];

        if (
null !== $messageAction) {
            if (!isset(
$actionMap[$messageAction])) {
               
http_response_code(400);
                echo
json_encode(['success' => false, 'error' => 'Invalid action requested']);
                exit;
            }

           
$imap->{$actionMap[$messageAction]}($messageUid);
        }

       
$singleMessage = $imap->getMessage($messageUid);
        if (
null === $singleMessage) {
           
http_response_code(404);
            echo
json_encode(['success' => false, 'error' => 'Message not found']);
            exit;
        }

        echo
json_encode([
           
'success' => true,
           
'message' => $formatMessage($singleMessage),
        ],
JSON_INVALID_UTF8_SUBSTITUTE);

        exit;
    }

   
$messages = $imap->getMessages();

   
// Format for frontend
   
$output = array_map($formatMessage, $messages);

   
// Get total count matching criteria (without limit)
   
$totalFound = $imap->getTotalCount($imap->getConfig()->toImapCriteria());

    echo
json_encode([
       
'success' => true,
       
'count' => count($output),
       
'totalFound' => $totalFound,
       
'searchCriteria' => $imap->getConfig()->toImapCriteria(),
       
'messages' => $output,
    ],
JSON_INVALID_UTF8_SUBSTITUTE);

} catch (
ConnectionException $e) {
   
http_response_code(401);
    echo
json_encode(['success' => false, 'error' => 'Connection failed: ' . $e->getMessage()]);
} catch (\
Throwable $e) {
   
http_response_code(500);
    echo
json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]);
}


Details

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

  1. Features
  2. Requirements
  3. Installation
  4. Usage - Low-Level Client - ImapService Fluent API - Array / Config-Driven Setup - Connection Options
  5. Field & Filter Reference
  6. Performance & Production Readiness - Real-World Benchmarks - Memory Optimization with FetchOptions - Shopware 6.7 Integration
  7. Advanced Usage
  8. Demo Application
  9. Error Handling
  10. Security
  11. Development & Testing
  12. Contributing
  13. Troubleshooting
  14. License

Packagist Version PHP Version Require PHPStan AI Approved

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

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

  • 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'])
    ->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

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

| 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']);

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

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

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

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

Advanced Usage

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

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

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.

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

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

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.

Future-Proof for PHP 8.4+

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

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

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

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.

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


  php-ymap Demo on VercelExternal page  

Open in a separate window

  Files folder image Files (32)  
File Role Description
Files folder image.github (1 directory)
Files folder imageapi (1 file)
Files folder imageexample (3 files)
Files folder imagesrc (9 files, 2 directories)
Files folder imagetests (3 files)
Accessible without login Plain text file AI-REVIEWS.md Data Auxiliary data
Accessible without login Plain text file CHANGELOG.md Data Auxiliary data
Accessible without login Plain text file composer.json Data Auxiliary data
Accessible without login Plain text file CONTRIBUTING.md Data Auxiliary data
Accessible without login Plain text file LICENSE Lic. License text
Accessible without login Plain text file phpstan.neon Data Auxiliary data
Accessible without login Plain text file phpunit.xml Data Auxiliary data
Accessible without login Plain text file README.md Doc. Documentation
Accessible without login Plain text file SECURITY.md Data Auxiliary data
Accessible without login Plain text file vercel.json Data Auxiliary data

  Files folder image Files (32)  /  .github  
File Role Description
Files folder imageworkflows (1 file)

  Files folder image Files (32)  /  .github  /  workflows  
File Role Description
  Accessible without login Plain text file main.yml Data Auxiliary data

  Files folder image Files (32)  /  api  
File Role Description
  Accessible without login Plain text file index.php Aux. Configuration script

  Files folder image Files (32)  /  example  
File Role Description
  Accessible without login Image file favicon.ico Icon Icon image
  Accessible without login Plain text file get.php Example Example script
  Accessible without login Plain text file index.php Aux. Auxiliary script

  Files folder image Files (32)  /  src  
File Role Description
Files folder imageConnection (2 files)
Files folder imageExceptions (3 files)
  Plain text file Attachment.php Class Class source
  Plain text file ConnectionConfig.php Class Class source
  Plain text file FetchOptions.php Class Class source
  Plain text file ImapClient.php Class Class source
  Plain text file ImapClientInterface.php Class Class source
  Plain text file ImapService.php Class Class source
  Plain text file Message.php Class Class source
  Plain text file MessageAddress.php Class Class source
  Plain text file ServiceConfig.php Class Class source

  Files folder image Files (32)  /  src  /  Connection  
File Role Description
  Plain text file ExtImapConnection.php Class Class source
  Plain text file ImapConnectionInterface.php Class Class source

  Files folder image Files (32)  /  src  /  Exceptions  
File Role Description
  Plain text file ConnectionException.php Class Class source
  Plain text file ImapException.php Class Class source
  Plain text file MessageFetchException.php Class Class source

  Files folder image Files (32)  /  tests  
File Role Description
  Plain text file AttachmentTest.php Class Class source
  Plain text file MessageTest.php Class Class source
  Plain text file ServiceConfigTest.php 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.
Install with Composer Install with Composer
 Version Control Unique User Downloads  
 100%
Total:0
This week:0