Sunucu Modülü Yazma
Sunucu modülü, ödenmiş bir siparişi hosting panelinde ya da bulut sağlayıcısında gerçek bir hesaba dönüştürür. Çekirdek yaşam döngüsü metotlarınızı adıyla çağırır; döndürdüğünüz şey hizmetin üzerine yazılır.
Genel Bakış
Servers en büyük modül tipidir: 50 modül, üçü paylaşımlı hosting, özel makine ve sanallaştırma için kum havuzu örneği.
Sınıfınız ServerModule'ü genişletir, o da ModuleBaseTrait'i içeri alır. İkisi birlikte sunucuyu, hizmeti, ürünü, alıcıyı, çözümlenmiş limitleri, eklenti cevaplarını ve araç düzeneğini zaten tutar.
Çekirdek sınıfınızı hiç kurmaz: örneği fabrikadan çözer ve fiili adıyla çağırır. Yazmadığınız bir fiil atlanır; tipin isteğe-bağlılık mekanizması budur.
new asla kullanılmaz.
Ön Koşullar
- Önce Modül Anatomisi ve Modül Yapılandırması; bu makale yalnızca Servers tipini anlatır.
- API erişimi olan bir sağlayıcı hesabı ve bağlantı testini geçen bir sunucu kaydı.
- O sunucuya bağlı bir test ürünü; yoksa
create()okuyacak bir plan bulamaz. - Hata,
falsedöndürerek değil fırlatılarak bildirilir.
Yapı
Her modül için tek bir dizin, içindeki sınıfla birebir aynı adda.
coremio/modules/Servers/Acme/
├── Acme.php sınıf: extends ServerModule
├── ApiClient.php kendi HTTP sarmalayıcınız, düz new, WISECP modülü değil
├── config.php künye, sunucu form alanları, desteklenen kartlar ve araçlar
├── logo.png modül seçicide görünür
├── lang/en.php $this->lang, dil başına bir dosya
└── pages/ get_page() ile render edilen isteğe bağlı şablonlar
namespace WISECP\Modules\Servers;
use Exception;
use Language;
use ServerModule;
class Acme extends ServerModule
{
private ApiClient $api;
// set_server() tarafından çağrılır; onu hem yapıcı hem set_service() koşar,
// $this->server dolduktan ve sırları çözüldükten sonra.
protected function define_server_info(array $server = []): void
{
include_once __DIR__ . DS . 'ApiClient.php';
$this->api = new ApiClient($server);
}
}
| Ekran | Sınıfınızdaki giriş noktası | Nereden ulaşılır |
|---|---|---|
| Sunucu ayar formu | config.php alanları, ardından test_connect() | Admin, Sunucular, Sunucu Yönet |
| Ürün ayarları | product_configuration(), save_product_configuration() | Admin, ürün detayı, Modül sekmesi |
| Sağlama | create(), suspend(), unsuspend(), cancel() | Sipariş onayı, admin aksiyonları, kuyruk |
| Müşteri paneli panosu | dashboard_data() | Müşteri panelindeki hizmet detayı |
| Müşteri paneli araçları | tool_data(), tool_action() | Araç kenar çubuğu, araçlar makalesine bakın |
| Metrik faturalama | metrics_usage() ya da metrics_usage_bulk() | Kullanım toplama cron'u |
| İçe aktarma | list() | Admin, panelden hesap içe aktarma |
Adım Adım
Katmanları Bu Sırayla Yazın
Yaşam döngüsüyle başlamak boşa iştir. create(), alıcının seçimlerini hizmet seçeneklerinden okur ve o seçenekler ancak ürün formu tanımlıysa oluşur.
define_server_info(), ardındantest_connect(): sunucuyu ekleyin, kimlik bilgilerini kanıtlayın.product_configuration()vesave_product_configuration(), artı plan listesini dolduran çağrılabilir metot.create(), sonrasuspend(),unsuspend(),cancel().change_password()veupgrade().list(),dashboard_data(), tek oturum açma metotları, metrikler.configure_features()ve araçlar.
Her katmanı bir sonrakine geçmeden canlı sağlayıcıya karşı doğrulayın: sonda çıkan ilk hatanın altı olası nedeni olur.
Sağlayıcıya Bağlanma
Kimlik bilgileri sunucu kaydından gelir. Hangi alanların görüneceğine config.php karar verir; taban sınıf onları çözülmüş teslim eder.
return [
'name' => 'Acme Cloud',
// 'hosting' = alan adı tabanlı hesaplar, 'server' = VPS ya da özel makineler.
'type' => 'hosting',
// Uzun bir API jetonu parola alanına değil erişim anahtarı alanına yazılır:
// servers.password bir varchar'dır ve şifrelenmiş uzun jeton oraya sığmaz.
'use-access-hash' => true,
'require-access-hash' => true,
'use-test-connection' => true,
'use-port' => true,
'not-secure-port' => 2082,
'secure-port' => 2083,
// Ek alanlar $server['fields'] içine düşer. 'crypt' değeri şifreli saklar.
'fields' => [
'region' => ['type' => 'text', 'name' => '{lang.region}', 'col_class' => 'col-md-6'],
'sub_token' => ['type' => 'password', 'name' => '{lang.sub_token}', 'crypt' => true],
],
// Hesabı sağlayıcı tarafında hangi hizmet seçeneğinin tanımladığı.
'service-relationship' => 'domain',
];
Taban sınıf $server['password'] değerini tek bir noktada çözer; define_server_info() metoduna her zaman düz metin ulaşır. Yeniden çözerseniz API bozulmuş bir sır alır.
Ürün Alanları ve Plan Açılır Listesi
product_configuration() bir alan tanım haritası döndürür. Yöneticinin seçtiği şey ürünün modül verisi olarak kaydedilir ve sağlama kodunuza $this->options['creation_info'] içinde ulaşır.
Seçenek değeri olarak planın adını yazın, sağlayıcının sayısal kimliğini değil; adı kimliğe create() içinde çevirin.
Yaşam Döngüsü Gövdesi
Her sağlama fiili aynı üç vuruşu izler: alıcının seçtiğini oku, sağlayıcıyı çağır, saklanacağı döndür. Kalıcılaştırmanın yolu dizi döndürmektir.
- Planı
creation_infoiçinden, limitleriget_limit()ile, eklenti cevaplarınıaddon_params, sipariş formu cevaplarınırequirement_paramsiçinden okuyun. - Sağlayıcıyı çağırın. API hatasında istemcinin fırlatmasına izin verin ya da çevrilmiş bir mesajla kendiniz fırlatın.
['config' => [...]]döndürün; çekirdek bunu hizmet seçeneklerine birleştirir ve sonraki her fiil hesabı oradan okur.
Referans
Yaşam Döngüsü İmzaları
Hiçbiri taban sınıfta soyut bildirilmez. Çekirdek her birini method_exists() ile yoklar ve olmayanı atlar; sözleşme imzanın kendisidir.
// Hazırlık. define_server_info, diğer her fiilden önce set_server() içinden koşar.
protected function define_server_info(array $server = []): void;
public function test_connect(): array|bool;
public function configure_features(): void;
// Ürün ve hizmet formları. İki save metodu da değerleri REFERANSLA alır.
public function product_configuration(array $data = []): array;
public function save_product_configuration(array &$values): void;
public function service_configuration(): array;
public function save_service_configuration(array &$values): void;
// Sağlama.
public function create(): array|bool;
public function suspend(): bool;
public function unsuspend(): bool;
public function cancel(): bool;
public function renew(): bool;
public function upgrade(array $new_product = []): bool;
public function change_password(string $password): bool;
public function change_limits(array $limits): bool;
public function reset_limits(): array|bool;
// Eklentiler. $addon, hizmetin eklenti tablosundaki bir satırdır.
public function addon_create(array $addon = []): array|bool;
public function addon_suspend(array $addon = []): array|bool;
public function addon_unsuspend(array $addon = []): array|bool;
public function addon_cancel(array $addon = []): array|bool;
public function addon_upgrade(array $addon, array $new_addon): array|bool;
// Müşteri paneli.
public function dashboard_data(): array;
public function tool_data(string $tool, string $action = 'index', array $params = []): array;
public function tool_action(string $tool, string $action, array $data = []): array;
public function sso_panel_login(): string;
public function sso_root_panel_login(): string;
// Metrik faturalama ve içe aktarma.
public function metrics_usage(): array;
public function metrics_usage_bulk(array $services = []): array;
public function metric_enable(array $metric): void;
public function metric_disable(array $metric): void;
public function list(bool $rCount = false, array $filters = [], array $orders = [], int $start = 0, int $end = -1): array|int;
| Metot | Zorunlu | Ne zaman çağrılır | Sonraki durum |
|---|---|---|---|
| define_server_info | evet | Her örnekleme | yok |
| test_connect | evet | Sunucu formunda Bağlantıyı Test Et | yok |
| product_configuration | evet | Ürün detayı, Modül sekmesi | yok |
| create | evet | Sipariş onaylandığında ya da adminde Yeniden Oluştur | active |
| suspend | evet | Vadesi geçen fatura ya da elle askıya alma | suspended |
| unsuspend | evet | Ödeme alındığında ya da elle geri açma | active |
| cancel | evet | İptal işlendiğinde | cancelled |
| change_password | evet | Müşteri ya da yönetici hesap parolasını değiştirdiğinde | değişmez |
| upgrade | evet | Aynı modülde kalan plan değişikliğinde | değişmez |
| renew | isteğe bağlı | Yenileme faturası ödendiğinde, vade zaten ilerlemişken | değişmez |
| change_limits, reset_limits | isteğe bağlı | Yalnız sağlayıcı hesap başına limit ezmeye izin veriyorsa | değişmez |
| addon_* | isteğe bağlı | Hizmetteki bir eklenti durum değiştirdiğinde | yalnız eklenti |
| list | isteğe bağlı | Yönetici Hesapları İçe Aktar ekranını açar; o ekranı görünür kılan şey bu metottur | yok |
| metric_enable, metric_disable | isteğe bağlı | Yalnız metrik faturalama artı sağlayıcı tarafı limit ezme varsa | yok |
create() Ne Döndürebilir
Sade bir başarı için true, aksi halde bir dizi döndürün. Dizinin her anahtarı bir talimat sayılır; ayrılmış olanlar, üzerine yazmak yerine hizmet seçeneklerine özyinelemeli olarak birleştirilir.
config altına birleştirilir. Hesap kimliği burada yaşar: user, şifrelenmiş password, home_dir, sağlayıcı tarafındaki kimlik.
login altına birleştirilir. Müşteri panelinin gösterdiği ya da tek oturum açmanın kullandığı kimlik bilgileri.
creation_info altına, yani ürün formunun yazdığı torbaya birleştirilir. Gerçekten neyin sağlandığını kaydetmek için kullanın.
'inprocess' ya da 'waiting', eşzamansız sağlama biterken hizmeti aktif durumdan uzak tutar.
hostname, ip ya da ftp_info iç içe koyulmadan böyle saklanır.
Elinizde Hazır Duran Durum
Hepsi metodunuz koşmadan önce, yapıcı ve set_service() tarafından doldurulur. Hiçbiri için sorgu atmayın.
fields.
$this->save_options() çağırın; sonda dizi döndürmek de aynı işi yapar.
disk_limit, bandwidth_limit, email_limit, database_limit, addons_limit, subdomain_limit, ftp_limit, park_limit, max_email_per_hour.
addon-params altında bildirilenlerdir.
requirement-params altında bildirilen adlarla anahtarlanır.
Çağrının Çekirdek Tarafı
public static function run_module(int|array $service, string $action, array $params = []): mixed;
public static function instance_module(int|array $service): ?object;
Services::run_module($id, 'create'); // argüman yok
Services::run_module($id, 'change_password', [$password]); // tek konumsal dize
Services::run_module($service, 'upgrade', [$newProduct]); // yeni ürün dizisi
Services::run_module($service, 'addon_create', [$addon]); // tek eklenti satırı
terminate() yoksa çekirdek önce cancel(), sonra cancelled() dener. cancel() yazarsanız iki yol da çalışır.
Örnek
Eksiksiz bir sağlama metodu ve hemen ardından dönüş değerini geri okuyan çekirdek kodu. İki yarı birbirine aittir: döndürdüğünüz anahtarlar çekirdeğin birleştirdiği anahtarlardır ve sonraki fiillerinizin okuduğu her şey aynı torbadan çıkar.
public function create(): array|bool
{
$domain = $this->options['domain'] ?? '';
if (!$domain) throw new Exception($this->lang['error-domain-required']);
// Yeniden sağlama: yeni bir kimlik üretmek yerine önceki koşudakini yeniden kullanın.
$username = $this->options['config']['user'] ?? '';
if (!$username) $username = self::username_generator($domain);
$password = ($this->options['config']['password'] ?? '') !== ''
? $this->decode_str($this->options['config']['password'])
: Utility::generate_hash(12);
$creation = $this->options['creation_info'] ?? [];
$parameters = [
'username' => $username,
'password' => $password,
'domain' => $domain,
// Seçenek değerleri plan ADINI taşır, bu yüzden onu bu sunucuda çözün.
'plan_id' => $this->resolve_plan_id($creation['plan'] ?? ''),
'disk' => $this->get_limit('disk_limit'),
'bandwidth' => $this->get_limit('bandwidth_limit'),
// Anahtar alanı "0" ya da "1" dizesi gönderir; empty("0") true'dur, onun yerine cast edin.
'shell' => (int) ($creation['shell_access'] ?? 0) === 1,
];
// Sipariş formu cevapları ve eklenti toplamları; config.php'de bildirilen adlarla anahtarlanır.
foreach ($this->requirement_params as $key => $value) $parameters[$key] = $value;
foreach ($this->addon_params as $key => $value) $parameters[$key] = $value;
// Yeniden deneme güvenliği: kuyruk bu metodu bir hatadan sonra tekrar koşar.
if (!$this->api->account_exists($username))
$this->api->call('accounts', $parameters, 'POST');
return [
'config' => [
'user' => $username,
'password' => $this->encode_str($password),
'home_dir' => '/home/' . $username,
],
// Ayrılmış bir anahtar değil, bu yüzden seçenek köküne yazılır.
'ip' => $this->server['ip'] ?? '',
];
}
// Services::apply_module_result, sizi ilgilendiren kısma indirgenmiş hali.
if (is_array($result)) {
if (isset($result['config']) && is_array($result['config']))
$options['config'] = array_replace_recursive($options['config'] ?? [], $result['config']);
// Ayrılmış kümenin dışında kalan her şey seçenek köküne düşer.
$reserved = ['config', 'login', 'creation_info', 'options', 'status'];
foreach ($result as $rKey => $rVal)
if (!in_array($rKey, $reserved, true)) $options[$rKey] = $rVal;
}
// Sonra ModuleQueue yeni hizmet durumuna karar verir.
$moduleStatus = is_array($result) ? ($result['status'] ?? null) : null;
$targetStatus = $moduleStatus ?: match ($action) {
'create', 'unsuspend', 'register' => 'active',
'suspend' => 'suspended',
'cancel' => 'cancelled',
default => null,
};
// Ve sonraki her fiilin okuduğu şey budur:
$username = $this->options['config']['user'] ?? '';
$password = $this->decode_str($this->options['config']['password'] ?? '');
Tuzaklar
Hata özelliğine atama yapıp false döndürmek önceki neslin kalıntısıdır. cPanel 41 yerde fırlatıyor, hiçbir yerde atamıyor. Çevrilmiş bir exception fırlatın; çağıran taraf mesajı gösterir.
Hesap oluştuktan sonraki bir hata baştan yeniden denenir ve kopyayı reddeden bir sağlayıcı bundan sonra hep başarısız olur. Önce hesabı denetleyin ve kimlik bilgilerini elinize geçer geçmez save_options() ile yazın ki ikinci koşu onları bulabilsin.
Yük dengeli bir sunucu grubunda sipariş yeri olan makineye düşer ve aynı plan orada farklı bir sayısal kimlik taşır. Sabit olan addır: adı kaydedin, kimliğe çağrı anında çevirin.
Onay ve anahtar alanları "0" ve "1" dizelerini gönderir; empty("0") ise true'dur. (int) ($data['key'] ?? 0) === 1 yazın; bu, o biçimlerin hepsinde aynı davranır.
Taban sınıf bunu tek bir yerde zaten yaptı; böylece bağlantı testi akışı ile sağlama akışı aynı davranır. İkinci bir çözme düz metin yolunu bozar ve hata ancak gerçek bir sağlayıcıya karşı ortaya çıkar.
İ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.