Skip to content

Commit 5aeaf1e

Browse files
committed
added CLAUDE.md
1 parent 425ee4c commit 5aeaf1e

2 files changed

Lines changed: 326 additions & 0 deletions

File tree

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.gitattributes export-ignore
22
.github/ export-ignore
33
.gitignore export-ignore
4+
CLAUDE.md export-ignore
45
ncs.* export-ignore
56
phpstan*.neon export-ignore
67
tests/ export-ignore

CLAUDE.md

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
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.
8+
9+
- **Requirements:** PHP 8.0 - 8.5, ext-iconv required
10+
- **Optional extensions:** ext-fileinfo (attachment type detection), ext-openssl (DKIM signing)
11+
- **Main dependency:** nette/utils ^4.0
12+
13+
## Essential Commands
14+
15+
### Testing
16+
17+
```bash
18+
# Run all tests
19+
composer run tester
20+
# or
21+
vendor/bin/tester tests -s
22+
23+
# Run specific test file
24+
vendor/bin/tester tests/Mail/Message.phpt -s
25+
26+
# Run tests in specific directory
27+
vendor/bin/tester tests/Mail/ -s
28+
```
29+
30+
### Static Analysis
31+
32+
```bash
33+
# Run PHPStan analysis (level 5)
34+
composer run phpstan
35+
# or
36+
vendor/bin/phpstan analyse
37+
```
38+
39+
## Architecture
40+
41+
### Core Components
42+
43+
The library consists of three main areas:
44+
45+
1. **Email Creation** (`src/Mail/`)
46+
- `Message` - Main class for composing emails, extends MimePart
47+
- `MimePart` - Base class handling MIME encoding, headers, and structure
48+
- Priority constants: `Message::High`, `Message::Normal`, `Message::Low`
49+
50+
2. **Email Sending** (`src/Mail/`)
51+
- `Mailer` interface - Contract for all mailer implementations
52+
- `SendmailMailer` - Uses PHP's `mail()` function
53+
- `SmtpMailer` - Full SMTP protocol implementation with TLS/SSL support
54+
- `FallbackMailer` - Retry mechanism across multiple mailers
55+
56+
3. **Email Signing** (`src/Mail/`)
57+
- `Signer` interface - Contract for signing implementations
58+
- `DkimSigner` - DKIM (DomainKeys Identified Mail) signing using RSA-SHA256
59+
60+
### Dependency Injection Integration
61+
62+
`src/Bridges/MailDI/MailExtension.php` - Nette DI compiler extension for configuration.
63+
64+
**DI Services registered:**
65+
- `mail.mailer` - Mailer instance (SendmailMailer or SmtpMailer based on config)
66+
- `mail.signer` - DKIM Signer instance (if DKIM is configured)
67+
- `nette.mailer` - Alias to mail.mailer (for backward compatibility)
68+
69+
**Configuration:**
70+
71+
```neon
72+
mail:
73+
# Use SmtpMailer instead of SendmailMailer
74+
smtp: true # (bool) defaults to false
75+
76+
# SMTP connection settings
77+
host: smtp.gmail.com # (string) SMTP server hostname
78+
port: 587 # (int) defaults: 25, 465 for ssl, 587 for tls
79+
username: user@example.com
80+
password: ****
81+
encryption: tls # (ssl|tls|null) null = no encryption
82+
timeout: 20 # (int) connection timeout in seconds, default 20
83+
persistent: false # (bool) use persistent connection
84+
clientHost: localhost # (string) defaults to $_SERVER['HTTP_HOST'] or 'localhost'
85+
86+
# SSL/TLS context options for SMTP connection
87+
context:
88+
ssl:
89+
verify_peer: true # NEVER set to false in production!
90+
verify_peer_name: true
91+
allow_self_signed: false # Do not allow self-signed certificates
92+
# See https://www.php.net/manual/en/context.ssl.php for all options
93+
94+
# DKIM signing configuration
95+
dkim:
96+
domain: example.com # Your domain name
97+
selector: dkim # DKIM selector from DNS
98+
privateKey: %appDir%/../dkim/private.key # Path to private key file
99+
passPhrase: **** # Optional passphrase for private key
100+
```
101+
102+
**Security 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.
103+
104+
### Exception Hierarchy
105+
106+
All exceptions in `src/Mail/exceptions.php`:
107+
- `SendException` - Base exception for sending failures
108+
- `SmtpException` - SMTP-specific errors (extends SendException)
109+
- `FallbackMailerException` - All mailers failed (contains array of failures)
110+
- `SignException` - Signing/verification errors
111+
112+
### Key Features
113+
114+
**Message Creation:**
115+
- Fluent API with method chaining
116+
- Automatic text alternative generation from HTML
117+
- Auto-embedding images from filesystem using `[[...]]` syntax or `<img src=...>`
118+
- Subject auto-extraction from `<title>` element
119+
- Attachment support with auto-detection of MIME types
120+
121+
**MIME Handling:**
122+
- Encoding methods: Base64, 7bit, 8bit, quoted-printable
123+
- Line length management (76 characters default)
124+
- Full UTF-8 support throughout
125+
126+
**SMTP Features:**
127+
- TLS/SSL encryption support (`encryption: 'ssl'` or `'tls'`)
128+
- Default ports: 25 (unencrypted), 465 (SSL), 587 (TLS)
129+
- Persistent connections
130+
- Configurable timeout (default 20s)
131+
- Custom stream options for SSL context
132+
- Envelope sender support
133+
- AUTH PLAIN and LOGIN authentication methods
134+
135+
**DKIM Signing:**
136+
- RSA-SHA256 signing algorithm
137+
- Private key passphrase support
138+
- Automatic header canonicalization
139+
- Compatible with Gmail, Outlook, and other major providers
140+
141+
## Testing Strategy
142+
143+
Uses Nette Tester with `.phpt` format:
144+
145+
```php
146+
<?php
147+
declare(strict_types=1);
148+
149+
use Tester\Assert;
150+
151+
require __DIR__ . '/../bootstrap.php';
152+
153+
test('Message correctly sets recipient', function () {
154+
$mail = new Nette\Mail\Message;
155+
$mail->addTo('test@example.com');
156+
157+
Assert::same(['test@example.com' => null], $mail->getHeader('To'));
158+
});
159+
```
160+
161+
- **31 test files** covering all major functionality
162+
- Test fixtures in `tests/Mail/fixtures/` for email samples
163+
- Bootstrap in `tests/bootstrap.php` provides `test()` helper function
164+
- Tests run on PHP 8.0-8.5 in CI
165+
166+
## Coding Standards
167+
168+
Follows Nette Coding Standard (PSR-12 based) with these requirements:
169+
170+
- **Mandatory:** `declare(strict_types=1)` in all PHP files
171+
- **Indentation:** Tabs (not spaces)
172+
- **Method spacing:** Two empty lines between methods
173+
- **Types:** All properties, parameters, and return values must be typed
174+
- **Documentation:** Only when adding information beyond PHP types
175+
- Document array contents: `@return string[]`
176+
- Document nullable relationships: `@param ?string`
177+
- Skip obvious parameters (width, height, name)
178+
- **String quotes:** Single quotes unless containing apostrophes
179+
- **Naming:** PascalCase for classes, camelCase for methods/properties
180+
- **No prefixes:** No `Abstract`, `Interface`, or `I` prefixes
181+
182+
### Return Type Format
183+
184+
Opening brace on separate line after return type:
185+
186+
```php
187+
public function send(Message $mail):
188+
{
189+
// method body
190+
}
191+
```
192+
193+
### phpDoc Examples
194+
195+
```php
196+
/**
197+
* Adds email recipient.
198+
* @param string|array $email Address or [address => name] pairs
199+
*/
200+
public function addTo(string|array $email, ?string $name = null): static
201+
202+
/**
203+
* Sets message priority.
204+
*/
205+
public function setPriority(int $priority): static
206+
```
207+
208+
## Development Workflow
209+
210+
1. **Before making changes:**
211+
- Read existing code to understand patterns
212+
- Check related test files
213+
- Verify PHPStan passes: `composer run phpstan`
214+
215+
2. **When adding features:**
216+
- Add corresponding tests in `tests/Mail/`
217+
- Use `test()` helper for test cases
218+
- Run tests: `vendor/bin/tester tests -s`
219+
220+
3. **When fixing bugs:**
221+
- Add regression test first
222+
- Ensure fix doesn't break existing tests
223+
- Update PHPDoc if behavior changes
224+
225+
4. **Before committing:**
226+
- Run full test suite: `composer run tester`
227+
- Run static analysis: `composer run phpstan`
228+
- Check code style with Nette Code Checker
229+
230+
## Usage in Nette Application
231+
232+
When using Nette Mail within a full Nette Application (with presenters), you can integrate it with Latte templates and create absolute links using `LinkGenerator`.
233+
234+
### Email Templates with Links
235+
236+
To use `n:href` and `{link}` in email templates, inject both `TemplateFactory` and `LinkGenerator`:
237+
238+
```php
239+
use Nette;
240+
241+
class MailSender
242+
{
243+
public function __construct(
244+
private Nette\Application\LinkGenerator $linkGenerator,
245+
private Nette\Bridges\ApplicationLatte\TemplateFactory $templateFactory,
246+
) {
247+
}
248+
249+
250+
private function createTemplate(): Nette\Application\UI\Template
251+
{
252+
$template = $this->templateFactory->createTemplate();
253+
// Add LinkGenerator as 'uiControl' provider for n:href and {link}
254+
$template->getLatte()->addProvider('uiControl', $this->linkGenerator);
255+
return $template;
256+
}
257+
258+
259+
public function sendOrderConfirmation(int $orderId): void
260+
{
261+
$template = $this->createTemplate();
262+
$html = $template->renderToString(__DIR__ . '/templates/orderEmail.latte', [
263+
'orderId' => $orderId,
264+
]);
265+
266+
$mail = new Nette\Mail\Message;
267+
$mail->setFrom('shop@example.com')
268+
->addTo('customer@example.com')
269+
->setHtmlBody($html);
270+
271+
$this->mailer->send($mail);
272+
}
273+
}
274+
```
275+
276+
**Template with absolute links:**
277+
278+
```latte
279+
<p>Your order #{$orderId} has been confirmed.</p>
280+
<p><a n:href="Order:detail $orderId">View order details</a></p>
281+
```
282+
283+
All links created via `LinkGenerator` are absolute (include full domain), which is required for emails.
284+
285+
## Important Patterns
286+
287+
### Encoding Detection
288+
289+
The library automatically handles encoding with these patterns:
290+
- Uses `mb_detect_encoding()` for content detection
291+
- Defaults to UTF-8 for all string operations
292+
- Converts to ASCII for headers when needed
293+
294+
### Header Management
295+
296+
Headers are case-insensitive and normalized:
297+
- Storage: lowercase with first letter capitalized
298+
- Access: case-insensitive lookup
299+
- Special handling for To, Cc, Bcc, From headers
300+
301+
### Image Embedding
302+
303+
Automatic embedding supports:
304+
- `<img src="...">`
305+
- `<body background="...">`
306+
- CSS `url(...)` in style attributes
307+
- Special `[[filename]]` syntax
308+
309+
### SendmailMailer Configuration
310+
311+
`SendmailMailer` uses PHP's `mail()` function. To set return path when server overwrites it:
312+
313+
```php
314+
$mailer = new Nette\Mail\SendmailMailer;
315+
$mailer->commandArgs = '-fmy@email.com'; // Set return path
316+
```
317+
318+
### SMTP Connection
319+
320+
`SmtpMailer` handles SMTP protocol details:
321+
- Automatic STARTTLS negotiation
322+
- AUTH PLAIN and LOGIN support
323+
- Proper QUIT handling in persistent mode
324+
- Full error message parsing
325+
- Connection reuse with persistent mode

0 commit comments

Comments
 (0)