Mail Modülü Yazma
Mail modülü e-postayı teslim eden sürücüdür: bildirim yardımcısının her mesajda çağırdığı metotları uygular.
Genel Bakış
MailModule diye bir taban sınıf yoktur: tip ördek tiplemesiyle çalışır. Çekirdek, modül dizininin adını taşıyan sınıfı yükler ve üzerinde sabit bir metot kümesini çağırır.
Aynı anda tek bir Mail modülü etkindir; adı modules/mail içinde durur. Bu anahtarı, operatör etkinleştirdiğinde modül kendi yazar.
SampleMail ile başlayın: sözleşmenin tamamını uygular ve mesajları diske yazar.
Ön Koşullar
coremio/modules/Mail/altına yazma erişimi.- Sunucudan erişilebilen bir taşıyıcı: SMTP ya da anahtarı olan bir HTTP API'si.
- Ayar formu: Admin Form Oluşturucu.
- Arka plan: Bildirim Şablonları Nasıl Çalışır.
Yapı
Dizin adı, dosya adı ve sınıf adı aynı dizedir. Kayıt mekanizmasının tamamı budur.
coremio/modules/Mail/Acme/
├── Acme.php sürücü sınıfı, adı Acme, ad alanı yok
├── config.php dizi döndürür: künye + kaydedilmiş ayarlar
├── lang/en.php düz bir key => string dizisi döndürür
├── lang/tr.php
├── logo.png isteğe bağlı, config meta.logo ile gösterilir
└── pages/ isteğe bağlı, page_settings() yerine settings.php
Sınıf hiçbir ad alanı taşımaz. İlk satır doğrudan erişim koruyucusudur.
Adım Adım
Sürücü Sınıfını Kurma
coremio/modules/Mail/Acme/Acme.phpoluşturun, erişim koruyucusuyla açın veclass Acmetanımlayın.- Yapıcıda modülün ayarlarını ve metinlerini yükleyin, isteğe bağlı geçersiz kılma dizisini birleştirin.
subject,body,AddAddressveaddAttachmentmetotlarını her biri$thisdöndürecek şekilde uygulayın.- Teslim kaydı için
getSubject,getBody,getAddressesuygulayın. submit()uygulayın: başarıda doğru değer, hatada$this->errordolar ve false döner.
Ayar Sayfası
- Formu döndüren
page_settings()ekleyin. Çekirdek bunupages/settings.phpdosyasına tercih eder. - Üç gizli alan gönderin: operation, controller adı ve modül adı.
- Etkinleştirme kutusunu ekleyin; etkin sürücü sizin sınıfınıza eşitse işaretli olsun.
controller_save()ekleyin: değişen alanlarıconfig.phpiçine yazın, sonramodules/mailanahtarını çevirin.- İsteğe bağlı:
controller_test_connection()ve formu o controller ile gönderen bir buton.
Etkinleştirme ve Doğrulama
{admin}/modules/mailaçın, modülünüzü seçin, kimlik bilgilerini doldurun ve etkinleştirin.- Kaydetmek sınıf adını
modules/mailanahtarına yazar; önceki sürücü kapanır. - Bir bildirim gönderin. Geliştirmede SampleMail kullanın ve yakalanan dosyayı okuyun.
Referans
Çekirdek Neyi, Nereden Çağırır
Yardımcının kullandığı metotlar yalnız bunlardır.
| Metot | Çekirdek ne zaman çağırır | Ne döndürmeli |
|---|---|---|
__construct($external_config = []) | Mesaj başına bir kez | hiçbir şey |
body($text, $template, $variables, $lang, $user) | İlk sırada | $this |
subject($arg) | body'den sonra, çağıran konuyu değiştiriyorsa | $this |
addAttachment($path, $name) | Ek başına bir kez | $this |
AddAddress($address, $name) | En son, alıcı başına bir kez | $this |
submit($isthis = false) | Alıcı eklendikten sonra | başarıda doğru değer, hatada false |
getSubject() | Başarılı gönderimden sonra (log satırı) | dize |
getBody() | Başarılı gönderimden sonra (log satırı) | dize |
getAddresses() | Başarılı gönderimden sonra (log satırı) | düz adres dizisi |
$error (public özellik) | Yanlış değer dönen gönderimden sonra | hata metni |
İki metot isteğe bağlıdır: set_credentials(array $data) kayıtlı kimlik bilgilerini geçersiz kılar, setFromEmail() ve setFromName() ise göndereni.
Sürücü Metot İmzaları
Bu imzaları birebir kopyalayın: çekirdek bazılarını kabul ettiklerinden daha az argümanla çağırır.
public function __construct($external_config = []);
// $text ham gövde, $template false olduğunda olduğu gibi kullanılır
// $template "group/name", örn. "invoice/invoice-created"; false işlemeyi atlar
// $variables şablona verilen yer tutucu haritası
// $lang alıcının dil kodu, operatörünki değil
// $user alıcının kullanıcı id'si, hesabı olmayan bir adres için 0
public function body($text = '', $template = false, $variables = [], $lang = '', $user = 0);
// Konuyu ayarlamadan önce alıcı listesini sıfırlar. Bu bilinçlidir, bkz. Tuzaklar.
public function subject($arg = '');
// $arg1 ya bir adres dizesidir ya da address => name haritasıdır.
public function AddAddress($arg1 = '', $arg2 = '');
public function addAttachment($path = '', $name = '');
public function setFromEmail($email = '');
public function setFromName($name = '');
public function set_credentials($data = []);
// $isthis = true boolean yerine sürücüyü döndürür, böylece çağrı zincirlenebilir.
public function submit($isthis = false);
public function getSubject();
public function getBody();
public function getAddresses();
public function address_reset();
$template dolu geldiğinde sürücü şablonu kendisi işler. Çağrı ve dönüş biçimi sabittir:
// View::notifications($type, $template_name, $content, $variables, $lang, $user): array
$look = View::notifications("mail", $template, $text, $variables, $lang, $user);
// ['subject' => '...', 'content' => '...'] döndürür, şablon eksikse false.
if ($look !== false && isset($look["subject"]) && isset($look["content"])) {
$this->subject($look["subject"]);
$text = $look["content"];
}
Config Dosyası ve Anahtarları
config.php düz bir dizi döndürür. meta anahtarını modül listesi okur; diğer anahtarlar sizindir.
name anahtarı üstün gelir.
logo.svg, logo.webp, logo.png arar.
femail der; setFromEmail() bu yüzden vardır.
Crypt::encode($v, Config::get("crypt/user")) ile yazın, Crypt::decode() ile okuyun. config.php dosyasına düz metin anahtar yapıştırmayın.
Ayar Gönderimi Size Nasıl Ulaşır
Ayar formu operation=module_controller gönderir. Bu operation modülü yükler, sonra controller adını iki adımda çözer.
| Adım | Aranan | Sonuç |
|---|---|---|
| 1 | Modül içinde 5 bayttan büyük controllers/{controller}.php | include edilir, dönüşü yanıt olur |
| 2 | Örnekte controller_{controller}(), tireler alt çizgi olarak | try/catch içinde çalışır, dönüşü yanıt olur |
| yedek | ikisi de yoksa | ['status' => 'error', 'message' => 'Module controller not found'] |
Yani controller=test-connection gönderimi controller_test_connection() metoduna ulaşır. İçinde fırlatabilirsiniz; çözücü yakalar. Şunu döndürün: ['status' => 'successful', 'message' => '...'].
Örnek
Eksiksiz bir sürücü, sonra onu süren çekirdek kodu.
<?php
defined('CORE_FOLDER') OR exit('You can not get in here!');
class Acme
{
public $error = null;
public $lang = [];
public $config = [];
public $credentials;
private $subject = '';
private $body = '';
private $addresses = [];
private $attachments = [];
public function __construct($external_config = [])
{
$config = Modules::Config('Mail', __CLASS__);
$this->lang = Modules::Lang('Mail', __CLASS__);
$this->config = array_merge($config ?: [], $external_config);
}
public function set_credentials($data = [])
{
$this->credentials = $data;
return $this;
}
public function subject($arg = '')
{
// Dağıtıcının alıcı döngüsünün birikmesini engelleyen şey burada temizlemektir.
$this->address_reset();
$this->subject = (string) $arg;
return $this;
}
public function body($text = '', $template = false, $variables = [], $lang = '', $user = 0)
{
if ($template) {
$look = View::notifications('mail', $template, $text, $variables, $lang, $user);
if ($look !== false && isset($look['subject']) && isset($look['content'])) {
$this->subject($look['subject']);
$text = $look['content'];
}
}
$this->body = (string) $text;
return $this;
}
public function setFromEmail($email = '')
{
$this->config['from'] = $email;
return $this;
}
public function setFromName($name = '')
{
$this->config['fname'] = $name;
return $this;
}
public function AddAddress($arg1 = '', $arg2 = '')
{
if (is_array($arg1)) foreach ($arg1 as $address => $name) $this->addresses[$address] = $name;
else $this->addresses[$arg1] = $arg2;
return $this;
}
public function addAttachment($path = '', $name = '')
{
$this->attachments[] = ['path' => $path, 'name' => $name];
return $this;
}
public function getAddresses()
{
return array_keys($this->addresses);
}
public function getSubject()
{
return $this->subject;
}
public function getBody()
{
return $this->body;
}
public function address_reset()
{
$this->addresses = [];
return true;
}
public function submit($isthis = false)
{
$config = $this->credentials ?: $this->config;
$key = Crypt::decode($config['api_key'] ?? '', Config::get("crypt/user"));
$payload = [
'from' => ['email' => $config['from'] ?? '', 'name' => $config['fname'] ?? ''],
'to' => array_map(fn ($a, $n) => ['email' => $a, 'name' => $n], array_keys($this->addresses), $this->addresses),
'subject' => $this->subject,
'html' => $this->body,
];
$response = Utility::HttpRequest([
'url' => 'https://api.example.com/v1/send',
'type' => 'POST',
'data' => Utility::jencode($payload),
'header' => ['Authorization: Bearer ' . $key, 'Content-Type: application/json'],
]);
$decoded = Utility::jdecode((string) $response, true) ?: [];
$sent = (string) ($decoded['status'] ?? '') === 'queued';
// Dağıtıcı yanlış değer dönüşünden sonra $this->error alanını okur; burada fırlatmaları yakalamaz.
if (!$sent) $this->error = $decoded['message'] ?? 'Acme refused the message.';
return $isthis ? $this : $sent;
}
public function controller_save(): array
{
$from = (string) Filter::init("POST/from", "email");
$fname = (string) Filter::init("POST/fname", "hclear");
$apiKey = (string) Filter::init("POST/api_key", "password");
if (!$from) throw new Exception($this->lang['error-from-required'] ?? 'Sender address is required.');
$sets = [];
if ($from !== ($this->config['from'] ?? '')) $sets['from'] = $from;
if ($fname !== ($this->config['fname'] ?? '')) $sets['fname'] = $fname;
// Form kayıtlı anahtar için bir maske gösterir; maske gerçek değerin üzerine asla yazmamalıdır.
if ($apiKey !== '*****' && $apiKey !== Crypt::decode($this->config['api_key'] ?? '', Config::get("crypt/user")))
$sets['api_key'] = Crypt::encode($apiKey, Config::get("crypt/user"));
if ($sets) {
$merged = array_replace_recursive($this->config, $sets);
$write = FileManager::file_write(__DIR__ . DS . "config.php", Utility::array_export($merged, ['pwith' => true]));
if (!$write) throw new Exception('Failed to save settings');
}
$status = (bool) (int) Filter::init("POST/status", "numbers");
$current = Config::get("modules/mail") == __CLASS__;
if ($current != $status) {
$modules = Config::get("modules");
$modules['mail'] = $status ? __CLASS__ : 'none';
Config::save("modules", Config::set("modules", $modules));
}
return ['status' => "successful", 'message' => $this->lang['settings-save-successful'] ?? 'Saved'];
}
}
// Modül adı verilmeden çağrılır, Load ETKİN sürücüyü modules/mail içinden çözer.
Modules::Load("Mail");
$mailModule = Config::get("modules/mail");
$mail = $mailModule && $mailModule !== 'none' ? new $mailModule() : false;
// Alıcı başına bir geçiş. Sıraya dikkat: body, subject, attachments, address, submit.
foreach ($adminContacts['emails'] as $address => $nameStr) {
$parse = explode("|", (string) $nameStr);
$aLang = $parse[1] ?? $localLang;
$sendMail = $mail->body($body, $templatePath, $variables, $aLang);
if ($subject) $mail->subject($subject);
if ($attachments) foreach ($attachments as $fn => $fname) $sendMail->addAttachment($fn, $fname);
$sendMail = $sendMail->addAddress($address, $parse[0] ?? '')->submit();
if ($sendMail) LogManager::Mail_Log(0, $reason, $mail->getSubject(), $mail->getBody(), implode(",", $mail->getAddresses()));
else $errors['mail'][$address] = $mail->error;
}
Bu biçimi üç çağrı noktası kullanır: şablon dağıtıcısı, kuyruk işçisi ve toplu gönderici.
Tuzaklar
Hazır dört sürücünün hepsi subject() ilk satırında address_reset() çağırır. Dağıtıcı alıcı başına aynı örneği kullanır; bu sıfırlama olmazsa ikinci alıcı birinciye de adreslenmiş bir kopya alır.
Dağıtım döngüsü try/catch içinde değildir ve yanlış değer dönen çağrıdan sonra $mail->error alanını okur. submit() içinden fırlatmak döngüyü keser ve kalan alıcıları düşürür. controller_* metotları tam tersidir: orada fırlatabilirsiniz.
Diğer her filtre, API anahtarını güçlü kılan karakterleri kaldırır. Form kayıtlı anahtarı beş yıldız olarak gösterir; bu literali atlayan bir kaydetme, çalışan anahtarın üzerine maskeyi yazar.
modules/mail tek bir sınıf adı tutar. SampleMail her şeyi kabul eder, mesajı temp/sample-mail/ altına EML olarak yazar ve yerel kısmı fail ile başlayan alıcı için hata simüle eder.
İlgili Makaleler
Geri bildiriminiz için teşekkürler!
Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.