Nette Mail
Chystáte se odesílat e-maily, například newslettery nebo potvrzení objednávek? Nette Framework poskytuje potřebné nástroje s velmi příjemným API. Ukážeme si:
- jak e-mail vytvořit včetně příloh
- jak ho odeslat
- jak spojit e-maily a šablony
Instalace
Knihovnu stáhnete a nainstalujete pomocí nástroje Composer:
composer require nette/mail
Vytvoření e-mailu
E-mail je objektem třídy Nette\Mail\Message. Vytvoříme jej třeba takto:
$mail = new Nette\Mail\Message;
$mail->setFrom('Franta <franta@example.com>')
->addTo('petr@example.com')
->addTo('jirka@example.com')
->setSubject('Potvrzení objednávky')
->setBody("Dobrý den,\nvaše objednávka byla přijata.");
Všechny zadávané parametry musí být v UTF-8.
Kromě uvedení příjemce metodou addTo() lze uvést i příjemce kopie addCc(), nebo příjemce
skryté kopie addBcc(). Ve všech těchto metodách včetně setFrom() můžeme adresáta zapsat třemi
způsoby:
$mail->setFrom('franta@example.com');
$mail->setFrom('franta@example.com', 'Franta');
$mail->setFrom('Franta <franta@example.com>');
Tělo e-mailu zapsaného v HTML se předává metodou setHtmlBody():
$mail->setHtmlBody('<p>Dobrý den,</p><p>vaše objednávka byla přijata.</p>');
Textovou alternativu nemusíte vytvářet, Nette ji vygeneruje automaticky za vás. A pokud e-mail nemá nastavený subjekt,
pokusí se jej převzít z elementu <title>.
Do HTML těla lze také neobyčejně snadno vkládat obrázky. Stačí jako druhý parametr předat cestu, kde se obrázky fyzicky nacházejí, a Nette je automaticky zahrne do e-mailu:
// automaticky přidá /path/to/images/background.gif do e-mailu
$mail->setHtmlBody(
'<b>Hello</b> <img src="background.gif">',
'/path/to/images',
);
Algoritmus vkládající obrázky vyhledává tyto vzory: <img src=...>,
<body background=...>, url(...) uvnitř HTML atributu style a speciální syntaxi
[[...]].
Může být odesílání e-mailů ještě jednodušší?
E-mail je něco jako pohlednice. Nikdy e-mailem neposílejte hesla ani jiné přístupové údaje.
Přílohy
Do e-mailu lze samozřejmě vkládat přílohy. Slouží k tomu metoda
addAttachment(string $file, ?string $content = null, ?string $contentType = null).
// vloží do e-mailu soubor /path/to/example.zip pod názvem example.zip
$mail->addAttachment('/path/to/example.zip');
// vloží do e-mailu soubor /path/to/example.zip pojmenovaný info.zip
$mail->addAttachment('info.zip', file_get_contents('/path/to/example.zip'));
// vloží do e-mailu soubor example.txt s obsahem "Hello John!"
$mail->addAttachment('example.txt', 'Hello John!');
Šablony
Pokud posíláte HTML e-maily, přímo se nabízí je zapisovat v šablonovacím systému Latte. Jak na to?
$latte = new Latte\Engine;
$params = [
'orderId' => 123,
];
$mail = new Nette\Mail\Message;
$mail->setFrom('Franta <franta@example.com>')
->addTo('petr@example.com')
->setHtmlBody(
$latte->renderToString('/path/to/email.latte', $params),
'/path/to/images',
);
Soubor email.latte:
<html>
<head>
<meta charset="utf-8">
<title>Potvrzení objednávky</title>
<style>
body {
background: url("background.png")
}
</style>
</head>
<body>
<p>Dobrý den,</p>
<p>Vaše objednávka číslo {$orderId} byla přijata.</p>
</body>
</html>
Nette automaticky vloží všechny obrázky, nastaví subject podle elementu <title> a vygeneruje textovou
alternativu k HTML.
Použití v Nette Application
Pokud e-maily používáte společeně s Nette Application, tj. s presentery, můžete chtít v šablonách vytvářet
odkazy pomocí atributu n:href nebo značky {link}. Ty Latte v základu nezná, ale je velmi snadné je
doplnit. Vytvářet odkazy umí objekt Nette\Application\LinkGenerator, ke kterému se dostanete tak, že si jej
necháte předat pomocí dependency
injection:
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();
$template->getLatte()->addProvider('uiControl', $this->linkGenerator);
return $template;
}
public function createEmail(): Nette\Mail\Message
{
$template = $this->createTemplate();
$html = $template->renderToString('/path/to/email.latte', $params);
$mail = new Nette\Mail\Message;
$mail->setHtmlBody($html);
// ...
return $mail;
}
}
V šabloně potom vytváříme odkazy tak, jak jsme zvyklí. Všechny odkazy vytvořene přes LinkGenerator budou absolutní.
<a n:href="Presenter:action">Odkaz</a>
Inlinování CSS
Nette\Mail\CssInliner převádí CSS pravidla na
inline atributy style, aby se e-maily zobrazovaly správně ve všech klientech. Zároveň generuje HTML atributy pro
kompatibilitu s Outlookem.
Vyžaduje PHP 8.4 nebo novější a rozšíření dom.
Většina e-mailových klientů má omezenou podporu značky <style> nebo ji zcela ignoruje. Pro správné
zobrazení je proto potřeba převést CSS pravidla na inline atributy style u jednotlivých elementů. Stačí HTML
předat metodě inline():
$inliner = new Nette\Mail\CssInliner;
$html = $inliner->inline($html);
Pokud HTML obsahuje například:
<style>
p { margin: 0; color: #333; }
a { color: #a0704e; }
</style>
<p>Hello <a href="#">world</a></p>
Výsledek po inlinování bude (značka <style> se zachová, zde ji vynecháváme pro stručnost):
<p style="margin: 0; color: #333">Hello <a href="#" style="color: #a0704e">world</a></p>
Značka <style> zůstane ve výstupu vždy zachována, takže @media dotazy a další pravidla,
která nelze inlinovat, budou nadále fungovat.
Kromě extrakce stylů ze značek <style> lze CSS dodat i metodou addCss(). Inlinování je
třeba provést před předáním HTML do setHtmlBody():
$latte = new Latte\Engine;
$params = [
'orderId' => 123,
];
$html = $latte->renderToString('/path/to/email.latte', $params);
$html = (new Nette\Mail\CssInliner)
->addCss(file_get_contents('/path/to/email.css'))
->inline($html);
$mail = new Nette\Mail\Message;
$mail->setHtmlBody($html);
Pokud je stejná CSS vlastnost nastavena z více zdrojů, pozdější přepisují dřívější. Pravidla ze značek
<style> mají nejnižší prioritu, následují pravidla přidaná přes addCss(). Pokud element
již má inline atribut style v HTML, ten má vždy nejvyšší prioritu.
At-rules jako @media nebo @font-face se při inlinování přeskakují. Pseudo-třídy jako
:hover nelze smysluplně inlinovat, protože inline styly nepodporují dynamické stavy.
HTML atributy pro Outlook
Desktopové verze Microsoft Outlooku používají vykreslovací jádro Wordu, které nerozumí mnoha CSS vlastnostem. Pro
zajištění kompatibility CssInliner automaticky generuje odpovídající HTML atributy z CSS pravidel vedle
inline stylů:
| CSS vlastnost | HTML atribut | Aplikuje se na |
|---|---|---|
background-color |
bgcolor |
<table>, <td>, <th>,
<body>, <tr> |
width |
width |
<table>, <td>, <th>, <img> |
height |
height |
<table>, <td>, <th>, <img> |
border-spacing |
cellspacing |
<table> |
U width, height a cellspacing se automaticky odstraní jednotka px (např.
width: 600px se převede na width="600"). Inline styl i HTML atribut se nastaví vždy společně,
takže se e-mail zobrazí správně v moderních klientech i v Outlooku.
HTML atributy se generují pouze z CSS pravidel zpracovaných třídou CssInliner, nikoliv z atributů
style již přítomných v původním HTML.
Odeslání e-mailu
Mailer je třída zajišťující odesílání e-mailů. Implementuje rozhraní Nette\Mail\Mailer a k dispozici je několik předpřipravených mailerů, které si představíme.
Framework automaticky přidává do DI kontejneru službu typu Nette\Mail\Mailer sestavenou na základě konfigurace, a ke které se dostanete tak, že si ji necháte předat pomocí dependency injection.
SendmailMailer
Výchozí mailer je SendmailMailer, který používá PHP funkci mail. Příklad použití:
$mailer = new Nette\Mail\SendmailMailer;
$mailer->send($mail);
Pokud chcete nastavit returnPath a server vám ho pořád přepisuje, použijte
$mailer->commandArgs = '-fMuj@email.cz'.
SmtpMailer
K odeslání pošty přes SMTP server slouží SmtpMailer.
$mailer = new Nette\Mail\SmtpMailer(
host: 'smtp.gmail.com',
username: 'franta@gmail.com',
password: '*****',
encryption: 'ssl',
);
$mailer->send($mail);
Konstruktoru lze předat tyto další parametry:
port– pokud není nastaven, použije se výchozí 25 nebo 465 prossltimeout– timeout pro SMTP spojenípersistent– použít persistent spojeníclientHost– nastavení hlavičky Host klientastreamOptions– umožňuje nastavit SSL context options pro spojení
FallbackMailer
E-maily přímo neodesílá, ale odesílání zprostředkovává přes sadu mailerů. V případě, že jeden mailer selže, zopakuje pokus u dalšího. Pokud selže i poslední, začíná znovu od prvního.
$mailer = new Nette\Mail\FallbackMailer([
$smtpMailer,
$backupSmtpMailer,
$sendmailMailer,
]);
$mailer->send($mail);
Jako další parametry v konstruktoru můžeme uvést počet opakování a čekací dobu v milisekundách.
DKIM
DKIM (DomainKeys Identified Mail) je technologie pro zvýšení důvěryhodnosti e-mailů, která také napomáhá odhalení podvržených zpráv. Odeslaná zpráva je podepsána privátním klíčem domény odesílatele a tento podpis je uložen v hlavičce e-mailu. Server příjemce porovná tento podpis s veřejným klíčem uloženým v DNS záznamech domény. Tím, že podpis odpovídá, je prokázáno, že e-mail skutečně pochází z odesílatelovy domény a že během přenosu zprávy nedošlo k její úpravě.
Podepisování e-mailů můžete maileru nastavit přímo v konfiguraci. Pokud nepoužíváte dependency injection, používá se tímto způsobem:
$signer = new Nette\Mail\DkimSigner(
domain: 'nette.org',
selector: 'dkim',
privateKey: file_get_contents('../dkim/dkim.key'),
passPhrase: '****',
);
$mailer = new Nette\Mail\SendmailMailer; // nebo SmtpMailer
$mailer->setSigner($signer);
$mailer->send($mail);
Konfigurace
Přehled konfiguračních voleb pro Nette Mail. Pokud nepoužívate celý framework, ale jen tuto knihovnu, přečtěte si, jak konfiguraci načíst.
Pro odesílání e-mailů se standardně používá mailer Nette\Mail\SendmailMailer, který se dále
nekonfiguruje. Můžeme jej však přepnout na Nette\Mail\SmtpMailer:
mail:
# použije SmtpMailer
smtp: true # (bool) výchozí je false
host: ... # (string)
port: ... # (int)
username: ... # (string)
password: ... # (string)
timeout: ... # (int)
encryption: ... # (ssl|tls|null) výchozí je null (má alias 'secure')
clientHost: ... # (string) výchozí je $_SERVER['HTTP_HOST']
persistent: ... # (bool) výchozí je false
# kontext pro připojení k SMTP serveru, výchozí je stream_context_get_default()
context:
ssl: # přehled voleb na https://www.php.net/manual/en/context.ssl.php
allow_self_signed: ...
...
http: # přehled voleb na https://www.php.net/manual/en/context.http.php
header: ...
...
Pomocí volby context › ssl › verify_peer: false lze vypnout ověřování SSL certifikátů. Důrazně
nedoporučujeme tohle dělat, protože se aplikace stane zranitelnou. Místo toho přidejte certifikáty do uložiště.
Pro zvýšení důvěryhodnosti můžeme e-maily podpisovat pomocí technologie DKIM:
mail:
dkim:
domain: myweb.com
selector: lovenette
privateKey: %appDir%/cert/dkim.priv
passPhrase: ...
Služby DI
Tyto služby se přidávají do DI kontejneru:
| Název | Typ | Popis |
|---|---|---|
mail.mailer |
Nette\Mail\Mailer | třída odesílající e-maily |
mail.signer |
Nette\Mail\Signer | DKIM podepisování |