fain182/ext-imap-polyfill
Composer 安装命令:
composer require fain182/ext-imap-polyfill
包简介
Drop-in polyfill for the imap_* functions removed from PHP core in 8.4, built on webklex/php-imap
README 文档
README
A drop-in polyfill for the imap_* functions removed from PHP core in 8.4.
PHP 8.4 moved ext-imap out of core and onto PECL (RFC). The C library it wraps (c-client) has been unmaintained since 2007 and is disappearing from Linux distributions, so installing the PECL package is getting harder every release. Codebases built on the imap_* functions are usually rewritten against an OOP library like webklex/php-imap instead — a real migration effort, not a version bump.
This package lets you skip that rewrite for the common cases: it defines the same global imap_* functions, backed by webklex/php-imap, and only activates if ext-imap isn't already loaded.
Install
composer require fain182/ext-imap-polyfill
No code changes. If ext-imap is present (e.g. you're still on PHP 8.3), the polyfill is a no-op — safe to add before you upgrade, not just after.
Coverage
This is not a reimplementation of all imap_* functions — 62 of 75 (83%) are implemented, chosen to cover the common path of connecting, reading, and moderating a mailbox. Calling any function marked ❌ below will simply hit PHP's "undefined function" error, same as before this package existed.
Every implemented function's object/array shape (property names, casing, flag semantics) is checked against the real extension — see Verifying against real ext-imap below. Known, deliberate divergences are called out in the notes column; anything not noted is expected to match exactly.
| Function | Implemented | Notes |
|---|---|---|
imap_8bit |
✅ | |
imap_alerts |
✅ | never populated — this polyfill doesn't surface server * OK [ALERT] responses |
imap_append |
✅ | |
imap_base64 |
✅ | |
imap_binary |
✅ | wraps output at 60 chars/line like c-client's rfc822_binary |
imap_body |
✅ | |
imap_bodystruct |
✅ | msgno-only, never uid — c-client's mail_body() has no UID equivalent, unlike imap_fetchbody() |
imap_check |
✅ | Mailbox property echoes the input spec rather than the c-client-normalized form |
imap_clearflag_full |
✅ | |
imap_close |
✅ | |
imap_create |
✅ | |
imap_createmailbox |
✅ | |
imap_delete |
✅ | |
imap_deletemailbox |
✅ | |
imap_errors |
✅ | |
imap_expunge |
✅ | |
imap_fetchbody |
✅ | |
imap_fetchheader |
✅ | |
imap_fetchmime |
✅ | |
imap_fetch_overview |
✅ | |
imap_fetchstructure |
✅ | |
imap_fetchtext |
✅ | alias of imap_body |
imap_gc |
✅ | this polyfill keeps no cache, so once the flags bitmask is validated it's a no-op that always returns true, like the real extension |
imap_getacl |
❌ | |
imap_getmailboxes |
✅ | |
imap_get_quota |
❌ | |
imap_get_quotaroot |
❌ | |
imap_getsubscribed |
✅ | |
imap_headerinfo |
✅ | fetchfrom/fetchsubject (nonzero $from_length/$subject_length) not implemented |
imap_headers |
❌ | |
imap_is_open |
✅ | |
imap_last_error |
✅ | |
imap_list |
✅ | |
imap_listmailbox |
✅ | alias of imap_list |
imap_listscan |
❌ | |
imap_listsubscribed |
✅ | alias of imap_lsub |
imap_lsub |
✅ | |
imap_mail |
❌ | |
imap_mailboxmsginfo |
✅ | Mailbox property echoes the input spec rather than the c-client-normalized form |
imap_mail_compose |
❌ | |
imap_mail_copy |
✅ | |
imap_mail_move |
✅ | copies and marks the source \Deleted without expunging, like c-client; target is a bare folder name |
imap_mime_header_decode |
✅ | |
imap_msgno |
✅ | |
imap_mutf7_to_utf8 |
✅ | |
imap_num_msg |
✅ | |
imap_num_recent |
✅ | cached client-side read, like imap_num_msg |
imap_open |
✅ | |
imap_ping |
✅ | |
imap_qprint |
✅ | |
imap_rename |
✅ | |
imap_renamemailbox |
✅ | |
imap_reopen |
✅ | only switches folders on the same connection — can't reconnect to a different host, since credentials aren't retained after imap_open |
imap_rfc822_parse_adrlist |
✅ | |
imap_rfc822_parse_headers |
✅ | |
imap_rfc822_write_address |
✅ | |
imap_savebody |
✅ | accepts a file path or an open stream resource; like the real extension, returns true regardless of whether the section fetch itself found anything, as long as the destination could be opened |
imap_scan |
❌ | |
imap_scanmailbox |
❌ | |
imap_search |
✅ | |
imap_setacl |
❌ | |
imap_setflag_full |
✅ | |
imap_set_quota |
❌ | |
imap_sort |
❌ | |
imap_status |
✅ | |
imap_subscribe |
✅ | |
imap_thread |
❌ | |
imap_timeout |
✅ | |
imap_uid |
✅ | |
imap_undelete |
✅ | |
imap_unsubscribe |
✅ | |
imap_utf7_decode |
✅ | |
imap_utf7_encode |
✅ | |
imap_utf8 |
✅ | |
imap_utf8_to_mutf7 |
✅ |
Limitations
A few deviations from the real extension that a package evaluator should know about before relying on it:
- Warnings are raised as
E_USER_WARNING, notE_WARNING— userland code can't raise the exact error level the C extension uses. OP_HALFOPENand most otherOP_*open flags are accepted (to avoid spuriousValueErrors) but have no effect; onlyOP_READONLYandCL_EXPUNGEactually change behavior.imap_reopen()only switches folders on the already-open connection — it can't reconnect to a different host, sinceimap_open()'s credentials aren't retained.imap_alerts()is never populated; this polyfill doesn't surface server* OK [ALERT]responses.- Only the
imapservice is supported in mailbox specs — a{host/pop3}or{host/nntp}prefix is parsed but ignored, and the connection always speaks IMAP. imap_open()'s$optionsargument (e.g.DISABLE_AUTHENTICATOR) is ignored.
Development
make install # composer install make test-unit # pure-PHP tests, no server needed make test-integration # spins up a disposable Greenmail IMAP server, runs the full suite against it make test # both of the above
Docker or Podman is required for test-integration (a docker-compose.yml is included for the equivalent setup with compose tooling).
Verifying against real ext-imap
make parity runs the exact same integration suite a second time, in a PHP 8.3 container with the genuine ext-imap extension installed from source, against the same Greenmail fixture. This is the real check that this polyfill's shapes and behavior actually match the extension it's replacing, not just internally-consistent test assertions.
make parity
This requires building a container image (Dockerfile.parity) the first time, which takes a few minutes.
License
MIT
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-07