Mail Modülü Yazma

1.9k görüntülenme Markdown

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

Yapı

Dizin adı, dosya adı ve sınıf adı aynı dizedir. Kayıt mekanizmasının tamamı budur.

düzen
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

  1. coremio/modules/Mail/Acme/Acme.php oluşturun, erişim koruyucusuyla açın ve class Acme tanımlayın.
  2. Yapıcıda modülün ayarlarını ve metinlerini yükleyin, isteğe bağlı geçersiz kılma dizisini birleştirin.
  3. subject, body, AddAddress ve addAttachment metotlarını her biri $this döndürecek şekilde uygulayın.
  4. Teslim kaydı için getSubject, getBody, getAddresses uygulayın.
  5. submit() uygulayın: başarıda doğru değer, hatada $this->error dolar ve false döner.

Ayar Sayfası

  1. Formu döndüren page_settings() ekleyin. Çekirdek bunu pages/settings.php dosyasına tercih eder.
  2. Üç gizli alan gönderin: operation, controller adı ve modül adı.
  3. Etkinleştirme kutusunu ekleyin; etkin sürücü sizin sınıfınıza eşitse işaretli olsun.
  4. controller_save() ekleyin: değişen alanları config.php içine yazın, sonra modules/mail anahtarını çevirin.
  5. İsteğe bağlı: controller_test_connection() ve formu o controller ile gönderen bir buton.

Etkinleştirme ve Doğrulama

  1. {admin}/modules/mail açın, modülünüzü seçin, kimlik bilgilerini doldurun ve etkinleştirin.
  2. Kaydetmek sınıf adını modules/mail anahtarına yazar; önceki sürücü kapanır.
  3. 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ırNe döndürmeli
__construct($external_config = [])Mesaj başına bir kezhiç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 sonrabaş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 sonrahata 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.

tam imzalar
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:

body() içinde şablon işleme
// 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.

meta.name Modül listesindeki görünen ad. Dil dosyasındaki name anahtarı üstün gelir.
meta.version Modülün yanındaki sürüm dizesi. Serbest biçimli.
meta.logo Modül dizinindeki logo dosyasının adı. Yazılmazsa liste sırasıyla logo.svg, logo.webp, logo.png arar.
fname Gönderen görünen adı. Hazır sürücülerin hepsi bu anahtarı kullanır.
from Gönderen adresi. Mailjet buna femail der; setFromEmail() bu yüzden vardır.
Crypt::encode() Sırlar şifreli saklanı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ımArananSonuç
1Modül içinde 5 bayttan büyük controllers/{controller}.phpinclude edilir, dönüşü yanıt olur
2Örnekte controller_{controller}(), tireler alt çizgi olaraktry/catch içinde çalışır, dönüşü yanıt olur
yedekikisi 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.

coremio/modules/Mail/Acme/Acme.php
<?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'];
    }
}
coremio/helpers/notification.php
// 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

subject() alıcı listesini temizler, bu gereklidir

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.

submit() hatayı fırlatarak değil false döndürerek bildirir

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.

Sırrı geçirgen filtreyle okuyun

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.

Sürücünüzü etkinleştirmek öncekini devre dışı bırakır

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.

Faydalı oldu mu?

Geri bildiriminiz için teşekkürler!

Hâlâ Yardıma mı İhtiyacınız Var?

Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.