contenir / contenir-mail
Compose, parse, store and send text and MIME-compliant multipart e-mail messages. A maintained continuation of laminas/laminas-mail and laminas/laminas-mime.
Requires
- php: ~8.3.0 || ~8.4.0 || ~8.5.0
- ext-iconv: *
- psr/clock: ^1.0
- symfony/polyfill-intl-idn: ^1.27.0
Requires (Dev)
- contenir/contenir-qa-tools: 0.1.x-dev
- infection/infection: ^0.34.1
- phpunit/phpunit: ^11.5.42
- psr/container: ^1.1 || ^2.0
- psr/log: ^2.0 || ^3.0
- symfony/process: ^7.3
Suggests
- ext-intl: Faster and stricter internationalised domain name handling than symfony/polyfill-intl-idn
- ext-openssl: DKIM signing with RSA keys, and reading DKIM keys from PEM
- ext-sodium: DKIM signing with Ed25519 keys (RFC 8463)
- psr/container: The container factories, Container\TransportFactory and ConfigProvider (PSR-11)
- psr/log: Logging the IMAP, POP3 and SMTP sessions, credentials redacted, with Protocol\LoggingConnection
Provides
None
Conflicts
None
Replaces
None
- 0.3.x-dev
- 0.3.0
- 0.2.x-dev
- 0.2.0
- 0.1.x-dev
- 0.1.1
- 0.1.0
- dev-release/0.3.0-notes
- dev-chore/best-practice
- dev-fix/address-set-mutant
- dev-refactor/config-and-naming
- dev-perf/reading-path
- dev-refactor/sasl-and-logging
- dev-perf/sending-path
- dev-perf/headers-addresses
- dev-test/live-mail-servers
- dev-fix/full-mutation-run
- dev-fix/pop3-subclass-api
- dev-fix/pre-0.3.0-security
- dev-refactor/pre-0.3.0-names
- dev-docs/pre-0.3.0-migration
- dev-release/0.3.0
- dev-feat/imap-idle
- dev-feat/imap-folder-metadata
- dev-feat/imap-uidplus
- dev-feat/dkim
- dev-feat/scram
- dev-feat/tnef
- dev-feat/imap-sort-paging
- dev-feat/imap4rev2
- dev-feat/lenient-addresses
- dev-feat/smtp-pipelining
- dev-feat/tls-options
- dev-release/0.2.0
- dev-feat/imap-xoauth2
- dev-release/0.1.1
- dev-fix/xoauth2-final-reply
- dev-test/provider-smoke
- dev-test/postfix-oauth
- dev-test/integration
This package is auto-updated.
Last update: 2026-10-09 05:49:08 UTC
README
Compose, parse, store and send text and MIME-compliant multipart e-mail messages.
contenir/contenir-mail is a maintained continuation of the abandoned
laminas/laminas-mail and
laminas/laminas-mime components, which
were themselves the successors of Zend Framework's Zend\Mail and Zend\Mime.
The complete history of both repositories, back to 2009, is preserved here, so
every contributor keeps their authorship in git log and git blame.
- Messages:
Message,Headersand theHeader\*classes,AddressandAddressList. - MIME:
Mime\Part,Mime\Multipart,Mime\Attachment,Mime\MimeandMime\Decode, formerly laminas-mime. - Transports:
Smtp,Sendmail,FileandInMemory, each configured with a typed*Config, andFailoverto try several in turn. - Protocols: SMTP, IMAP and POP3 clients over a small connection layer, with a scripted
Testing\InMemoryConnectionfor testing code that sends or reads mail. - Storage: read and write
MboxandMaildir, and read overImapandPop3. - Container support: optional PSR-11 factories and a
ConfigProvider, for Mezzio, laminas-mvc or any PSR-11 container. No container is required:psr/containeris suggested, not required.
Requirements
- PHP 8.3, 8.4 or 8.5
ext-iconvext-opensslfor TLS connections, andext-fileinfoto detect attachment types
ext-mbstring is not needed. The only other dependencies are the PSR clock and container interfaces and the Symfony IDN polyfill; install ext-intl for faster and stricter handling of internationalised domain names.
Install
composer require contenir/contenir-mail
Usage
use Contenir\Mail\Message; use Contenir\Mail\Mime\Attachment; use Contenir\Mail\Transport\Sendmail; $message = new Message(); $message->addFrom('sender@example.org', 'Sender'); $message->addTo('recipient@example.com', 'Recipient'); $message->setSubject('Hello'); $message->setText('This is the text of the e-mail.'); $message->setHtml('<p>This is the text of the e-mail.</p>'); $message->attach(Attachment::fromPath('/path/to/report.pdf')); (new Sendmail())->send($message);
Sending through SMTP with STARTTLS, which is the default:
use Contenir\Mail\Protocol\Smtp\Auth\Login; use Contenir\Mail\Transport\Smtp; use Contenir\Mail\Transport\SmtpConfig; $transport = new Smtp(new SmtpConfig( host: 'smtp.example.com', auth: new Login('orders', $password), )); // or from configuration $transport = new Smtp([ 'host' => 'smtp.example.com', 'auth' => ['type' => 'login', 'username' => 'orders', 'password' => $password], ]); $transport->send($message);
Reading a mailbox:
use Contenir\Mail\Storage\Flag; use Contenir\Mail\Storage\Maildir; foreach (new Maildir(['dirname' => '/var/mail/jo']) as $number => $message) { if (! $message->hasFlag(Flag::Seen)) { echo $message->getSubject(), ' from ', $message->getFrom()->first()?->getEmail(), "\n"; } }
getTextBody() and getHtmlBody() return a message's bodies as UTF-8. The
HTML is returned as sent, so sanitise it before showing it.
See the documentation for transports, attachments, character sets and reading mail.
Security
Mail libraries sit on trust boundaries: application input becomes protocol commands and shell arguments, and hostile servers and messages are parsed. This package checks every input where it enters, and each protection below has a regression test.
- Injection. Header values, display names, MIME parameters, SMTP, IMAP and POP3 command arguments and sendmail arguments refuse CR, LF, NUL and other characters that could end or extend them. IMAP strings that need it are sent as literals. SMTP bodies have their line endings normalised before dot-stuffing, which closes SMTP smuggling.
- Sendmail.
-fis only ever passed for a shell-safe sender (the 2016 PHPMailer and Zend Mail CVEs), and sendmail can be run without a shell at all. - TLS by default. Connections require STARTTLS unless told otherwise, verify the server certificate, accept TLS 1.2 or later only, refuse to continue in plain text when STARTTLS fails, and discard anything a server sends before the handshake. SMTP AUTH is refused over an unencrypted connection.
- Secrets. Passwords and tokens are kept out of protocol logs, exception traces
and
var_dump()output. - Hostile input. Server responses, header blocks, MIME nesting and part counts have limits. Storage paths must be local files, symlinks are refused, and Maildir files are created exclusively with mode 0600. Protocol and storage objects refuse to be unserialized.
- Spoofing. Addresses refuse control characters and bidirectional overrides, and internationalised domains are checked with the IDNA2008 bidi and CONTEXTJ rules. Attachment filenames read from mail have a sanitised accessor.
The security documentation maps every protection to the test that proves it and to the published vulnerabilities it guards against, and lists open findings. Standards covers RFC conformance. Report vulnerabilities as described in SECURITY.md.
Coming from laminas-mail and laminas-mime
contenir-mail keeps the Zend_Mail and laminas-mail vocabulary: Message,
Headers, Address, MIME parts, transports, protocols and storage keep their
names and their roles. The API underneath is modernised for PHP 8.3, so it is not
a drop-in replacement: values are typed and immutable, and a few classes have
gone.
composer remove laminas/laminas-mail laminas/laminas-mime composer require contenir/contenir-mail
Then rewrite the Laminas\Mime and Laminas\Mail namespaces and update the code
that touches the changed APIs. The migration guide has
the steps and the full mapping tables. It starts with the changes your code will
not complain about, so check these first:
Storage\Message::getFlags()is a list, soisset($flags['\Seen'])is always false; usehasFlag(Flag::Seen).Storage\Part::getContent()returns the decoded body; your ownbase64_decode()corrupts attachments.- IMAP folder names are given and returned as UTF-8; encoding them to modified UTF-7 yourself sends them to the wrong folder.
catchblocks naming the removedStorage\Part\Exceptionclasses never match.- Headers, addresses and MIME parts are immutable; a
with()whose result you discard does nothing. security: 'tls'is TLS from the start, not STARTTLS as laminas-mail'sssl: 'tls'was, and STARTTLS is now required by default.Mime\Partdefaults to base64 rather than 8bit.Crammd5is nowCramMd5, which only fails on a case-sensitive file system, andProtocol\Smtp\Auth\Xoauth2is nowProtocol\Sasl\Xoauth2.
Development
The QA toolchain comes from contenir/contenir-qa-tools: Mago for formatting, linting and static analysis, PHPUnit 11, and Infection for mutation testing.
composer check # cs-check, static-analysis and test composer cs-fix # mago format + mago lint --fix composer mutation-test # Infection; needs a coverage driver such as pcov or Xdebug
CI fails if any mutant of covered code survives. Infection does not mutate code the tests never reach.
Findings inherited from laminas-mail and laminas-mime are recorded in
mago-lint-baseline.toml and mago-analyze-baseline.toml. Many can only be fixed
by breaking the public API. New code is held to the full standard.
Tests that need a live IMAP, POP3 or SMTP server are skipped unless enabled through
the TESTS_CONTENIR_MAIL_* variables documented in phpunit.xml.dist.
License
BSD-3-Clause. See LICENSE.md and COPYRIGHT.md.