# Modül Sistemi

https://dev.wisecp.com/tr/modul-sistemi

Her entegrasyon bir modüldür: bir tip klasörü altında duran, platformun adıyla keşfettiği, gerektiğinde yüklediği ve tek fabrika üzerinden verdiği bir dizin.

## Genel Bakış

Bir modül hiçbir yere kaydedilmez. Düzenlenecek bir manifest, eklenecek bir konteyner girdisi ya da koddan çağrılacak bir kurulum yordamı yoktur. Kayıt defteri dosya sistemini okur: adı içindeki sınıf dosyasıyla eşleşen bir dizin, diskte var olduğu anda modüldür ve panel onu bir sonraki istekte listeler.

Bir üst dizin modülün **tipidir** ve sözleşmenin tamamı tiptir. Çekirdeğin hangi metotları çağıracağına, modülü hangi panel ekranının listeleyeceğine ve genişletilecek bir taban sınıf olup olmadığına tip karar verir. On altı tipin sekizinde taban sınıf vardır; kalan sekizi düz sınıflardır ve sözleşmeleri, çekirdeğin `method_exists` ile aradığı metot kümesinden ibarettir.

- **coremio/modules**: Genişletme alanının tamamı: tip başına bir alt dizin, içinde modül başına bir alt dizin. 16 tip, şablon olarak gelen `Sample` kum havuzu modülleri dahil 300 modül.
- **Modules**: Kayıt defteri ve fabrika, tamamı statik. Dizinleri tarar, sınıf dosyalarını dahil eder, yapılandırmayı ve dil paketini önbelleğe alır, örnekleri kurar.
- **Modül tipi**: Üst dizinin adı, büyük küçük harfiyle birlikte (`Servers`, `Payment`, `Registrars`). Her kayıt defteri çağrısına dize olarak geçer; bu yüzden bir yazım hatası hata değil, sessiz bir boş sonuç üretir.
- **Modül adı**: Modül dizininin adı. Sınıf dosyası ve sınıfın kendisi aynı adı harfi harfine taşır, büyük küçük harf dahil.

## Yapı

### On Altı Tip

Sayılar, adı `Sample` ile başlayan kum havuzu modüllerini içerir. Onlar okunmak ve kopyalanmak için vardır, üretimde etkinleştirilmek için değil.

| Tip | Taban sınıf | Çekirdeğin ne için çağırdığı | Mevcut |
| --- | --- | --- | --- |
| Servers | `ServerModule` | Bir kontrol panelinde barındırma hesabı, sanal makine ya da oyun sunucusu sağlama ve yönetme | 50 |
| Payment | `PaymentGatewayModule` | Tahsilat: ödeme ekranı, çekim, geri bildirim ve mutabakat | 164 |
| Registrars | `RegistrarModule` | Bir servis sağlayıcıda alan adı kaydı, yenilemesi ve transferi | 21 |
| Product | `ProductModule`, `SslProductModule` | Sunucusuz sağlanan ürün. Buradaki dört örnek dışı modülün hepsi SSL sertifikası ürünüdür | 7 |
| Addons | `AddonModule` | Panelin kendisine eklenen özellik: kendi ayar sayfası, yetkileri ve kancaları | 9 |
| SMS | yok, düz sınıf | Bir sağlayıcı üzerinden kısa mesaj gönderme | 11 |
| Mail | yok, düz sınıf | Giden postayı SMTP ile ya da sağlayıcı API'siyle teslim etme | 4 |
| Authentication | yok, düz sınıf | Girişte ikinci faktör: posta kodu, SMS kodu ya da doğrulayıcı uygulama | 3 |
| Pipe | yok, düz sınıf | Bir posta kutusundan postaları çekip destek talebine dönüştürme | 3 |
| Imports | yok, düz sınıf | Başka bir platformdan müşteri, hizmet ve fatura taşıma | 3 |
| Fraud | `FraudModule` | Bir siparişi ya da kaydı puanlama ve bulunanı kaydetme | 2 |
| SocialAuth | `SocialAuthProvider` | Sosyal giriş: yetkilendirme yönlendirmesi, kod takası ve kimlik jetonu doğrulaması | 3 |
| Captcha | yok, düz sınıf | Açık bir formu kabul edilmeden önce sınama | 4 |
| Currency | yok, düz sınıf | Tanımlı para birimleri için kur çekme | 7 |
| IP | yok, düz sınıf | Bir adresi ülkeye ve ağa çözme; yerelleştirme ve risk için kullanılır | 3 |
| Storage | `StorageModule`, `CloudStorageModule` | Yedeklerin yüklendiği ve geri alındığı uzak hedef | 6 |

### Keşif ve Sınıf Çözümlemesi

Bir modülü yüklemek iki ayrı iştir. Yapılandırma ve dil paketi, statik bir önbelleğe yapılan düz `include` çağrılarıdır; sınıf dosyası ise ancak gerçekten bir örnek istendiğinde dahil edilir. Yükleyicinin üçüncü argümanının denetlediği şey bu ayrımdır.

```bash
coremio/modules/{Type}/{Name}/
├── {Name}.php      # yalnız bir örnek kurulurken dahil edilir (nominc = false)
├── config.php      # dizi döndürür, Modules::$modules[Type][Name]['config'] altında önbelleklenir
└── lang/
    ├── en.php      # yedek, etkin dil dosyası eksikken her zaman denenir
    └── {lang}.php  # ['lang'] altında önbelleklenir
```

Kurma zamanı geldiğinde şu sırayla üç sınıf adı denenir ve var olan ilki kazanır. Global ad alanında yazılmış bir modülün de tip ad alanında yazılmış bir modülün de çalışmasının sebebi budur.

```php
$classList = [
    $name . "_Module",                                       // eski sonek biçimi
    $name,                                                   // global ad alanı
    "WISECP\\Modules\\" . ucfirst($type) . "\\" . $name,      // yeni modüllerin kullandığı biçim
];
```

## Referans

### Kayıt Defteri

Tamamı statiktir ve tamamı tek bir sınıfta durur. İmzalar birebir şunlardır.

```php
// Kurar. Aday adların hiçbirinde sınıf yoksa null döner.
public static function getInstance(string $type, string $name, array $params = []): ?object;

// Yapılandırmayı ve dili statik önbelleğe yükler. Yüklenen kayıtları döndürür,
// adı verilen modül dizini yoksa false döner.
public static function Load($type = '', $name = '', $nominc = false, $status = '');
public static function add($file, $type, $nominc = false, $status = '');

// Önbellekten geri okur. Config() yükleme YAPMAZ; Lang() yapar.
public static function Config($type, $module);
public static function Lang($type, $module, $lang = '');
public static function getName(string $type, string $module): string;
public static function getModules($type = '', $name = '');

// Panelin bir modül için render ettiği yüzeyler.
public static function getPage(string $type, string $name, string $page, array $data = []): string;
public static function getController($type = '', $name = '', $cname = '');
public static function view($file, $variables = []): string;
public static function logo(string $name = '', $type = 'Servers'): string;

// Ayar alanı render'ı; config.php'de alan tanımlayan her tipin ortak kullandığı.
public static function fields_output($data = [], $input_name = ''): string;
public static function fields_output_wBuilder(AdminFormBuilder $form, $data = [], $input_name = ''): void;

// Panelin müşterinin işlem geçmişi altında gösterdiği modül işlem günlüğü.
public static function save_log($type = '', $module = '', $action = '', $request = '', $response = '', $processed = '');
```

### Sonucu Değiştiren Argümanlar

- **$name = 'All'**: Tek bir modül yerine tip dizininin tamamını tara. Boş dize değil, birebir `'All'` dizesi: `Mail` ve `SMS` için boş ad bambaşka bir anlama gelir (Tuzaklar bölümüne bakın).
- **$nominc = true**: Dahil etme yok. Yapılandırma ve dil okunur, sınıf dosyasına dokunulmaz. Ucuz çağrı budur ve her listeleme ekranının kullandığı şey budur.
- **$status**: Varsayılan boş dize olarak bırakılırsa hiçbir şeyi süzmez. Dize olmayan bir değer, pratikte `true`, geçilirse yalnız `config['status']` değeri buna eşit olan modüller yüklenir. Panelin yalnız etkin eklentileri listelemesi böyle olur.
- **$params**: Konumsal yapıcı argümanları. Fabrika yapıcıyı yansıma ile okur ve listeyi zorunlu parametre sayısına kadar `null` ile doldurur; böylece zorunlu argümanı olan bir modül hiçbir şey geçmeseniz de kurulur.

### Dönüş Biçimleri

- **getInstance()**: Nesne ya da `null`. Tip, ad ve serileştirilmiş parametrelere göre önbelleklenir; bir istekteki iki çağrı size aynı nesneyi verir.
- **Load()**: Adla: `['config' => [], 'lang' => [], 'config_file' => '']`, dizin yoksa `false`. `'All'` ile: ad başına aynı kaydı taşıyan, görünen ada göre sıralı bir harita.
- **Config()**: Önbellekteki yapılandırma dizisi ya da henüz kimse yüklemediyse `null`. Dosyayı kendisi hiçbir zaman okumaz.
- **Lang()**: Dil dizisi ya da `[]`. `Config()` ile aynı değil: dosyayı kendisi okur ve sırasıyla etkin arayüz diline, sistemin varsayılanına, sonra İngilizceye düşer.
- **getName()**: Görünen etiket: önce `lang['name']`, sonra `config['name']`, sonra dizin adı. Yüklemeyi kendisi yapar, yani soğuk çağrılabilir.

## Örnek

Günlük kullanımın iki yarısı: bir modülü kurup çağırmak ve hiçbir şey kurmadan bir tipin tamamını listelemek.

```php
$module = Modules::getInstance("Registrars", "ExampleRegistrarModule");
if (!$module) throw new Exception(Language::gc("modules/error-not-found"));

// Sınıfa değil sözleşmeye bakın: modül düz PHP'dir ve bir metottan eski olabilir.
if (!method_exists($module, "create")) throw new Exception(Language::gc("modules/error-unsupported"));

// Bağlam argüman olarak geçilmez, setter'larla bağlanır.
$module->set_service($serviceId);

$result = $module->create();
```

```php
// nominc = true: config.php ve lang/ okunur, hiçbir sınıf dosyası dahil edilmez.
$installed = Modules::Load("Currency", "All", true) ?: [];

$rows = [];
foreach ($installed as $name => $record) {
    $rows[$name] = [
        'label'  => Modules::getName("Currency", $name),
        'active' => (bool) ($record['config']['status'] ?? false),
        'logo'   => Modules::logo($name, "Currency"),
    ];
}

// Tek çağrıda yalnız etkin modüller: dördüncü argüman config['status'] ile karşılaştırılır.
$enabled = Modules::Load("Addons", "All", true, true) ?: [];
```

Aynı verinin okuma tarafı: istediğiniz modülü zaten biliyorsanız ve yalnız ayarları gerekiyorsa.

```php
// Config() yalnız önbelleği okur, bu yüzden önce yükleme gelmelidir.
Modules::Load("Servers", "cPanel", true);

$config = Modules::Config("Servers", "cPanel") ?: [];
$fields = $config['fields'] ?? [];

// Aynı kayıt, tek çağrı: yalnız yükleme sonucu gerekiyorsa.
$fields = Modules::Load("Servers", "cPanel", true)["config"]["fields"] ?? [];
```

## Tuzaklar

> **Bir modülü asla new ile kurmayın**
> 
> Sınıf dosyasını dahil eden, yapılandırma ve dil özelliklerini dolduran, eksik yapıcı argümanlarını tamamlayan ve sonucu önbellekleyen şey fabrikadır. Sınıfı doğrudan kurmak bunların hepsini atlar. Modül boş bir yapılandırmayla çalışır; bu da tıpkı hiçbir şey döndürmeyen bir sağlayıcı gibi görünür.

> **Yüklemeden yapılandırma okumak boş dizi değil null döndürür**
> 
> Okuma yalnız önbellektendir. Soğuk çağrıldığında `null` döner ve bu değer kodunuza hata olarak değil, eksik bir ayar olarak akar. Ya önce yükleyin ya da yapılandırmayı yükleme sonucundan alın.

> **Mail ve SMS için boş ad, etkin modül demektir**
> 
> Yalnız bu iki tipte adsız bir yükleme, tanımlı etkin sürücüyü çözer ve sadece onu yükler. Diğer bütün tipler dizinin tamamını okur. Tam listeyi istiyorsanız her zaman açıkça `'All'` geçin.

> **Örnek istek boyunca paylaşılır**
> 
> Fabrika tip, ad ve parametrelere göre önbelleklediği için çok hizmetli bir döngü size hep aynı nesneyi verir. Önceki turun ona bağladığı ne varsa hâlâ oradadır. Yeni nesne varsaymak yerine her turun başında bağlamı setter'larla açıkça bağlayın.

> **Pano widget'ları bir modül tipi değildir**
> 
> Panel widget'ları, modüller klasörüne dizin bırakılarak değil, pano controller'ındaki yetki kontrollerinden üretilir ve kancalarla genişletilir. Karşısına yazılacak bir widget modül tipi yoktur.

## İlgili Makaleler

- [Modül Anatomisi](https://dev.wisecp.com/tr/modul-anatomisi)
- [İlk Modülünüz](https://dev.wisecp.com/tr/ilk-modulunuz)
- [Modül Yaşam Döngüsü](https://dev.wisecp.com/tr/modul-yasam-dongusu)
- [Modül Yapılandırması](https://dev.wisecp.com/tr/modul-yapilandirmasi)
- [Modülden Kanca Kaydetme](https://dev.wisecp.com/tr/modulden-kanca-kaydetme)
- [Bootstrap ve Otomatik Yükleme](https://dev.wisecp.com/tr/bootstrap-ve-otomatik-yukleme)
