Nette Documentation Preview

syntax
Nette Mail
**********

<div class=perex>

Haber bültenleri ya da sipariş onayları gibi e-postalar göndermeyi mi planlıyorsunuz? Nette Framework, çok kullanıcı dostu bir API ile gereken araçları sunar. Size şunları göstereceğiz:

- ekler dahil bir e-postanın nasıl oluşturulacağı
- onun nasıl gönderileceği
- e-postaların ve şablonların nasıl birleştirileceği

</div>


Kurulum
=======

Kütüphaneyi [Composer|best-practices:composer] kullanarak indirin ve kurun:

```shell
composer require nette/mail
```


E-posta Oluşturma
=================

E-posta bir [api:Nette\Mail\Message] nesnesidir. Bir tane şöyle oluşturalım:

```php
$mail = new Nette\Mail\Message;
$mail->setFrom('John <john@example.com>')
	->addTo('peter@example.com')
	->addTo('jack@example.com')
	->setSubject('Order Confirmation')
	->setBody("Hello,\nYour order has been accepted.");
```

Belirtilen tüm parametreler UTF-8 kodlamasında olmalıdır.

`jan@příklad.cz` gibi uluslararasılaştırılmış alan adı taşıyan adresler, posta sunucularının gerektirdiği punycode denen ASCII biçimine otomatik dönüştürülür; bunun için `intl` uzantısı gerekir. .{data-version:4.2.0}

Alıcıları `addTo()` ile belirtmenin yanı sıra, kopya alıcılarını `addCc()` ile, gizli kopya alıcılarını ise `addBcc()` ile belirtebilirsiniz. `setFrom()` dahil tüm bu metotlar, alıcıyı üç şekilde kabul eder:

```php
$mail->setFrom('john.doe@example.com');
$mail->setFrom('john.doe@example.com', 'John Doe');
$mail->setFrom('John Doe <john.doe@example.com>');
```

HTML ile yazılmış bir e-postanın gövdesi `setHtmlBody()` metoduyla aktarılır:

```php
$mail->setHtmlBody('<p>Hello,</p><p>Your order has been accepted.</p>');
```

Metin alternatifi oluşturmanıza gerek yok; Nette onu sizin için otomatik üretecek. Ve e-postanın konusu ayarlanmamışsa, onu `<title>` elemanından almaya çalışacak.

Görseller de HTML gövdesine olağanüstü kolay biçimde gömülebilir. Görsellerin fiziksel olarak bulunduğu yolu ikinci parametre olarak aktarmanız yeter; Nette onları e-postaya otomatik ekleyecek:

```php
// /path/to/images/background.gif dosyasını e-postaya otomatik ekler
$mail->setHtmlBody(
	'<b>Hello</b> <img src="background.gif">',
	'/path/to/images',
);
```

Görsel gömme algoritması şu kalıpları arar: `<img src=...>`, `<body background=...>`, HTML `style` niteliğinin içindeki `url(...)` ve özel `[[...]]` sözdizimi.

E-posta göndermek daha da kolay olabilir miydi?

.[tip]
E-postalar kartpostal gibidir. Parolaları ya da başka kimlik bilgilerini asla e-postayla göndermeyin.


Diğer Seçenekler
----------------

`Message` nesnesi ayrıca bir yanıt adresi, geri dönen mesajlar için bir dönüş yolu ve mesaj önceliği ayarlamanızı da sağlar:

```php
$mail->addReplyTo('reply@example.com', 'Support')
	->setReturnPath('bounces@example.com')
	->setPriority(Nette\Mail\Message::High);
```

Öncelik, `Message::High`, `Message::Normal` ya da `Message::Low` sabitlerinden biridir.


Tek Tıkla Abonelikten Çıkma .{data-version:4.2.0}
-------------------------------------------------

Gmail ve Yahoo, haber bültenleri gibi toplu postaların doğrudan posta istemcisinde tek tıkla abonelikten çıkma sunmasını gerektirir. Bunu, RFC 8058 tarafından tanımlanan bir başlık çifti üstlenir; `setUnsubscribe()` metodu onları sizin için doğru biçimde ayarlar:

```php
$mail->setUnsubscribe('https://example.com/unsubscribe?token=xyz');
```

URL, alıcının aboneliğini yalın bir HTTP POST isteğine yanıt olarak, başka bir onay olmadan sonlandırmalıdır. İkinci parametre, POST gönderemeyen istemciler için yedek olarak bir e-posta adresi sağlayabilir; tek başına da çalışır: `$mail->setUnsubscribe(email: 'unsubscribe@example.com')`.


Ekler
-----

Elbette e-postalara dosya ekleyebilirsiniz. Bunun için `addAttachment(string $file, ?string $content = null, ?string $contentType = null)` metodunu kullanın.

```php
// /path/to/example.zip dosyasını e-postaya example.zip adıyla ekler
$mail->addAttachment('/path/to/example.zip');

// /path/to/example.zip dosyasını info.zip adıyla ekler
$mail->addAttachment('info.zip', file_get_contents('/path/to/example.zip'));

// example.txt dosyasını "Hello John!" içeriğiyle ekler
$mail->addAttachment('example.txt', 'Hello John!');
```

Bir dosyayı `addEmbeddedFile()` ile doğrudan HTML gövdesine de gömebilirsiniz. Bu metot, HTML'de `Content-ID` değerine başvuracağınız, oluşturulan MIME parçasını döndürür (otomatik görsel gömme içeride tam olarak bu mekanizmayı kullanır):

```php
$file = $mail->addEmbeddedFile('/path/to/logo.png');
$mail->setHtmlBody('<img src="cid:' . trim($file->getHeader('Content-ID'), '<>') . '">');
```


Şablonlar
---------

HTML e-postalar gönderiyorsanız, onları [Latte|latte:] şablon sisteminde yazmak harika bir seçenektir. Bu nasıl yapılır?

```php
$latte = new Latte\Engine;
$params = [
	'orderId' => 123,
];

$mail = new Nette\Mail\Message;
$mail->setFrom('John <john@example.com>')
	->addTo('jack@example.com')
	->setHtmlBody(
		$latte->renderToString('/path/to/email.latte', $params),
		'/path/to/images',
	);
```

`email.latte` dosyası:

```latte
<html>
<head>
	<meta charset="utf-8">
	<title>Order Confirmation</title>
	<style>
	body {
		background: url("background.png")
	}
	</style>
</head>
<body>
	<p>Hello,</p>

	<p>Your order number {$orderId} has been accepted.</p>
</body>
</html>
```

Nette tüm görselleri otomatik gömer, konuyu `<title>` elemanına göre ayarlar ve HTML için bir metin alternatifi üretir.


Nette Application'da Kullanım
-----------------------------

E-postaları Nette Application ile, yani presenter'larla birlikte kullanıyorsanız, şablonlarda `n:href` niteliğini ya da `{link}` etiketini kullanarak bağlantı oluşturmak isteyebilirsiniz. Latte bunları varsayılan olarak bilmez, ama eklemek çok kolaydır. `Nette\Application\LinkGenerator` nesnesi bağlantı oluşturabilir; onu [bağımlılık enjeksiyonuyla |dependency-injection:passing-dependencies] aktarılmasını sağlayarak elde edersiniz:

```php
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;
	}
}
```

Şablonda bağlantıları sonra alışık olduğunuz gibi oluşturursunuz. LinkGenerator ile oluşturulan tüm bağlantılar mutlak olacak.

```latte
<a n:href="Presenter:action">Link</a>
```


CSS Satır İçine Alma
====================

[api:Nette\Mail\CssInliner], e-postaların tüm istemcilerde tutarlı render edilmesi için CSS kurallarını satır içi `style` niteliklerine dönüştürür. Ayrıca Outlook uyumluluğu için HTML nitelikleri üretir.

.[note]
PHP 8.4 ya da daha yenisini ve `dom` uzantısını gerektirir.

Çoğu e-posta istemcisi `<style>` etiketlerini sınırlı destekler ya da onları tümüyle yok sayar. Doğru render'ı güvence altına almak için CSS kurallarının tek tek elemanlar üzerinde satır içi `style` niteliklerine dönüştürülmesi gerekir. HTML'inizi `inline()` içinden geçirmeniz yeter:

```php
$inliner = new Nette\Mail\CssInliner;
$html = $inliner->inline($html);
```

Örneğin HTML şunu içeriyorsa:

```latte
<style>
p { margin: 0; color: #333; }
a { color: #a0704e; }
</style>
<p>Hello <a href="#">world</a></p>
```

Satır içine alma sonrası sonuç şöyle olacak (`<style>` etiketi korunur, ama burada kısalık için atlandı):

```latte
<p style="margin: 0; color: #333">Hello <a href="#" style="color: #a0704e">world</a></p>
```

`<style>` etiketi çıktıda her zaman korunur, böylece `@media` sorguları ve satır içine alınamayan diğer kurallar çalışmayı sürdürür.

Stilleri `<style>` etiketlerinden ayıklamanın yanı sıra, CSS'i `addCss()` metoduyla da sağlayabilirsiniz. CSS'i, HTML'i `setHtmlBody()` metoduna aktarmadan önce satır içine almanız gerekir:

```php
$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);
```

Birden çok kural bir elemanın aynı özelliğini hedeflediğinde, kazananı tıpkı tarayıcıdaki gibi CSS kaskadı belirler: `!important` bildirimleri olağanları yener, var olan bir satır içi `style` niteliği her seçiciyi yener, daha özgül bir seçici daha az özgül olanı yener ve eşitlikte sonraki kural kazanır. `<style>` etiketlerinden gelen kurallar, `addCss()` ile eklenenlerden önce işlenir ve yalnızca kazanan değer yazılır. .{data-version:4.2.0}

`@media` ya da `@font-face` gibi at-kuralları satır içine alma sırasında atlanır. `:hover` gibi sözde sınıfların anlamlı biçimde satır içine alınamayacağını unutmayın; çünkü satır içi stiller dinamik durumları desteklemez.


Outlook İçin HTML Nitelikleri
-----------------------------

Microsoft Outlook'un masaüstü sürümleri, pek çok CSS özelliğini anlamayan Word render motorunu kullanır. Uyumluluğu güvence altına almak için `CssInliner`, satır içi stillerin yanı sıra CSS kurallarından karşılık gelen HTML niteliklerini otomatik üretir:

| CSS Özelliği | HTML Niteliği | Uygulandığı Yer
|-----------------------------------------------------
| `background-color` | `bgcolor` | `<table>`, `<td>`, `<th>`, `<body>`, `<tr>`
| `width` | `width` | `<table>`, `<td>`, `<th>`, `<img>`
| `height` | `height` | `<table>`, `<td>`, `<th>`, `<img>`
| `border-spacing` | `cellspacing` | `<table>`

`width`, `height` ve `cellspacing` için `px` birimi otomatik olarak atılır (örneğin `width: 600px` değeri `width="600"` olur), yüzde `%` işaretini korur ve `auto` ya da `calc()` gibi bir niteliğin ifade edemeyeceği değerler hiç nitelik üretmez. Hem satır içi stil hem HTML niteliği birlikte ayarlanır, böylece e-posta modern istemcilerde de Outlook'ta da doğru render edilir.

HTML nitelikleri yalnızca `CssInliner` tarafından işlenen CSS kurallarından üretilir, özgün HTML'de zaten bulunan `style` niteliklerinden değil.


E-posta Gönderme
================

Mailer, e-posta göndermekten sorumlu bir sınıftır. [api:Nette\Mail\Mailer] arayüzünü gerçekleştirir ve tanıtacağımız birkaç hazır mailer bulunur.

Framework, DI container'a [yapılandırmaya |#Yapılandırma] göre otomatik olarak bir `Nette\Mail\Mailer` servisi ekler; onu [bağımlılık enjeksiyonuyla |dependency-injection:passing-dependencies] aktarılmasını sağlayarak elde edersiniz.


SendmailMailer
--------------

Varsayılan mailer, [php:mail] PHP fonksiyonunu kullanan SendmailMailer'dır. Örnek kullanım:

```php
$mailer = new Nette\Mail\SendmailMailer;
$mailer->send($mail);
```

`returnPath` ayarlamak istiyor ve sunucunuz onu yine de üzerine yazıyorsa, `$mailer->commandArgs = '-fmy@email.com'` kullanın.

`SendmailMailer` varsayılan olarak gönderenin adresini `mail()` fonksiyonuna envelope sender olarak (`-f` argümanı) aktarır. Bunu `$mailer->setEnvelopeSender(false)` ile kapatabilirsiniz.


SmtpMailer
----------

Postayı bir SMTP sunucusu üzerinden göndermek için `SmtpMailer` kullanın.

```php
$mailer = new Nette\Mail\SmtpMailer(
	host: 'smtp.gmail.com',
	username: 'john@gmail.com',
	password: '*****', // parolanız
	encryption: 'ssl', // ya da 'tls'
);
$mailer->send($mail);
```

Yapıcıya şu ek parametreler aktarılabilir:

* `port` - ayarlanmazsa varsayılan kullanılır: `ssl` için 465, `tls` için 587, aksi hâlde 25
* `timeout` - SMTP bağlantısı için zaman aşımı
* `persistent` - kalıcı bağlantı kullan
* `clientHost` - istemcinin host başlığını belirtir
* `streamOptions` - bağlantı için "SSL context seçeneklerini":https://www.php.net/manual/en/context.ssl.php ayarlamayı sağlar


OAuth 2.0 Kimlik Doğrulama .{data-version:4.2.0}
------------------------------------------------

Gmail ve Microsoft 365, SMTP için parola kimlik doğrulamasını sonlandırıyor ve bunun yerine bir OAuth 2.0 erişim token'ı gerektiriyor (XOAUTH2 mekanizması). Token'ı `setAccessToken()` metoduyla aktarın; kullanıcı adı kalır, parola boş bırakılır:

```php
$mailer = new Nette\Mail\SmtpMailer(
	host: 'smtp.gmail.com',
	username: 'john@gmail.com',
	password: '',
	encryption: 'tls',
);
$mailer->setAccessToken($accessToken);
```

Erişim token'larının süresi dolduğundan, bunun yerine bir callback aktarabilirsiniz; o her bağlantıda çağrılır, dolayısıyla her zaman taze bir token sağlayabilir. Token'ı elde etmek ve tazelemek size ya da OAuth kütüphanenize kalır:

```php
$mailer->setAccessToken(fn() => $oauthProvider->getFreshToken());
```


FallbackMailer
--------------

Bu mailer e-postaları doğrudan göndermez, gönderimi bir mailer kümesi aracılığıyla yürütür. Bir mailer başarısız olursa bir sonrakiyle yeniden dener. Sonuncusu da başarısız olursa yeniden ilkinden başlar.

```php
$mailer = new Nette\Mail\FallbackMailer([
	$smtpMailer,
	$backupSmtpMailer,
	$sendmailMailer,
]);
$mailer->send($mail);
```

Yapıcıdaki diğer parametreler, yeniden deneme sayısı (varsayılan `3`) ve aralarındaki milisaniye cinsinden bekleme süresidir (varsayılan `1000`). Tüm mailer'lar her denemede başarısız olursa, toplanan istisnaları `$failures` özelliğinde tutan bir `Nette\Mail\FallbackMailerException` fırlatılır.

Başarısızlığı kalıcı olan bir mailer, örneğin kimlik bilgilerini reddeden bir SMTP sunucusu, sonraki denemelerden çıkarılır; yeniden denemek sonucu değiştiremez. .{data-version:4.2.0}

Sonradan `addMailer()` ile başka bir mailer ekleyebilir ve her başarısız denemeden sonra çağrılan `$onFailure` olayını kaydedebilirsiniz:

```php
$mailer->onFailure[] = function ($mailer, $exception, $failedMailer, $mail) {
	// örneğin başarısız denemeyi günlükle
};
```


FileMailer .{data-version:4.2.0}
--------------------------------

Bu mailer hiçbir şey göndermez: her mesajı verilen dizine bir `.eml` dosyası olarak yazar. Dosyalar her e-posta istemcisinde açılır, dolayısıyla neyin gönderilmiş olacağını tam olarak denetleyebilirsiniz; testlerde ve geliştirme sırasında kullanışlıdır.

```php
$mailer = new Nette\Mail\FileMailer('/path/to/mails');
$mailer->send($mail);
```


E-postaları Hata Ayıklama .{data-version:4.1.2}
===============================================

Geliştirirken ya da bir staging sunucusu çalıştırırken, bir sınama e-postasının gerçek bir müşteriye kaçmasını istemezsiniz. Bunun asla olmadığından emin olmanın iki yolu vardır.

Önerilen yerel kurulum, makinenizde "Mailpit":https://mailpit.axllent.org ya da "MailHog":https://github.com/mailhog/MailHog gibi hafif bir SMTP yakalayıcı çalıştırmaktır. Bunlar her mesajı kabul eder, onu bir web arayüzünde gösterir ve hiçbir şeyi iletmez; Nette Mail'i yalnızca `127.0.0.1:1025` adresine yöneltirsiniz:

```neon
mail:
	smtp: true
	host: 127.0.0.1
	port: 1025
```

Staging için ya da yerel bir yakalayıcı çalıştıramadığınız ortamlar için Nette Mail'in yerleşik bir yönlendirmesi vardır. Hedefi yapılandırmada ayarlayın; her `To`, `Cc` ve `Bcc` alıcısı onunla değiştirilir. Nette Mail, e-postanın kime yönelik olduğunu görebilmeniz için özgün adresleri `X-Original-*` başlıklarında korur ve konuya bir işaretçi ekleyebilirsiniz:

```neon
mail:
	redirect:
		to: dev@example.com
		subjectPrefix: '[debug]'   # isteğe bağlı
```

Konu öneki gerekmediğinde `redirect: dev@example.com` kısayol biçimi çalışır. Hata ayıklama kipinde, gönderilen tüm e-postaları listeleyen bir [Tracy Bar |tracy:] paneli otomatik iliştirilir.

Bunu içeride, özel dinleyiciler (denetim günlükleri, ölçümler, …) için bir `$onSent` olayı da sunan [api:Nette\Mail\Interceptor] üstlenir.


DKIM
====

DKIM (DomainKeys Identified Mail), e-posta güvenilirliğini artırmaya yarayan ve sahte mesajları saptamaya da yardım eden bir teknolojidir. Gönderilen mesaj, gönderenin alan adının özel anahtarıyla imzalanır ve bu imza e-posta başlığında saklanır. Alıcının sunucusu bu imzayı, alan adının DNS kayıtlarında saklanan genel anahtarla karşılaştırır. İmza eşleşirse, e-postanın gerçekten gönderenin alan adından geldiğini ve mesajın iletim sırasında değiştirilmediğini kanıtlar.

Mailer'ı, e-postaları imzalayacak biçimde doğrudan [yapılandırmada |#Yapılandırma] ayarlayabilirsiniz. Bağımlılık enjeksiyonu kullanmıyorsanız şöyle kullanılır:

```php
$signer = new Nette\Mail\DkimSigner(
	domain: 'yourdomain.com',
	selector: 'dkim', // DNS kaydındaki selector
	privateKey: file_get_contents('/path/to/dkim.key'), // özel anahtarınızın yolu
	passPhrase: 'your_passphrase', // varsa özel anahtarın parolası
);

$mailer = new Nette\Mail\SendmailMailer; // ya da SmtpMailer
$mailer->setSigner($signer);
$mailer->send($mail);
```

Özel anahtar ya PEM biçiminde bir RSA anahtarı ya da base64 kodlanmış ham baytlar olarak bir Ed25519 anahtarı ("RFC 8463":https://datatracker.ietf.org/doc/html/rfc8463) olabilir; tip anahtarın kendisinden algılanır. Ed25519 ile imzalama `sodium` uzantısını gerektirir. .{data-version:4.2.0}

`oversignHeaders` parametresinde, zaten imzalanmış mesaja ikinci bir kopyanın eklenmesine karşı korunacak başlıkları listeleyebilirsiniz; bu, sahte e-postaların kullandığı bir hiledir ve olağan aday `From` başlığıdır. .{data-version:4.2.0}


Yapılandırma
============

Nette Mail için yapılandırma seçeneklerine genel bakış. Framework'ün tamamını değil de yalnızca bu kütüphaneyi kullanıyorsanız, [yapılandırmanın nasıl yükleneceğini|bootstrap:] okuyun.

E-posta göndermek için varsayılan olarak, başka bir yapılandırma gerektirmeyen `Nette\Mail\SendmailMailer` kullanılır. Ancak onu `Nette\Mail\SmtpMailer` ile değiştirebiliriz:

```neon
mail:
	# SmtpMailer kullan
	smtp: true       # (bool) varsayılan false

	host: ...        # (string) SMTP sunucusunun hostname'i
	port: ...        # (int) SMTP sunucusunun portu
	username: ...    # (string) SMTP kimlik doğrulama için kullanıcı adı
	password: ...    # (string) SMTP kimlik doğrulama için parola
	timeout: ...     # (int) SMTP bağlantısı için zaman aşımı
	encryption: ...  # (ssl|tls|null) varsayılan null ('secure' alias'ı)
	clientHost: ...  # (string) istemci hostname'i, varsayılan $_SERVER['HTTP_HOST'] ya da 'localhost'
	persistent: ...  # (bool) kalıcı bağlantı kullan, varsayılan false

	# SMTP bağlantısı için stream context seçenekleri, varsayılan stream_context_get_default()
	context:
		ssl:         # tüm seçenekler https://www.php.net/manual/en/context.ssl.php
			allow_self_signed: ...
			...
		http:        # seçenek listesi https://www.php.net/manual/en/context.http.php
			header: ...
			...
```

SSL sertifika doğrulamasını `context › ssl › verify_peer: false` seçeneğiyle kapatabilirsiniz. Uygulamayı savunmasız kıldığı için **bunu yapmamanızı güçlü biçimde öneririz**. Onun yerine "sertifikaları güven deposuna ekleyin":https://www.php.net/manual/en/openssl.configuration.php.

Güvenilirliği artırmak için e-postaları [DKIM teknolojisiyle |https://blog.nette.org/tr/sign-emails-with-dkim] imzalayabiliriz:

```neon
mail:
	dkim:
		domain: myweb.com                  # alan adınız
		selector: lovenette                # DKIM selector
		privateKey: %appDir%/cert/dkim.key # özel anahtar dosyanızın yolu
		passPhrase: ...                    # gerekiyorsa özel anahtarın parolası
```

Tüm e-postaları yönlendirme ve hata ayıklama panelini etkinleştirme seçenekleri [E-postaları Hata Ayıklama|#E-postaları Hata Ayıklama] bölümünde anlatılıyor:

```neon
mail:
	# tüm e-postaları tek bir adrese yönlendirir
	redirect: dev@example.com

	# Tracy panelini ve e-posta yakalamayı etkinleştirir (true) ya da kapatır (false)
	debugger: ...    # (bool) varsayılan null, yani hata ayıklama kipinde auto
```


DI Servisleri
=============

DI container'a şu servisler eklenir:

| Ad             | Tip                         | Açıklama
|-----------------------------------------------------
| `mail.mailer`	  | [api:Nette\Mail\Mailer]   | [e-posta gönderme sınıfı |#E-posta Gönderme]
| `mail.signer`	  | [api:Nette\Mail\Signer]   | [DKIM imzalama |#DKIM]


Daha yeni bir sürüme yükseltiyorsanız [yükseltme |upgrading] sayfasına bakın.

Nette Mail

Haber bültenleri ya da sipariş onayları gibi e-postalar göndermeyi mi planlıyorsunuz? Nette Framework, çok kullanıcı dostu bir API ile gereken araçları sunar. Size şunları göstereceğiz:

  • ekler dahil bir e-postanın nasıl oluşturulacağı
  • onun nasıl gönderileceği
  • e-postaların ve şablonların nasıl birleştirileceği

Kurulum

Kütüphaneyi Composer kullanarak indirin ve kurun:

composer require nette/mail

E-posta Oluşturma

E-posta bir Nette\Mail\Message nesnesidir. Bir tane şöyle oluşturalım:

$mail = new Nette\Mail\Message;
$mail->setFrom('John <john@example.com>')
	->addTo('peter@example.com')
	->addTo('jack@example.com')
	->setSubject('Order Confirmation')
	->setBody("Hello,\nYour order has been accepted.");

Belirtilen tüm parametreler UTF-8 kodlamasında olmalıdır.

jan@příklad.cz gibi uluslararasılaştırılmış alan adı taşıyan adresler, posta sunucularının gerektirdiği punycode denen ASCII biçimine otomatik dönüştürülür; bunun için intl uzantısı gerekir.

Alıcıları addTo() ile belirtmenin yanı sıra, kopya alıcılarını addCc() ile, gizli kopya alıcılarını ise addBcc() ile belirtebilirsiniz. setFrom() dahil tüm bu metotlar, alıcıyı üç şekilde kabul eder:

$mail->setFrom('john.doe@example.com');
$mail->setFrom('john.doe@example.com', 'John Doe');
$mail->setFrom('John Doe <john.doe@example.com>');

HTML ile yazılmış bir e-postanın gövdesi setHtmlBody() metoduyla aktarılır:

$mail->setHtmlBody('<p>Hello,</p><p>Your order has been accepted.</p>');

Metin alternatifi oluşturmanıza gerek yok; Nette onu sizin için otomatik üretecek. Ve e-postanın konusu ayarlanmamışsa, onu <title> elemanından almaya çalışacak.

Görseller de HTML gövdesine olağanüstü kolay biçimde gömülebilir. Görsellerin fiziksel olarak bulunduğu yolu ikinci parametre olarak aktarmanız yeter; Nette onları e-postaya otomatik ekleyecek:

// /path/to/images/background.gif dosyasını e-postaya otomatik ekler
$mail->setHtmlBody(
	'<b>Hello</b> <img src="background.gif">',
	'/path/to/images',
);

Görsel gömme algoritması şu kalıpları arar: <img src=...>, <body background=...>, HTML style niteliğinin içindeki url(...) ve özel [[...]] sözdizimi.

E-posta göndermek daha da kolay olabilir miydi?

E-postalar kartpostal gibidir. Parolaları ya da başka kimlik bilgilerini asla e-postayla göndermeyin.

Diğer Seçenekler

Message nesnesi ayrıca bir yanıt adresi, geri dönen mesajlar için bir dönüş yolu ve mesaj önceliği ayarlamanızı da sağlar:

$mail->addReplyTo('reply@example.com', 'Support')
	->setReturnPath('bounces@example.com')
	->setPriority(Nette\Mail\Message::High);

Öncelik, Message::High, Message::Normal ya da Message::Low sabitlerinden biridir.

Tek Tıkla Abonelikten Çıkma

Gmail ve Yahoo, haber bültenleri gibi toplu postaların doğrudan posta istemcisinde tek tıkla abonelikten çıkma sunmasını gerektirir. Bunu, RFC 8058 tarafından tanımlanan bir başlık çifti üstlenir; setUnsubscribe() metodu onları sizin için doğru biçimde ayarlar:

$mail->setUnsubscribe('https://example.com/unsubscribe?token=xyz');

URL, alıcının aboneliğini yalın bir HTTP POST isteğine yanıt olarak, başka bir onay olmadan sonlandırmalıdır. İkinci parametre, POST gönderemeyen istemciler için yedek olarak bir e-posta adresi sağlayabilir; tek başına da çalışır: $mail->setUnsubscribe(email: 'unsubscribe@example.com').

Ekler

Elbette e-postalara dosya ekleyebilirsiniz. Bunun için addAttachment(string $file, ?string $content = null, ?string $contentType = null) metodunu kullanın.

// /path/to/example.zip dosyasını e-postaya example.zip adıyla ekler
$mail->addAttachment('/path/to/example.zip');

// /path/to/example.zip dosyasını info.zip adıyla ekler
$mail->addAttachment('info.zip', file_get_contents('/path/to/example.zip'));

// example.txt dosyasını "Hello John!" içeriğiyle ekler
$mail->addAttachment('example.txt', 'Hello John!');

Bir dosyayı addEmbeddedFile() ile doğrudan HTML gövdesine de gömebilirsiniz. Bu metot, HTML'de Content-ID değerine başvuracağınız, oluşturulan MIME parçasını döndürür (otomatik görsel gömme içeride tam olarak bu mekanizmayı kullanır):

$file = $mail->addEmbeddedFile('/path/to/logo.png');
$mail->setHtmlBody('<img src="cid:' . trim($file->getHeader('Content-ID'), '<>') . '">');

Şablonlar

HTML e-postalar gönderiyorsanız, onları Latte şablon sisteminde yazmak harika bir seçenektir. Bu nasıl yapılır?

$latte = new Latte\Engine;
$params = [
	'orderId' => 123,
];

$mail = new Nette\Mail\Message;
$mail->setFrom('John <john@example.com>')
	->addTo('jack@example.com')
	->setHtmlBody(
		$latte->renderToString('/path/to/email.latte', $params),
		'/path/to/images',
	);

email.latte dosyası:

<html>
<head>
	<meta charset="utf-8">
	<title>Order Confirmation</title>
	<style>
	body {
		background: url("background.png")
	}
	</style>
</head>
<body>
	<p>Hello,</p>

	<p>Your order number {$orderId} has been accepted.</p>
</body>
</html>

Nette tüm görselleri otomatik gömer, konuyu <title> elemanına göre ayarlar ve HTML için bir metin alternatifi üretir.

Nette Application'da Kullanım

E-postaları Nette Application ile, yani presenter'larla birlikte kullanıyorsanız, şablonlarda n:href niteliğini ya da {link} etiketini kullanarak bağlantı oluşturmak isteyebilirsiniz. Latte bunları varsayılan olarak bilmez, ama eklemek çok kolaydır. Nette\Application\LinkGenerator nesnesi bağlantı oluşturabilir; onu bağımlılık enjeksiyonuyla aktarılmasını sağlayarak elde edersiniz:

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;
	}
}

Şablonda bağlantıları sonra alışık olduğunuz gibi oluşturursunuz. LinkGenerator ile oluşturulan tüm bağlantılar mutlak olacak.

<a n:href="Presenter:action">Link</a>

CSS Satır İçine Alma

Nette\Mail\CssInliner, e-postaların tüm istemcilerde tutarlı render edilmesi için CSS kurallarını satır içi style niteliklerine dönüştürür. Ayrıca Outlook uyumluluğu için HTML nitelikleri üretir.

PHP 8.4 ya da daha yenisini ve dom uzantısını gerektirir.

Çoğu e-posta istemcisi <style> etiketlerini sınırlı destekler ya da onları tümüyle yok sayar. Doğru render'ı güvence altına almak için CSS kurallarının tek tek elemanlar üzerinde satır içi style niteliklerine dönüştürülmesi gerekir. HTML'inizi inline() içinden geçirmeniz yeter:

$inliner = new Nette\Mail\CssInliner;
$html = $inliner->inline($html);

Örneğin HTML şunu içeriyorsa:

<style>
p { margin: 0; color: #333; }
a { color: #a0704e; }
</style>
<p>Hello <a href="#">world</a></p>

Satır içine alma sonrası sonuç şöyle olacak (<style> etiketi korunur, ama burada kısalık için atlandı):

<p style="margin: 0; color: #333">Hello <a href="#" style="color: #a0704e">world</a></p>

<style> etiketi çıktıda her zaman korunur, böylece @media sorguları ve satır içine alınamayan diğer kurallar çalışmayı sürdürür.

Stilleri <style> etiketlerinden ayıklamanın yanı sıra, CSS'i addCss() metoduyla da sağlayabilirsiniz. CSS'i, HTML'i setHtmlBody() metoduna aktarmadan önce satır içine almanız gerekir:

$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);

Birden çok kural bir elemanın aynı özelliğini hedeflediğinde, kazananı tıpkı tarayıcıdaki gibi CSS kaskadı belirler: !important bildirimleri olağanları yener, var olan bir satır içi style niteliği her seçiciyi yener, daha özgül bir seçici daha az özgül olanı yener ve eşitlikte sonraki kural kazanır. <style> etiketlerinden gelen kurallar, addCss() ile eklenenlerden önce işlenir ve yalnızca kazanan değer yazılır.

@media ya da @font-face gibi at-kuralları satır içine alma sırasında atlanır. :hover gibi sözde sınıfların anlamlı biçimde satır içine alınamayacağını unutmayın; çünkü satır içi stiller dinamik durumları desteklemez.

Outlook İçin HTML Nitelikleri

Microsoft Outlook'un masaüstü sürümleri, pek çok CSS özelliğini anlamayan Word render motorunu kullanır. Uyumluluğu güvence altına almak için CssInliner, satır içi stillerin yanı sıra CSS kurallarından karşılık gelen HTML niteliklerini otomatik üretir:

CSS Özelliği HTML Niteliği Uygulandığı Yer
background-color bgcolor <table>, <td>, <th>, <body><tr>
width width <table>, <td>, <th><img>
height height <table>, <td>, <th><img>
border-spacing cellspacing <table>

width, height ve cellspacing için px birimi otomatik olarak atılır (örneğin width: 600px değeri width="600" olur), yüzde % işaretini korur ve auto ya da calc() gibi bir niteliğin ifade edemeyeceği değerler hiç nitelik üretmez. Hem satır içi stil hem HTML niteliği birlikte ayarlanır, böylece e-posta modern istemcilerde de Outlook'ta da doğru render edilir.

HTML nitelikleri yalnızca CssInliner tarafından işlenen CSS kurallarından üretilir, özgün HTML'de zaten bulunan style niteliklerinden değil.

E-posta Gönderme

Mailer, e-posta göndermekten sorumlu bir sınıftır. Nette\Mail\Mailer arayüzünü gerçekleştirir ve tanıtacağımız birkaç hazır mailer bulunur.

Framework, DI container'a yapılandırmaya göre otomatik olarak bir Nette\Mail\Mailer servisi ekler; onu bağımlılık enjeksiyonuyla aktarılmasını sağlayarak elde edersiniz.

SendmailMailer

Varsayılan mailer, mail PHP fonksiyonunu kullanan SendmailMailer'dır. Örnek kullanım:

$mailer = new Nette\Mail\SendmailMailer;
$mailer->send($mail);

returnPath ayarlamak istiyor ve sunucunuz onu yine de üzerine yazıyorsa, $mailer->commandArgs = '-fmy@email.com' kullanın.

SendmailMailer varsayılan olarak gönderenin adresini mail() fonksiyonuna envelope sender olarak (-f argümanı) aktarır. Bunu $mailer->setEnvelopeSender(false) ile kapatabilirsiniz.

SmtpMailer

Postayı bir SMTP sunucusu üzerinden göndermek için SmtpMailer kullanın.

$mailer = new Nette\Mail\SmtpMailer(
	host: 'smtp.gmail.com',
	username: 'john@gmail.com',
	password: '*****', // parolanız
	encryption: 'ssl', // ya da 'tls'
);
$mailer->send($mail);

Yapıcıya şu ek parametreler aktarılabilir:

  • port – ayarlanmazsa varsayılan kullanılır: ssl için 465, tls için 587, aksi hâlde 25
  • timeout – SMTP bağlantısı için zaman aşımı
  • persistent – kalıcı bağlantı kullan
  • clientHost – istemcinin host başlığını belirtir
  • streamOptions – bağlantı için SSL context seçeneklerini ayarlamayı sağlar

OAuth 2.0 Kimlik Doğrulama

Gmail ve Microsoft 365, SMTP için parola kimlik doğrulamasını sonlandırıyor ve bunun yerine bir OAuth 2.0 erişim token'ı gerektiriyor (XOAUTH2 mekanizması). Token'ı setAccessToken() metoduyla aktarın; kullanıcı adı kalır, parola boş bırakılır:

$mailer = new Nette\Mail\SmtpMailer(
	host: 'smtp.gmail.com',
	username: 'john@gmail.com',
	password: '',
	encryption: 'tls',
);
$mailer->setAccessToken($accessToken);

Erişim token'larının süresi dolduğundan, bunun yerine bir callback aktarabilirsiniz; o her bağlantıda çağrılır, dolayısıyla her zaman taze bir token sağlayabilir. Token'ı elde etmek ve tazelemek size ya da OAuth kütüphanenize kalır:

$mailer->setAccessToken(fn() => $oauthProvider->getFreshToken());

FallbackMailer

Bu mailer e-postaları doğrudan göndermez, gönderimi bir mailer kümesi aracılığıyla yürütür. Bir mailer başarısız olursa bir sonrakiyle yeniden dener. Sonuncusu da başarısız olursa yeniden ilkinden başlar.

$mailer = new Nette\Mail\FallbackMailer([
	$smtpMailer,
	$backupSmtpMailer,
	$sendmailMailer,
]);
$mailer->send($mail);

Yapıcıdaki diğer parametreler, yeniden deneme sayısı (varsayılan 3) ve aralarındaki milisaniye cinsinden bekleme süresidir (varsayılan 1000). Tüm mailer'lar her denemede başarısız olursa, toplanan istisnaları $failures özelliğinde tutan bir Nette\Mail\FallbackMailerException fırlatılır.

Başarısızlığı kalıcı olan bir mailer, örneğin kimlik bilgilerini reddeden bir SMTP sunucusu, sonraki denemelerden çıkarılır; yeniden denemek sonucu değiştiremez.

Sonradan addMailer() ile başka bir mailer ekleyebilir ve her başarısız denemeden sonra çağrılan $onFailure olayını kaydedebilirsiniz:

$mailer->onFailure[] = function ($mailer, $exception, $failedMailer, $mail) {
	// örneğin başarısız denemeyi günlükle
};

FileMailer

Bu mailer hiçbir şey göndermez: her mesajı verilen dizine bir .eml dosyası olarak yazar. Dosyalar her e-posta istemcisinde açılır, dolayısıyla neyin gönderilmiş olacağını tam olarak denetleyebilirsiniz; testlerde ve geliştirme sırasında kullanışlıdır.

$mailer = new Nette\Mail\FileMailer('/path/to/mails');
$mailer->send($mail);

E-postaları Hata Ayıklama

Geliştirirken ya da bir staging sunucusu çalıştırırken, bir sınama e-postasının gerçek bir müşteriye kaçmasını istemezsiniz. Bunun asla olmadığından emin olmanın iki yolu vardır.

Önerilen yerel kurulum, makinenizde Mailpit ya da MailHog gibi hafif bir SMTP yakalayıcı çalıştırmaktır. Bunlar her mesajı kabul eder, onu bir web arayüzünde gösterir ve hiçbir şeyi iletmez; Nette Mail'i yalnızca 127.0.0.1:1025 adresine yöneltirsiniz:

mail:
	smtp: true
	host: 127.0.0.1
	port: 1025

Staging için ya da yerel bir yakalayıcı çalıştıramadığınız ortamlar için Nette Mail'in yerleşik bir yönlendirmesi vardır. Hedefi yapılandırmada ayarlayın; her To, Cc ve Bcc alıcısı onunla değiştirilir. Nette Mail, e-postanın kime yönelik olduğunu görebilmeniz için özgün adresleri X-Original-* başlıklarında korur ve konuya bir işaretçi ekleyebilirsiniz:

mail:
	redirect:
		to: dev@example.com
		subjectPrefix: '[debug]'   # isteğe bağlı

Konu öneki gerekmediğinde redirect: dev@example.com kısayol biçimi çalışır. Hata ayıklama kipinde, gönderilen tüm e-postaları listeleyen bir Tracy Bar paneli otomatik iliştirilir.

Bunu içeride, özel dinleyiciler (denetim günlükleri, ölçümler, …) için bir $onSent olayı da sunan Nette\Mail\Interceptor üstlenir.

DKIM

DKIM (DomainKeys Identified Mail), e-posta güvenilirliğini artırmaya yarayan ve sahte mesajları saptamaya da yardım eden bir teknolojidir. Gönderilen mesaj, gönderenin alan adının özel anahtarıyla imzalanır ve bu imza e-posta başlığında saklanır. Alıcının sunucusu bu imzayı, alan adının DNS kayıtlarında saklanan genel anahtarla karşılaştırır. İmza eşleşirse, e-postanın gerçekten gönderenin alan adından geldiğini ve mesajın iletim sırasında değiştirilmediğini kanıtlar.

Mailer'ı, e-postaları imzalayacak biçimde doğrudan yapılandırmada ayarlayabilirsiniz. Bağımlılık enjeksiyonu kullanmıyorsanız şöyle kullanılır:

$signer = new Nette\Mail\DkimSigner(
	domain: 'yourdomain.com',
	selector: 'dkim', // DNS kaydındaki selector
	privateKey: file_get_contents('/path/to/dkim.key'), // özel anahtarınızın yolu
	passPhrase: 'your_passphrase', // varsa özel anahtarın parolası
);

$mailer = new Nette\Mail\SendmailMailer; // ya da SmtpMailer
$mailer->setSigner($signer);
$mailer->send($mail);

Özel anahtar ya PEM biçiminde bir RSA anahtarı ya da base64 kodlanmış ham baytlar olarak bir Ed25519 anahtarı (RFC 8463) olabilir; tip anahtarın kendisinden algılanır. Ed25519 ile imzalama sodium uzantısını gerektirir.

oversignHeaders parametresinde, zaten imzalanmış mesaja ikinci bir kopyanın eklenmesine karşı korunacak başlıkları listeleyebilirsiniz; bu, sahte e-postaların kullandığı bir hiledir ve olağan aday From başlığıdır.

Yapılandırma

Nette Mail için yapılandırma seçeneklerine genel bakış. Framework'ün tamamını değil de yalnızca bu kütüphaneyi kullanıyorsanız, yapılandırmanın nasıl yükleneceğini okuyun.

E-posta göndermek için varsayılan olarak, başka bir yapılandırma gerektirmeyen Nette\Mail\SendmailMailer kullanılır. Ancak onu Nette\Mail\SmtpMailer ile değiştirebiliriz:

mail:
	# SmtpMailer kullan
	smtp: true       # (bool) varsayılan false

	host: ...        # (string) SMTP sunucusunun hostname'i
	port: ...        # (int) SMTP sunucusunun portu
	username: ...    # (string) SMTP kimlik doğrulama için kullanıcı adı
	password: ...    # (string) SMTP kimlik doğrulama için parola
	timeout: ...     # (int) SMTP bağlantısı için zaman aşımı
	encryption: ...  # (ssl|tls|null) varsayılan null ('secure' alias'ı)
	clientHost: ...  # (string) istemci hostname'i, varsayılan $_SERVER['HTTP_HOST'] ya da 'localhost'
	persistent: ...  # (bool) kalıcı bağlantı kullan, varsayılan false

	# SMTP bağlantısı için stream context seçenekleri, varsayılan stream_context_get_default()
	context:
		ssl:         # tüm seçenekler https://www.php.net/manual/en/context.ssl.php
			allow_self_signed: ...
			...
		http:        # seçenek listesi https://www.php.net/manual/en/context.http.php
			header: ...
			...

SSL sertifika doğrulamasını context › ssl › verify_peer: false seçeneğiyle kapatabilirsiniz. Uygulamayı savunmasız kıldığı için bunu yapmamanızı güçlü biçimde öneririz. Onun yerine sertifikaları güven deposuna ekleyin.

Güvenilirliği artırmak için e-postaları DKIM teknolojisiyle imzalayabiliriz:

mail:
	dkim:
		domain: myweb.com                  # alan adınız
		selector: lovenette                # DKIM selector
		privateKey: %appDir%/cert/dkim.key # özel anahtar dosyanızın yolu
		passPhrase: ...                    # gerekiyorsa özel anahtarın parolası

Tüm e-postaları yönlendirme ve hata ayıklama panelini etkinleştirme seçenekleri E-postaları Hata Ayıklama bölümünde anlatılıyor:

mail:
	# tüm e-postaları tek bir adrese yönlendirir
	redirect: dev@example.com

	# Tracy panelini ve e-posta yakalamayı etkinleştirir (true) ya da kapatır (false)
	debugger: ...    # (bool) varsayılan null, yani hata ayıklama kipinde auto

DI Servisleri

DI container'a şu servisler eklenir:

Ad Tip Açıklama
mail.mailer Nette\Mail\Mailer e-posta gönderme sınıfı
mail.signer Nette\Mail\Signer DKIM imzalama

Daha yeni bir sürüme yükseltiyorsanız yükseltme sayfasına bakın.