# php-imap wraps ext-imap, and version 6 drops everything below PHP 8.2

> php-imap is an MIT licensed PHP library that connects to a mailbox over POP3, IMAP or NNTP through the PHP IMAP extension, with helpers for search criteria, attachments, flags and folders. Version 6.0.0 raised the floor to PHP 8.2, and ext-imap remains a non-thread-safe, ZTS-incompatible dependency you have to install yourself.

**barbushin/php-imap** — Manage mailboxes, filter/get/delete emails in PHP (supports IMAP/POP3/NNTP)

- Repository: https://github.com/barbushin/php-imap
- Stars: 1,699 · Forks: 452
- Language: PHP
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/barbushin-php-imap

## The library is a wrapper, so the extension is the real dependency

php-imap does not implement a mail protocol. It calls the PHP IMAP extension, ext-imap, and the README is explicit that you must have that extension installed for your PHP version before running composer require or composer install. Every capability the library advertises, connecting by POP3, IMAP or NNTP, fetching attachments and inline images, filtering and sorting, marking seen or unseen, deleting, managing folders, goes through ext-imap.

That framing matters for planning an upgrade. The extension is the part that decides whether your environment can run this at all, and its availability varies by PHP version in a way the README documents in detail. On PHP 8.2 and 8.3 the IMAP extension comes from your PHP distribution, and on Windows that can still mean enabling extension=php_imap.dll in php.ini. On PHP 8.4 and newer you install IMAP from PECL and enable it separately for your CLI and web SAPIs. Building from source may pull in c-client, OpenSSL and Kerberos development libraries.

One constraint has no workaround: ext-imap is not thread-safe and should not be used with ZTS builds. That rules out a class of PHP deployments outright, and it is the first thing to check rather than the last.

## The support table ends the 3.x, 4.x and 5.x lines

The README carries a version matrix, and it is unusually blunt. PHP 5.6 through 7.4 are end of life, as is PHP 8.0. PHP 8.1 is also listed as end of life, mapped to versions above 4.3.0 and the 5.x line. Active support covers PHP 8.2, 8.3, 8.4 and 8.5 on the 6.x line.

So the practical reading is that there is exactly one supported combination family: php-imap 6.x on PHP 8.2 or newer. Everything older is maintained in the sense that the code exists, not in the sense that it is supported.

The repository's release history matches that table. Version 5.0.1 dates from 2022-12-05, then 5.1.0 added OAuth support on 2026-05-10, and 6.0.0 followed the same day. The last push was on 2026-05-10, the same date as the 6.0.0 release. A project that had been quiet for three and a half years and then shipped a major and an OAuth feature on one day is worth testing carefully on your own PHP version before you depend on it.

Development tooling in the repository suggests what the maintainers hold themselves to: .php-cs-fixer.dist.php for style, psalm.xml and psalm/baseline.xml for static analysis, phpunit.xml and tests/ for the suite, composer-require-checker.config.json for dependency hygiene, and CI workflows for unit tests, static analysis and coverage.

## Installing the extension first, then the package

Once ext-imap is present, the install is a single Composer line for the latest tagged release:

```bash
composer require php-imap/php-imap
```

There is a second form for people who want the current master even when it is not tagged yet:

```bash
composer require php-imap/php-imap:dev-master
```

That distinction matters when you plan a deployment. The first gives you a release, the second gives you a moving branch that has not passed through a tag.

The test commands are documented in the same section. All tests run through a script, and PHPUnit alone can be run directly from the installed directory:

```bash
composer run tests
php vendor/bin/phpunit --testdox
```

Both assume a working ext-imap installation and, for the first, a composer install of the development dependencies.

The repository also ships four runnable examples under examples/: parsing all emails with a matching subject, parsing all emails without saving attachments, parsing unseen emails, and parsing unseen emails saving attachments one by one. Those files are the fastest way to see the intended call sequence.

## The constructor takes the server string, credentials and four optional knobs

The getting started example connects in one constructor call. The first argument is the classic PHP mailbox string, then the username and password, then optional directory, encoding, path trimming and attachment filename mode:

```php
$mailbox = new PhpImap\Mailbox(
    '{imap.gmail.com:993/imap/ssl}INBOX', // IMAP server and mailbox folder
    'some@gmail.com', // Username for the before configured mailbox
    '*********', // Password for the before configured username
    __DIR__, // Directory, where attachments will be saved (optional)
    'UTF-8', // Server encoding (optional)
    true, // Trim leading/ending whitespaces of IMAP path (optional)
    false // Attachment filename mode (optional; false = random filename; true = original filename)
);
```

Passing __DIR__ as the attachment directory is what makes attachments save automatically as messages are fetched. Leave it out and you get message objects without files on disk.

Connection arguments are set separately, and the two flags in the example are the ones to keep:

```php
$mailbox->setConnectionArgs(
    CL_EXPUNGE // expunge deleted mails upon mailbox close
    | OP_SECURE // don't do non-secure authentication
);
```

OP_SECURE is the flag that stops the client from authenticating over an unencrypted connection. CL_EXPUNGE means a deletion is not merely flagged but removed when the mailbox closes, which is the difference between hiding a message and destroying it.

## Attachment filenames are random by default for a reason

The attachment filename behaviour has three modes and the README explains the reasoning for the default. By default the library uses random filenames, because two different emails carrying an invoice.pdf would otherwise overwrite each other in the attachment directory.

Setting the constructor's filename mode to true keeps the original name, and the README calls out the consequence for backward compatibility: an existing file with the same name is still overwritten. The third mode is the one worth using when you keep attachments long term, and it is set after construction:

```php
$mailbox->setAttachmentFilenameCollisionMode(
    PhpImap\Mailbox::ATTACHMENT_FILENAME_COLLISION_SUFFIX
);
```

With that constant set, an original filename is kept and duplicates are suffixed with (1), (2) and so on. For an intake system where the user later looks for a file by its real name, this is the mode that avoids both problems at once.

The rest of the message API follows the same pragmatic shape. Search is a single call taking the criteria string understood by PHP's own imap_search:

```php
$mailsIds = $mailbox->searchMailbox('ALL');
```

The example then checks the empty case and exits with Mailbox is empty, because an empty result is the normal state of a mailbox and not an error. It also documents a subtle point: inspecting a message without changing its seen state means calling getMail($id, false) or getRawMail($id, false), which matters for any application that must not mark mail as read by looking at it.

## Errors arrive as a typed exception carrying provider messages

Connection failure is handled with PhpImap\Exceptions\ConnectionException, and the exception is useful rather than merely present:

```php
} catch(PhpImap\Exceptions\ConnectionException $ex) {
    echo "IMAP connection failed: " . implode(",", $ex->getErrors('all'));
    die();
}
```

getErrors('all') returns the underlying provider errors, which is the difference between an application that can log why Gmail refused the login and one that can only say the mailbox failed. The 'all' argument selects the full set rather than a single error string.

OAuth is supported in version 5.1.0 and later, with a specific division of labour the README spells out: you obtain and refresh the access token outside the library, then enable OAuth explicitly with $mailbox->enableOAuth($accessToken). The catch is that your ext-imap build has to expose OP_XOAUTH2 for it to work at all, which ties the feature back to how your extension was compiled.

Framework integration is a separate project rather than something bundled here: a Symfony bundle at secit-pl/imap-bundle. Longer examples live in the repository wiki.

## Conclusion

php-imap suits a PHP application that has to read a real mailbox, for example an intake form that lands in an inbox, and version 6 on PHP 8.2 or newer is the line to adopt because the older branches are marked end of life. It is the wrong choice on a shared host where you cannot install an extension, on a ZTS build, or in an application whose mail flow is webhook based rather than mailbox based. Before writing any code, confirm ext-imap loads in the SAPI your worker runs under and that you can satisfy the PHP 8.4 and newer route through PECL, then decide the attachment filename mode deliberately, because the default of random names and the COLLISION_SUFFIX mode behave differently for the files your users will keep.

## FAQ

### Which PHP versions does the current php-imap release support?

Version 6.x is supported on PHP 8.2, 8.3, 8.4 and 8.5, and requires the fileinfo, iconv, mbstring and json extensions. Every older PHP version and the 3.x, 4.x and 5.x lines of the library are marked end of life in the README.

### How do I install php-imap?

Install ext-imap first, then run composer require php-imap/php-imap for the latest release, or composer require php-imap/php-imap:dev-master for untagged master. On PHP 8.4 and newer you install IMAP from PECL and enable it for both CLI and web SAPIs.

### Why are my attachment files being renamed?

Random filenames are the default, so that identical names from different emails do not overwrite each other. Pass true as the constructor's filename mode to keep original names, which still overwrites duplicates, or set PhpImap\Mailbox::ATTACHMENT_FILENAME_COLLISION_SUFFIX to keep the original name and suffix duplicates with (1), (2) and so on.

### Can php-imap work with ZTS builds?

No. The README states that ext-imap is not thread-safe and should not be used with ZTS builds, so any ZTS PHP deployment is excluded regardless of the library version.

### Does php-imap support OAuth instead of a password?

Yes, since version 5.1.0. You obtain and refresh the access token outside the library and call $mailbox->enableOAuth($accessToken), and your ext-imap build must expose OP_XOAUTH2 for it to work.

## Sources

- [barbushin/php-imap on GitHub](https://github.com/barbushin/php-imap)
- [Issues](https://github.com/barbushin/php-imap/issues)
- [License: MIT](https://github.com/barbushin/php-imap/blob/master/LICENSE)
- [README](https://github.com/barbushin/php-imap/blob/master/README.md)
- [Releases](https://github.com/barbushin/php-imap/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/barbushin-php-imap
