Modül Yaşam Döngüsü

1.7k görüntülenme Markdown

Bir modülün diske gelişiyle silinişi arasında olan biten. Ne zaman yüklenir, ne zaman kurulur, etkinlik işareti nerede durur ve hangi metodunuz çağrılır.

Genel Bakış

Bir kurulum yordamı yoktur. Dizini yerine kopyalamak kurulumun kendisidir; modül bir sonraki istekte listelenir. Sonrasındaki her şey, ayrı ayrı başlatılan küçük adımlardan oluşur. Bu adımlardaki metotların neredeyse hepsi isteğe bağlıdır: platform sınıfınızda var mı diye bakar, yoksa devam eder.

İki kavram kolayca karışır. Mevcut, dizinin var olması demektir; modülün listelenmesi, kanca dosyasının çalıştırılması ve isteyen herkes tarafından kurulabilmesi için bu yeter. Etkin ise bir operatörün onu seçmiş olması demektir ve bu kararın nerede saklandığı tamamen tipe bağlıdır. Kapalı olmak, kodunuzun yüklenmesini hiçbir şekilde engellemez.

Yapı

Aşamalar

AşamaNeyle olurModülünüzde ne çalışırNasıl geri alınır
DiskteDizin kopyalanır, bir arşivden çıkarılır ya da panel tarafından indirilirHiçbir şey. Gelmiş olmak hiçbir kodu çalıştırmazDizini silmek
Kancalar kaydedilirHer istekte, her modül dizini için, hiçbir duruma bakılmadanhooks.php, baştan sonaYalnız gövdeyi kendiniz kapılayarak ya da dosyayı kaldırarak
YüklenirBir yer kayıt defterinden bu modülü ya da tipinin tamamını isterHiçbir şey. config.php ve dil dosyası statik bir önbelleğe okunurGeri alınacak bir şey yok; önbellek bir istek boyu yaşar
KurulurFabrikadan bir nesne istenirTaban yapıcı, sonra yazdıysanız sizinkiGeri alınacak bir şey yok; nesne bir istek boyu önbelleklenir
BağlanırÇağıran taraf bir hizmet, sipariş ya da ürün bağlarVarsa sizin set_service geçersiz kılmanızAynı nesneye başka bir şey bağlamak
EtkinleşirOperatör açar ya da bir içe aktarıma hemen etkinleştirmesi söyleniractivate(), ardından enable(); ikisi de isteğe bağlı, ikisi de reddedebilirKapatmak
KapanırOperatör kapatırdeactivate(), ardından disable(); ikisi de isteğe bağlıYeniden açmak; bu da etkinleştirme metotlarını tekrar koşturur
SilinirModül listesindeki silme eylemiuninstall(), hiçbir dosyaya dokunulmadan önceDosyaları geri koymak. Sildiğiniz veriyi hiçbir şey geri getirmez

Referans

Etkinlik İşareti Nerede Durur

Dört tip kendi yapılandırma dosyasında bir status anahtarı tutar. Kalanı başka bir yerde seçilir ve ikisinde böyle bir işaret hiç yoktur.

TipKarar nerede saklanırKim yazar
AddonsModülün kendi config.php dosyasındaki statusEklenti listesindeki anahtar, taban durum metodu üzerinden
ProductModülün kendi config.php dosyasındaki statusO modül grubunun ayar ekranı; her modülün dosyasını tek geçişte yazar
FraudModülün kendi config.php dosyasındaki statusDolandırıcılık ayarları ekranı
SocialAuthModülün kendi config.php dosyasındaki statusSosyal giriş ayarları
Mail, SMS, IP, CurrencyPlatformun modül yapılandırmasındaki tek bir modül adıO grubun ayar ekranı. Aynı anda yalnız biri etkin olabilir
PaymentPlatformun modül yapılandırmasındaki modül adı listesiÖdeme ayarları ekranı; ayrıca kart saklama geçidini adlandıran ayrı bir girdi
AuthenticationPlatformun modül yapılandırmasındaki modül adı listesiGüvenlik ayarları
CaptchaSeçenek yapılandırmasındaki seçili tip, yanında bir açık kapalı işaretiyleGüvenlik ayarları
ServersHiçbir yerde işaret yokModülü adlandıran bir sunucu kaydı onu kullanıma sokar
RegistrarsHiçbir yerde işaret yokModülü gösteren bir üst düzey alan adı uzantısı onu kullanıma sokar
Storage, Pipe, ImportsKullanıldığı noktada seçilirSırasıyla yedek hedefi, destek posta kutusu ve içe aktarma koşusu

Bildirebileceğiniz Yaşam Döngüsü Metotları

İlk beşi salt konvansiyondur. Hiçbir taban sınıf onları bildirmez; her biri method_exists ile aranır ve yoksa atlanır. Yanlış bir dönüş, ait olduğu adımı durdurur. Son ikisi farklıdır ve bu fark önemlidir. change_addon_status eklenti tabanında zaten uygulanmıştır. testConnection ise sosyal giriş tabanında abstract bildirilmiştir; yani orada isteğe bağlı değil zorunludur.

isteğe bağlı sözleşme
// Etkinleştirme yolu, bu sırayla. Önce activate(), sonra enable() koşar.
// false döndürmek modülü kapalı bırakır ve hiçbir şey yazılmaz.
public function activate(): bool;
public function enable(): bool;

// Kapatma yolu, bu sırayla.
public function deactivate(): bool;
public function disable(): bool;

// Modül dizini kaldırılmadan önce koşar. false döndürmek silmeyi iptal eder.
// Bu argümansız boolean biçim EKLENTİ biçimidir.
public function uninstall(): bool;

// Authentication tipi aynı adı farklı bir biçimle yeniden kullanır: saklanan
// kayıt verisi geçilir ve bir dizi döndürülür. Yalnız 'error' iptal eder.
public function uninstall(array $data = []): array;   // ['status' => 'successful']

// Test butonu. İki biçim var ve hangisini alacağınıza çağıran taraf karar verir:
// sosyal giriş sağlayıcıları ARGÜMANSIZ çağrılır (tabanda abstract),
// servis sağlayıcılar ise tek argüman olarak birleştirilmiş yapılandırmayla çağrılır.
public function testConnection(): bool;                  // SocialAuth
public function testConnection($config = []): bool;      // Registrars

// Eklenti tabanında zaten uygulanmıştır: yukarıdaki dört metodu koşturur, sonra
// yeni durumu config.php'ye yazar. Yalnız o davranışı değiştirmek için geçersiz kılın.
public function change_addon_status($arg = '');
Bu adlardan ikisi farklı imzayla yeniden kullanılır

Bir servis sağlayıcıda argümansız testConnection() bildirmek tuzağın ta kendisidir. Taban, birleştirilmiş yapılandırma dizisini ilk argüman olarak geçer. Operatörün girdiği kimlik bilgilerini sınamak için gereken değeri atmış olursunuz. Kurulu her servis sağlayıcı $config = [] bildirir. Aynısı uninstall için de geçerlidir: eklenti biçimi argüman almaz, kimlik doğrulama biçimi saklanan kayıt dizisini alır.

Eklenti Durum Zinciri

Sıranın tamamı budur. Düşen bir enable() çağrısının modülü neden olduğu gibi bıraktığını görmenin en hızlı yolu bunu okumaktır.

taban durum metodu ne yapar
public function change_addon_status($arg = '')
{
    $status = $arg == "enable";
    $apply  = true;

    if ($status && method_exists($this, 'activate'))    $apply = $this->activate();
    if ($status && method_exists($this, 'enable'))      $apply = $this->enable();
    if (!$status && method_exists($this, 'deactivate')) $apply = $this->deactivate();
    if (!$status && method_exists($this, 'disable'))    $apply = $this->disable();

    // Bayrak EN SON yazılır, hem de yalnız modül onay verdiyse.
    if ($apply) {
        $config           = $this->config;
        $config["status"] = $status;
        $this->save_config($config);
    }

    return $apply;
}

Onu çağıran operation; bir reddin operatöre nasıl ulaştığını ve iki argümanın nereden geldiğini görün:

çağıran operation, kısaltılmış
$key    = (string) Filter::init("POST/module", "route");
$status = (int) Filter::init("POST/status", "rnumbers");

$instance = Modules::getInstance("Addons", $key);

if (!method_exists($instance, "change_addon_status"))
    throw new Exception("Module class does not have a method named change_addon_status.");

$status = $status ? "enable" : "disable";

// Fırlatılan bir istisna doğrudan hata mesajı olarak dışarı çıkar. false dönüşü ise
// eski yoldur: çağıran taraf o zaman gerekçe için eski hata özelliğini okur.
$result = $instance->change_addon_status($status);
if (!$result) throw new Exception($instance->error ?: "Unknown error");

User::addAction($adata["id"], "alteration", "change-addon-status-" . $status, ['module' => $key]);

Hook::run('action:addon.status_changed', $key, $status);

Yaşam Döngüsü Kancaları

Kapılar veto edebilir, eylemler yalnız izler. Boş olmayan bir dize döndüren kapı, o dizeyi operatörün gördüğü hataya çevirir.

gate:module.activate Herhangi bir modül grubu etkinleştirmesi yazılmadan önce, grup ve yeni etkinleşen adlar listesiyle çalışır. Reddetmek için boş olmayan bir dize ya da mesaj taşıyan bir dizi döndürün.
action:module.activated Grup ayarları kaydedildikten sonra, kapalıdan açığa geçen adlarla. Yalnız fark bildirilir, seçimin tamamı değil. Dönüş yoksayılır.
action:module.deactivated Bir öncekinin aynası; açıktan kapalıya geçen adlarla. Dönüş yoksayılır.
action:addon.status_changed Bir eklenti açıldıktan ya da kapandıktan sonra, modül anahtarı ve uygulanan kelimenin kendisiyle. Dönüş yoksayılır.
gate:module.addon_install Yüklenen arşiv açılmadan önce, yükleme kaydı ve hemen etkinleştirilip etkinleştirilmeyeceği bilgisiyle.
action:addon.installed Çıkarma sonrası, modül anahtarı ve etkinleştirme işaretiyle. Mağaza kaydı ya da güncelleme künyesi buraya yazılır.
gate:module.addon_delete Bir eklenti silinmeden önce, anahtarıyla. Modül hâlâ canlı veri taşırken reddetmek için kullanın.
action:addon.deleted Dizin gittikten sonra. Modülün kendi sınıfı bu noktada artık yoktur; dinleyiciyi başka bir yere koyun.
gate:module.delete Eklenti olmayan bir modül için aynı veto; tip ve anahtarla.
action:module.config_saved Bir modülün yapılandırma dosyası yeniden yazıldıktan sonra, tip ve anahtarla. Modülünüzün tuttuğu bir önbelleği temizlemek için işe yarar.

Örnek

Kendi şemasını kuran bir etkinleştirme, veriyi bilerek koruyan bir kapatma ve nihayet onu silen bir kaldırma. Üçünün de amacı şudur: yalnız sonuncusu yıkıcıdır.

modül tarafı
public function enable(): bool
{
    // Bilerek idempotent: enable() her yeniden etkinleştirmede ve güncellemeden sonra tekrar koşar.
    $this->check_database();

    return true;
}

/** Yalnız ekler. Asla düşürmez, var olan bir kolonu asla yeniden yazmaz. */
private function check_database(): void
{
    if (!\WDB::hasTable("Acme_events"))
        \WDB::exec('CREATE TABLE `Acme_events` ('
            . '`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,'
            . '`service_id` INT(11) UNSIGNED NOT NULL DEFAULT 0,'
            . '`payload` TEXT NULL DEFAULT NULL,'
            . 'PRIMARY KEY (`id`)'
            . ') ENGINE = InnoDB CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;');

    // Tablo ilk gönderildikten sonra eklenen kolonlar tek tek kontrol edilir.
    $col = \WDB::query("SHOW COLUMNS FROM `Acme_events` LIKE 'created_at'");
    if (!($col ? \WDB::getAssoc($col) : false))
        \WDB::exec("ALTER TABLE `Acme_events` ADD `created_at` INT(11) UNSIGNED NOT NULL DEFAULT '0'");
}

public function disable(): bool
{
    // Burada hiçbir şey düşürülmez. Kapatmak geri alınabilir bir işlemdir; modülü bir
    // öğleden sonra kapatan operatör bir yıllık satırı kaybetmemelidir.
    return true;
}

public function uninstall(): bool
{
    // Kaybedilecek bir şey varken sessizce yok etmek yerine reddedin.
    // select() OLUŞTURUCUYU döndürür, yani build() ve getAssoc() onun üzerinde çağrılır.
    // Oluşturucuyu WDB::getAssoc() metoduna argüman olarak vermek ölümcüldür: o
    // parametre bir PDOStatement bekler, onu da WDB::query() döndürür.
    $stmt = \WDB::select('COUNT(id) AS total')->from('Acme_events');
    $rows = $stmt->build() ? (int) (($stmt->getAssoc() ?: [])['total'] ?? 0) : 0;

    if ($rows > 0 && !(int) \Filter::init("POST/purge", "rnumbers"))
        throw new \Exception($this->lang['error-uninstall-has-data'] ?? 'The module still holds records.');

    \WDB::exec("DROP TABLE IF EXISTS `Acme_events`");

    return true;
}

Diğer taraf, modülde değil bir dinleyicide; çünkü silme dinleyicisinin tepki verdiği sınıftan daha uzun yaşaması gerekir:

hooks.php
// Modül etkin olsun olmasın, HER istekte koşar. Burayı yalnız kayıtlara ayırın.
Hook::add('gate:module.addon_delete', 1, function ($key) {
    if ($key !== 'Acme') return '';

    $stmt = WDB::select('COUNT(id) AS cnt')->from('Acme_events');
    $stmt->where('processed', '=', 0);

    $open = $stmt->build() ? (int) (($stmt->getAssoc() ?: [])['cnt'] ?? 0) : 0;

    // Boş olmayan dize reddin kendisidir ve operatörün okuduğu şey odur.
    return $open > 0 ? 'Acme still has ' . $open . ' unprocessed events.' : '';
});

Hook::add('action:addon.status_changed', 1, function ($key, $status) {
    if ($key !== 'Acme') return;

    Cache::getInstance()->clear(['acme']);
});

Tuzaklar

Etkinleştirme birden çok kez çalışır, yani idempotent olmalıdır

İlk etkinleştirmede ve her yeniden etkinleştirmede çalışır; bir güncellemenin şemayı yeniden kurduğu olağan yer de burasıdır. Oluşturmadan önce kontrol edin, kolonları tek tek ekleyin ve operatörün düzenlediği bir satırı asla ezmesine izin vermeyin. Tohumlama yalnız boş tabloya sayım kapısıyla yapıldığında güvenlidir.

Kapatmak kaldırmak değildir, silmek de değildir

Kapatma her tabloyu ve her satırı olduğu gibi bırakmalıdır; o bir anahtardır, temizlik değil. Silme yalnız dizini kaldırır, yani modülünüzün veritabanına yazdığı her şey hayatta kalır. Veri de gidecekse, dosyalar yok olmadan önce çalışan tek adım olan kaldırma metodunda düşürün.

Kapalı bir modül de kancalarını kaydeder

Kanca dosyaları modüller dizini gezilerek toplanır, hiçbir duruma bakılmadan. Gövde modülün kendi etkinlik işaretini önce kontrol etmiyorsa dinleyicileriniz modül kapalıyken de çalışır. Kanca dosyasını bir kayıt listesi olarak görün ve kararı her dinleyicinin içine koyun.

Reddi fırlatarak, kişinin yapabileceği bir cümleyle bildirin

Fırlatılan bir istisna ekrandaki mesaj olur. Mesajsız bir yanlış dönüş ise "Unknown error" kelimelerini üretir. Çağıran taraf bu durumda yeni kodun doldurmadığı eski hata özelliğine düşer ve operatör hiçbir şey öğrenmez.

Sınıfınıza önce yalnız eklenti silme eylemi sorar

Kaldırma metodunu eklenti silme eylemi çağırır; bir de kimlik doğrulama tipi, kayıt kaldırılırken çağırır. Başka herhangi bir tipteki modülü silmek, kapı kancası reddetme şansını kullandıktan sonra dosyaları kaldırır. Sınıfınızdaki hiçbir metot çağrılmaz. Kaldırma anında çalışması gereken ne varsa, kimsenin çağırmadığı bir metoda değil kapının arkasına koyun.

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.