Captcha Modülü Yazma
Captcha modülü tek bir sorunun iki yarısını sağlar: açık bir formda görünen işaretleme ve gönderimde çalışan doğrulama.
Genel Bakış
Dört modül gelir. DefaultCaptcha kod görselini sunucuda üretir ve yazılan cevabı oturumla karşılaştırır. Turnstile, hCaptcha ve reCaptcha üçüncü parti bir bileşen gömüp jetonunu doğrular. Taban sınıf yoktur.
Aynı anda tek sağlayıcı etkindir; adı options/captcha/type içinde yazar. Hangi formların korunduğuna paylaşılan yardımcı karar verir. Siz soruyu sağlarsınız, gösterim zamanını yardımcı seçer.
Hiç görünmeyen bir bileşen çoğu zaman modül sorunu değil yapılandırma sorunudur.
Ön Koşullar
coremio/modules/Captcha/altına yazma erişimi.- Site anahtarı ve gizli anahtarı olan bir sağlayıcı hesabı.
- Test edeceğiniz bir form.
- Tema Formlarını Güvenli Hale Getirme: captcha oradaki beş katmandan biridir.
Yapı
Üç dosya, ad alanı yok, view yok. Ayar ekranı modülünüzün döndürdüğü alan tanımından üretilir.
coremio/modules/Captcha/Acme/
├── Acme.php Acme sınıfı, ad alanı yok
├── config.php kaydedilmiş anahtarları döndürür, örn. site-key ve secret-key
└── lang/
├── en.php hata metinleri ve işaretlemenizin bastığı buton etiketleri
└── tr.php
Kendi görselini üreten sağlayıcı /captcha.jpg adresindeki paylaşılan ucu kullanır; o uç etkin sağlayıcıda generateDisplay() çağırır.
Adım Adım
Modül Sınıfını Kurma
coremio/modules/Captcha/Acme/Acme.phpdosyasını dizin adını taşıyan bir sınıfla ve ad alanı olmadan oluşturun.- Yapıcıda config ve dili okuyun. Config'i dizi kontrolüyle koruyun: kaydedilmemiş modül boş döner.
getMarkup()uygulayın; tam form değil bir parça döndürün, yuvayı yardımcı sarar.check()uygulayın ve düz bir boolean döndürün.- Script etiketi için
headJS(), harcanmış jetonu sıfırlamak içinrefreshJS()ekleyin.
Ayar Alanları
config_fields()metodundan alan tanımını döndürün; burada kullandığınız anahtarlar geri okuduklarınızdır.- Gizli anahtar alanına password tipini verin.
save_fields()uygulayın ve saklanacak diziyi döndürün;$this->errordolu false mesajınızı gösterir.- Üzerine yazmak yerine birleştirin.
- Dönen diziyi
config.phpdosyasına çağıran yazar.
Bir Formu Kapılama
- Bileşeni temadan captcha etiketiyle, alanı adlandırarak gösterin.
- Gönderim işleyicisinde önce sorunun gerekip gerekmediğini hesaplayın, sonra doğrulayın.
- Soru gerekli ama cevapsızsa ayırt edici bir durum döndürün.
- Her gönderimden sonra tazeleme fonksiyonunu çağırın: jetonlar tek kullanımlıktır.
- Hazır listede olmayan bir form için yeni alan adını alanlar kancasıyla kaydedin.
Referans
Metot Sözleşmesi
Korumasız çağrılan tek metot check() olduğu için yazmadan geçemeyeceğiniz tek metot odur. Diğerleri method_exists ile yoklanır.
| Metot | Çağıran | Döndürür |
|---|---|---|
check() | Gönderim işleyicisi | bool; başka şey incelenmez |
getMarkup() | Bileşen kurucusu | HTML parçası |
headJS() | Bileşen kurucusu, istek başına bir kez | script etiketi ya da boş dize |
refreshJS() | Bileşen kurucusu | JavaScript ifadesi ya da boş dize |
getInputName() | Yardımcının yapıcısı | cevap alanının adı |
generateDisplay() | Paylaşılan görsel ucu | görseli yazar, boş dize döner |
config_fields() | Ayar ekranı | alan tanım haritası |
save_fields($fields) | Ayar kaydetme | saklanacak dizi ya da false |
getInputName() varlığı aile işaretidir. Ona sahip modül kod sorusu sayılır: yardımcı çerçeveli bir kutu ve o adı taşıyan bir metin girdisi ekler. Sahip olmayan modülün işaretlemesi doğrudan yuvaya girer.
İmzalar
public function __construct();
// Karar. Durum dizisi değil bir bool: yardımcı onu doğrudan çağırana döndürür.
public function check(): bool;
// Bileşen parçası. Form etiketi yok, sarmalayıcı yok: yuvayı yardımcı sağlar.
public function getMarkup(): string;
// İstek başına sağlayıcı başına bir kez, yuvanın üstünde basılır.
public function headJS(): string;
// Fonksiyon değil bir ifade: yardımcı onu window.wcpCaptchaRefresh içine sarar.
public function refreshJS(): string;
// Yalnız kod sağlayıcıları. VARLIĞI yuvayı kod kipine geçirir.
public function getInputName(): string;
// Yalnız kod sağlayıcıları. Görseli kendisi yazar ve '' döndürür.
public function generateDisplay(): string;
// Ayar ekranı.
public function config_fields(): array;
public function save_fields($fields = []): array|bool;
// config_fields(): dizi anahtarı config anahtarının TA KENDİSİDİR. Burada ne ad verirseniz
// save_fields() metoduna o gelir ve $this->config içinden onu geri okursunuz.
public function config_fields(): array
{
return [
'site-key' => [
'wrap_width' => 100, // alan satırının yüzde olarak genişliği
'name' => "Site Key", // etiket
'description' => "", // alanın altındaki yardım metni
'type' => "text", // text | password | select | switch
'value' => $this->config['site-key'] ?? '',
],
'secret-key' => [
'wrap_width' => 100,
'name' => "Secret Key",
'description' => "",
'type' => "password",
'value' => $this->config['secret-key'] ?? '',
],
];
}
// save_fields(): doğrulayın, sonra saklanacak diziyi döndürün. Dosyayı ÇAĞIRAN yazar.
public function save_fields($fields = []): array|bool
{
if (!isset($fields['site-key']) || !$fields['secret-key']) {
$this->error = $this->lang['error1'];
return false;
}
return $this->config ? array_replace_recursive($this->config, $fields) : $fields;
}
Sınıfınızda $error bulunmak zorundadır: ayar kaydetme false dönüşünden sonra onu okur.
Operatörün Seçimleri
Bunların hiçbiri modülünüzün config'inde değil, paylaşılan seçenekler dosyasındadır.
tray (collapse id'si), class ve force (her zaman görünür) kabul eder.
Üç Durum
Aynı bileşen çağrısı üç sonuç üretir; hangisine baktığınızı bilmek "neden görünmüyor" sorusunu cevaplar.
| Durum | Ne zaman | Sonuç |
|---|---|---|
| Statik | Alan açıkken | Yuva görünür, cevap alanı kapı işaretini taşır |
| Uyarlanır | Alan kapalı, bot kalkanı izliyorsa | Yuva kapalı tepside, kapı işareti olmadan |
| Yok | İkisi de değilse ve zorlanmadıysa | Boş bir dize |
Uyarlanır durum kapı işaretini bilerek atlar. Atlamasaydı tema ilk gönderimi engellerdi ve bot kalkanı hiçbir denemeyi göremezdi. Adresi kalkanı tetiklemiş bir ziyaretçiye kutu baştan gösterilir.
Örnek
Jeton tabanlı bir sağlayıcı, sonra onu tüketen tema ve işleyici.
<?php
class Acme
{
public array $lang;
public array $config;
// save_fields() false döndürdükten sonra ayar kaydetme tarafından okunur.
public string $error = '';
public function __construct()
{
$config = Modules::Config("Captcha", __CLASS__);
$this->config = is_array($config) ? $config : [];
$this->lang = Modules::Lang("Captcha", __CLASS__);
}
public function config_fields(): array
{
return [
'site-key' => [
'wrap_width' => 100,
'name' => "Site Key",
'description' => "",
'type' => "text",
'value' => $this->config['site-key'] ?? '',
],
'secret-key' => [
'wrap_width' => 100,
'name' => "Secret Key",
'description' => "",
'type' => "password",
'value' => $this->config['secret-key'] ?? '',
],
];
}
public function save_fields($fields = []): array|bool
{
if (!isset($fields['site-key']) || !$fields['secret-key']) {
$this->error = $this->lang['error1'] ?? 'Both keys are required.';
return false;
}
return $this->config ? array_replace_recursive($this->config, $fields) : $fields;
}
// Bir parça. Yuva sarmalayıcısını ve boşluk yardımcılarını yardımcı ekler.
public function getMarkup(): string
{
return '<div class="acme-captcha" data-sitekey="' . htmlspecialchars($this->config['site-key'] ?? '', ENT_QUOTES) . '" data-theme="auto"></div>';
}
public function headJS(): string
{
return '<script src="https://challenges.example.com/v1/api.js" async defer></script>';
}
// Fonksiyon gövdesi değil bir ifade: yardımcı onu window.wcpCaptchaRefresh içine sarar.
public function refreshJS(): string
{
return 'if (typeof acmeCaptcha !== "undefined") acmeCaptcha.reset();';
}
public function check(): bool
{
// POST alanını sağlayıcı kendi adlandırır, bu yüzden burası kod ailesinin bildirdiği
// girdi adı üzerinden değil doğrudan okunur.
$token = (string) Filter::init("POST/acme-captcha-response", "hclear");
if ($token === '') return false;
$response = Utility::HttpRequest([
'url' => 'https://challenges.example.com/v1/siteverify',
'type' => 'POST',
'data' => [
'secret' => $this->config['secret-key'] ?? '',
'response' => $token,
'remoteip' => UserManager::GetIP(),
],
]);
$decoded = Utility::jdecode((string) $response, true) ?: [];
// Bir bool, başka bir şey değil. Taşıma hatası bir istisna değil başarısız bir sorudur:
// burada fırlatmak, ulaşılamayan bir sağlayıcıyı bozuk bir giriş formuna çevirir.
return (bool) ($decoded['success'] ?? false);
}
}
<form action="{link route='domain'}" method="post" data-check>
<input type="text" name="domain" class="form-control">
{csrf form='domain-check'}
{captcha area='domain-check' tray='domainCaptchaTray'}
<button type="submit" class="btn btn-primary">{lang key='search'}</button>
</form>
// Sıra önemlidir: sahtecilik denetimi, sonra sert engel, sonra soru.
if (!\Validation::verify_csrf_token((string) Filter::init("POST/token", "hclear"), "domain-check"))
return $operation->output(['status' => "error", 'message' => Language::g("needs/csrf-failed")]);
if (\ProcessRestriction::blocked("domain-check"))
return $operation->output(['status' => "error", 'message' => Language::g("needs/too-many-requests")]);
// Sormak için iki bağımsız sebep: operatör alanı açtı ya da bu ziyaretçi
// kalkanı tetikledi. İkisinden biri cevabı zorunlu kılar.
$needCaptcha = \Captcha::enabled("domain-check") || \BotShield::triggered("domain-check");
if ($needCaptcha && !(new \Captcha())->check()) {
\BotShield::record("domain-check");
// Genel bir hata değil ayırt edici bir durum: form JS'i bunda tepsiyi açar.
return $operation->output([
'status' => "captcha_required",
'message' => Language::g("needs/captcha-failed"),
]);
}
// ... asıl sorgu ...
\ProcessRestriction::hit("domain-check");
if ($needCaptcha) \BotShield::clear("domain-check");
else \BotShield::record("domain-check");
Tuzaklar
Yardımcı yapılandırılmış sınıfı yükler; başarısız olursa sessizce yerleşik modülü kurar. Yanlış dizin, eşleşmeyen sınıf adı ya da yapıcıdaki ölümcül hata aynı belirtiyi verir: bileşeninizin yerinde kod görseli çıkar.
Doğrulanmış bir jeton ikinci denemede reddedilir. refreshJS() metodundan bir sıfırlama ifadesi döndürün ve formun finally bloğunda genel tazelemeyi çağırın.
Kendi görselini üreten sağlayıcı ifadesini tek bir oturum yuvasında saklar, yani her görsel isteği onu değiştirir. Bir sayfada birden fazla bileşen varsa hepsinin adresini tek geçişte aynı değerle damgalayın.
getMarkup() yalnız yardımcı sorunun var olması gerektiğine karar verdiğinde çağrılır. Config'i kendiniz kontrol etmek uyarlanır yolu kırar: o yuvanın gizli gelip sonra açılması beklenir.
Alan dizesi hem config'te hem kapı çağrısında anahtardır. Alanınızı alanlar kancasıyla kaydedin, sonra aynı dizeyi tema etiketinde ve işleyicide kullanın.
İ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.