This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Nette Mail is a standalone PHP library for creating and sending emails with support for SMTP, sendmail, DKIM signing, and fallback mechanisms. Part of the Nette Framework ecosystem but usable independently.
- Requirements: PHP 8.2 - 8.5, ext-iconv required
- Optional extensions: ext-dom (CssInliner, PHP 8.4+), ext-fileinfo (attachment type detection), ext-openssl (DKIM signing)
- Main dependency: nette/utils ^4.0
# Run all tests
composer run tester
# or
vendor/bin/tester tests -s
# Run specific test file
vendor/bin/tester tests/Mail/Message.phpt -s
# Run tests in specific directory
vendor/bin/tester tests/Mail/ -s# Run PHPStan analysis (level 5)
composer run phpstan
# or
vendor/bin/phpstan analyseThe library consists of four main areas:
-
Email Creation (
src/Mail/)Message- Main class for composing emails, extends MimePartMimePart- Base class handling MIME encoding, headers, and structure- Priority constants:
Message::High,Message::Normal,Message::Low
-
Email Sending (
src/Mail/)Mailerinterface - Contract for all mailer implementationsSendmailMailer- Uses PHP'smail()functionSmtpMailer- Full SMTP protocol implementation with TLS/SSL supportFallbackMailer- Retry mechanism across multiple mailers
-
Email Signing (
src/Mail/)Signerinterface - Contract for signing implementationsDkimSigner- DKIM (DomainKeys Identified Mail) signing using RSA-SHA256
-
CSS Inlining (
src/Mail/)CssInliner- Converts CSS rules to inlinestyleattributes for email HTML (requires PHP 8.4+ forDom\HTMLDocument)
src/Bridges/MailDI/MailExtension.php - Nette DI compiler extension for configuration.
DI Services registered:
mail.mailer- Mailer instance (SendmailMailer or SmtpMailer based on config)mail.signer- DKIM Signer instance (if DKIM is configured)nette.mailer- Alias to mail.mailer (for backward compatibility)
Configuration:
mail:
# Use SmtpMailer instead of SendmailMailer
smtp: true # (bool) defaults to false
# SMTP connection settings
host: smtp.gmail.com # (string) SMTP server hostname
port: 587 # (int) defaults: 25, 465 for ssl, 587 for tls
username: user@example.com
password: ****
encryption: tls # (ssl|tls|null) null = no encryption
timeout: 20 # (int) connection timeout in seconds, default 20
persistent: false # (bool) use persistent connection
clientHost: localhost # (string) defaults to $_SERVER['HTTP_HOST'] or 'localhost'
# SSL/TLS context options for SMTP connection
context:
ssl:
verify_peer: true # NEVER set to false in production!
verify_peer_name: true
allow_self_signed: false # Do not allow self-signed certificates
# See https://www.php.net/manual/en/context.ssl.php for all options
# DKIM signing configuration
dkim:
domain: example.com # Your domain name
selector: dkim # DKIM selector from DNS
privateKey: %appDir%/../dkim/private.key # Path to private key file
passPhrase: **** # Optional passphrase for private keySecurity Warning: Never disable SSL certificate verification (verify_peer: false) as it makes your application vulnerable to man-in-the-middle attacks. Instead, add certificates to the trust store if needed.
All exceptions in src/Mail/exceptions.php:
SendException- Base exception for sending failuresSmtpException- SMTP-specific errors (extends SendException)FallbackMailerException- All mailers failed (contains array of failures)SignException- Signing/verification errors
Message Creation:
- Fluent API with method chaining
- Automatic text alternative generation from HTML
- Auto-embedding images from filesystem using
[[...]]syntax or<img src=...> - Subject auto-extraction from
<title>element - Attachment support with auto-detection of MIME types
MIME Handling:
- Encoding methods: Base64, 7bit, 8bit, quoted-printable
- Line length management (76 characters default)
- Full UTF-8 support throughout
SMTP Features:
- TLS/SSL encryption support (
encryption: 'ssl'or'tls') - Default ports: 25 (unencrypted), 465 (SSL), 587 (TLS)
- Persistent connections
- Configurable timeout (default 20s)
- Custom stream options for SSL context
- Envelope sender support
- AUTH PLAIN and LOGIN authentication methods
DKIM Signing:
- RSA-SHA256 signing algorithm
- Private key passphrase support
- Automatic header canonicalization
- Compatible with Gmail, Outlook, and other major providers
Uses Nette Tester with .phpt format:
<?php
declare(strict_types=1);
use Tester\Assert;
require __DIR__ . '/../bootstrap.php';
test('Message correctly sets recipient', function () {
$mail = new Nette\Mail\Message;
$mail->addTo('test@example.com');
Assert::same(['test@example.com' => null], $mail->getHeader('To'));
});- 33 test files covering all major functionality
- Test fixtures in
tests/Mail/fixtures/for email samples - Bootstrap in
tests/bootstrap.phpprovidestest()helper function - Tests run on PHP 8.2-8.5 in CI
Follows Nette Coding Standard (PSR-12 based) with these requirements:
- Mandatory:
declare(strict_types=1)in all PHP files - Indentation: Tabs (not spaces)
- Method spacing: Two empty lines between methods
- Types: All properties, parameters, and return values must be typed
- Documentation: Only when adding information beyond PHP types
- Document array contents:
@return string[] - Document nullable relationships:
@param ?string - Skip obvious parameters (width, height, name)
- Document array contents:
- String quotes: Single quotes unless containing apostrophes
- Naming: PascalCase for classes, camelCase for methods/properties
- No prefixes: No
Abstract,Interface, orIprefixes
Opening brace on separate line after return type:
public function send(Message $mail):
{
// method body
}/**
* Adds email recipient.
* @param string|array $email Address or [address => name] pairs
*/
public function addTo(string|array $email, ?string $name = null): static
/**
* Sets message priority.
*/
public function setPriority(int $priority): static-
Before making changes:
- Read existing code to understand patterns
- Check related test files
- Verify PHPStan passes:
composer run phpstan
-
When adding features:
- Add corresponding tests in
tests/Mail/ - Use
test()helper for test cases - Run tests:
vendor/bin/tester tests -s
- Add corresponding tests in
-
When fixing bugs:
- Add regression test first
- Ensure fix doesn't break existing tests
- Update PHPDoc if behavior changes
-
Before committing:
- Run full test suite:
composer run tester - Run static analysis:
composer run phpstan - Check code style with Nette Code Checker
- Run full test suite:
When using Nette Mail within a full Nette Application (with presenters), you can integrate it with Latte templates and create absolute links using LinkGenerator.
To use n:href and {link} in email templates, inject both TemplateFactory and LinkGenerator:
use Nette;
class MailSender
{
public function __construct(
private Nette\Application\LinkGenerator $linkGenerator,
private Nette\Bridges\ApplicationLatte\TemplateFactory $templateFactory,
) {
}
private function createTemplate(): Nette\Application\UI\Template
{
$template = $this->templateFactory->createTemplate();
// Add LinkGenerator as 'uiControl' provider for n:href and {link}
$template->getLatte()->addProvider('uiControl', $this->linkGenerator);
return $template;
}
public function sendOrderConfirmation(int $orderId): void
{
$template = $this->createTemplate();
$html = $template->renderToString(__DIR__ . '/templates/orderEmail.latte', [
'orderId' => $orderId,
]);
$mail = new Nette\Mail\Message;
$mail->setFrom('shop@example.com')
->addTo('customer@example.com')
->setHtmlBody($html);
$this->mailer->send($mail);
}
}Template with absolute links:
<p>Your order #{$orderId} has been confirmed.</p>
<p><a n:href="Order:detail $orderId">View order details</a></p>All links created via LinkGenerator are absolute (include full domain), which is required for emails.
The library automatically handles encoding with these patterns:
- Uses
mb_detect_encoding()for content detection - Defaults to UTF-8 for all string operations
- Converts to ASCII for headers when needed
Headers are case-insensitive and normalized:
- Storage: lowercase with first letter capitalized
- Access: case-insensitive lookup
- Special handling for To, Cc, Bcc, From headers
Automatic embedding supports:
<img src="..."><body background="...">- CSS
url(...)in style attributes - Special
[[filename]]syntax
SendmailMailer uses PHP's mail() function. To set return path when server overwrites it:
$mailer = new Nette\Mail\SendmailMailer;
$mailer->commandArgs = '-fmy@email.com'; // Set return pathSmtpMailer handles SMTP protocol details:
- Automatic STARTTLS negotiation
- AUTH PLAIN and LOGIN support
- Proper QUIT handling in persistent mode
- Full error message parsing
- Connection reuse with persistent mode
CssInliner converts CSS rules to inline style attributes for email HTML compatibility. Uses Dom\HTMLDocument (PHP 8.4+) for DOM manipulation and CSS selectors.
// From <style> tags in HTML (automatically extracted, tag preserved)
$html = (new CssInliner)->inline($html);
// External CSS string
$html = (new CssInliner)->addCss('p { margin: 0; }')->inline($html);Architecture:
- Single-regex tokenizer (comment, whitespace, string, url, escape, at-ident, hash, number, ident, char)
- Token-based stylesheet parser with CSS nesting and @-rule skipping
- Declarations parsed into
property => valuearrays (enables deduplication and HTML attribute generation) - Cascade:
<style>→addCss()→ existing inlinestyle=""wins (last declaration wins) <style>tags are preserved (keeps @media queries intact)- Automatic HTML attribute generation for Outlook compatibility (background-color→bgcolor, width→width, height→height, text-align→align, vertical-align→valign)