IP Modülü Yazma

1.7k görüntülenme Markdown

Bir konum ve proxy tespiti sağlayıcısını, ürünün ziyaretçinin yerini ve adresin riskini sorduğu tek noktaya bağlayın.

Genel Bakış

Bir IP modülü bir adres hakkında iki soruyu yanıtlar. Nerede, ve proxy mi? Aynı anda tek modül etkindir; seçim modules/ip altında saklanır. Diğer her şey bunu UserManager::ip_info() ve UserManager::is_proxy() üzerinden okur.

Bu cevaplar uzağa uzanır. Para birimi ülke kodundan seçilir, giriş akışı ülkesi değişen oturumu doğrulatır, formlar proxy'yi reddedebilir.

İki modül gelir: WAtlas ve ip_api. Taban sınıf yoktur; sözleşme çağrı noktasının kendisidir.

Ön Koşullar

  • IPv4 ya da IPv6 adresini en azından bir ISO ülke koduna çözen bir sağlayıcı. O alanın yedeği yoktur.
  • Sağlayıcıya dışa HTTP erişimi ve hız sınırı planı.
  • Modül Anatomisi ve Modül Yapılandırması.

Yapı

dosya düzeni
coremio/modules/IP/AcmeGeo/
├── AcmeGeo.php       AcmeGeo sınıfı   (taban sınıf yok, ad alanı gerekmez)
├── config.php        ['website' => 'AcmeGeo', 'key' => '']
└── pages/
    └── settings.php  kimlik bilgisi alanları, Ayarlar içinde render edilir
UserManager::ip_info() Konum giriş noktası. info() çağırıp önbelleğe alır.
UserManager::is_proxy() Risk giriş noktası. proxy() bildirilmişse çağırır, sonra beyaz listeyi uygular.
Modules::getInstance() Nesnenizi kurar; new kullanılmaz.
Modules::getPage() pages/settings.php parçasını Ayarlar ekranına yerleştirir.
coremio/modules/IP Klasörünüz buraya girer.

Adım Adım

1. Modül İskeletini Kurun

  1. coremio/modules/IP/AcmeGeo/ dizinini AcmeGeo.php ile açın. Sınıf adı klasörle aynı olmalı.
  2. config.php yazın. Açılır liste website anahtarını kullanır.
  3. Ayar alanlarınızı boş anahtarlar olarak ekleyin.

2. info() Metodunu Uygulayın

  1. Size verilen adresle sağlayıcınızı çağırın; çekirdek adresi zaten çözdü.
  2. Cevabı normalize edin. İki kural zorunlu: countryCode küçük harf, sağlayıcı yalnız bölge verse bile city dolu.
  3. Başarısızlıkta $this->error yazın ve false döndürün.

3. proxy() Metodunu Uygulayın ya da Uygulamayın

  1. Metot isteğe bağlıdır. Yalnız konum çözen bir modül onsuz da tamamdır; proxy engelleme o zaman karar üretmez.
  2. İki anahtar benzer görünür: proxy "proxy'ye benziyor", result "bunu engelle" demektir. Yalnız result bir şeyi durdurur.
  3. Otonom sistemi as anahtarında AS15169 Example Org biçiminde döndürün; beyaz liste ilk parçayla eşleşir.

4. Ayarlar Sayfasını Ekleyin

  1. pages/settings.php oluşturun: form değil, Ayarlar formuna eklenen düz bir parça.
  2. Her girdiyi ip_api_config[anahtar] olarak adlandırın; ad doğrudan config anahtarıdır.
  3. Mevcut değerleri $module->config üzerinden okuyun.
  4. Ayarlar'da modülünüzü seçin ve kaydedin. Seçim modules/ip anahtarına düşer.

Referans

İki Metot

Uygulanacak bir arayüz yoktur. Aşağıdakiler tam olarak çağrı noktalarıdır.

sınıfınız
public $error;          // false dönüşünden sonra çekirdek tarafından okunur
public $config = [];    // yapıcıda modülün config.php dosyasından doldurulur

public function __construct();

// Konum. ZORUNLU. Aşağıdaki diziyi döndürün, ya da $this->error dolu olarak false.
// classes/UserManager.php içindeki UserManager::ip_info() metodundan çağrılır.
public function info($ip = '');

// Risk puanlama. İSTEĞE BAĞLI - çekirdek çağırmadan önce method_exists() ile yoklar.
// classes/UserManager.php içindeki UserManager::is_proxy() metodundan çağrılır.
public function proxy($ip = '');
çekirdek tarafı
// classes/UserManager.php, ip_info()
$ip_module = Config::get("modules/ip");
$obj       = Modules::getInstance("IP", $ip_module);
if (!$obj) return ['status' => "error", 'message' => "IP module '{$ip_module}' could not be loaded."];

$result = $obj->info($ip);
if (!$result) {
    // Zaman aşımı düz bir false olarak yutulur; başka her şey loglanır ve yüzeye çıkarılır.
    if (stristr($obj->error, 'timed out')) return false;
    Modules::save_log("IP", $ip_module, "check", $ip, $obj->error);

    return ['status' => "error", 'message' => $obj->error];
}

// classes/UserManager.php, is_proxy()
$proxy_obj = Modules::getInstance("IP", $ip_module);
if ($proxy_obj && method_exists($proxy_obj, 'proxy')) {
    $pdata = $proxy_obj->proxy($ip);
    if ($pdata === false) $error = $proxy_obj->error;
}

info() Ne Döndürür

Tüketicilerin okuduğu anahtarlar, bekledikleri biçimle.

AnahtarBiçimKim okur
countryCodeküçük harf ISO kodu, örn. nlPara birimi ve konum denetimi. Yoksa sorgu başarısız.
cityşehir, yoksa bölge adıŞehir hassasiyetinde konum denetimi.
regionNamebölge ya da eyalet adıGösterim; çoğu modül city içine kopyalar.
countryİngilizce ülke adıGösterim.
asAS15169 Example OrgProxy beyaz listesi, ilk parça.
querysorgulanan adresGirdinin yankısı.
zip, lat, lon, timezone, ispdize ya da sayıOpsiyonel; saklanır, zorunlu değildir.
Ülke kodu yoksa false döndürün

Çağıranlar boş countryCode değerini "bilinmiyor" sayar; ama dolu dizi yine başarılı sorgu sayılıp önbelleğe alınır. Arıza, dosya silinene kadar o adrese yapışır.

proxy() Ne Döndürür

result Boolean. Engelleme kararı; bir şeyi durduran tek anahtar budur.
proxy Boolean. "Proxy ya da VPN'e benziyor." Bilgilendiricidir.
hosting Boolean. Veri merkezi aralığı. O da bilgilendiricidir.
as Otonom sistem. Beyaz listedeki bir AS numarası riskli kararı temizler.
score, verdict Serbest ekler. Çekirdek okumaz ama önbelleğe yazılır.

Ayrıntılı biçim yalnız üç boolean döndürür:

çağıran taraf
// Üçüncü argüman true, çıplak karar yerine ayrıntılı dökümü ister.
$verdict = UserManager::is_proxy($ip, false, true);
// ['proxy' => bool, 'hosting' => bool, 'risky' => bool]
// 'risky' sizin 'result' değerinizdir, operatörün beyaz listesi uygulandıktan sonra.

// Giriş ve kayıt kapılarının kullandığı yaygın biçim:
if (Config::get('options/proxy-block') && UserManager::is_proxy() === true)
    throw new Exception(Language::g('errors/error9'));

Önbellek ve Kota

Çekirdek sağlayıcınızı siz kod çalıştırmadan önce korur. Bir düzeltmenin etkisiz görünmesinin sebebi de bu dosyalardır.

temp/ip-log-{ip}.json Başarılı bir info() sonucu, adres başına, süresiz.
temp/{ip}-proxy.json proxy() için aynısı. Bir adresi yeniden denerken ikisini de silin.
coremio/storage/ip-overload-limit.php Tarih ve sayaç. Sınır aşılınca ip_info() hata dizisi döner ve sizi çağırmaz. options/ip-overload-limit, varsayılan 2000; 0 sınırı kaldırır.
coremio/storage/proxy-overload-limit.php Risk sorguları için aynısı: options/proxy-overload-limit, varsayılan 100.
istek içi bellek Adres başına statik harita; tek istekteki tekrar sorgular bedavadır.

Ayarlar Sözleşmesi

config.php: website Açılır liste etiketi, $v["config"]["website"] değerinden. Anahtar yoksa seçenek boş görünür.
girdi adı: ip_api_config[key] Köşeli parantez içindeki ad birebir config anahtarı olur.
$module Parçanın aldığı tek değişken: canlı örneğiniz.
get_ip_api_configs() Parçanızı getiren operation (operations/AdminGeneralSettings.php).
özyinelemeli birleştirme Gönderilen dizi saklı config üzerine birleştirilir; yazmadığınız anahtar değerini korur.

Örnek

Eksiksiz bir modül, ayarlar parçası ve tüketici tarafı.

AcmeGeo.php
<?php

class AcmeGeo
{
    public $error;
    public $config = [];

    public function __construct()
    {
        $this->config = Modules::Config("IP", __CLASS__);
    }

    public function info($ip = '')
    {
        $this->error = null;

        $key = (string) ($this->config["key"] ?? '');
        $url = "https://api.acmegeo.example/v1/lookup/" . rawurlencode($ip);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-API-Key: " . $key]);

        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            $this->error = curl_error($ch);
            $response    = false;
        }
        curl_close($ch);

        if ($response === false) return false;

        $data = Utility::jdecode((string) $response, true);
        if (!is_array($data)) {
            $this->error = "Invalid response from AcmeGeo.";

            return false;
        }

        // Ülke yoksa kullanılabilir cevap da yok. Burada dolu bir dizi döndürmek başarı
        // sayılıp önbelleğe alınır ve dosya silinene dek o adres bozuk kalır.
        $iso = (string) ($data["country_code"] ?? '');
        if (!$iso) {
            $this->error = "Country code not found : " . $ip;

            return false;
        }

        $region = (string) ($data["region"] ?? '');
        $city   = (string) ($data["city"] ?? '');

        return [
            'status'      => "success",
            'query'       => (string) ($data["ip"] ?? $ip),
            'countryCode' => strtolower($iso),          // küçük harf zorunludur
            'country'     => (string) ($data["country_name"] ?? ''),
            'regionName'  => $region ?: $city,
            'city'        => $city ?: $region,          // city alanını asla boş bırakmayın
            'zip'         => (string) ($data["postal"] ?? ''),
            'lat'         => $data["latitude"] ?? '',
            'lon'         => $data["longitude"] ?? '',
            'timezone'    => (string) ($data["time_zone"] ?? ''),
            'as'          => isset($data["asn"]) ? trim("AS" . $data["asn"] . " " . ($data["asn_org"] ?? '')) : '',
        ];
    }

    public function proxy($ip = '')
    {
        $this->error = null;

        $ch = curl_init("https://api.acmegeo.example/v1/risk/" . rawurlencode($ip));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-API-Key: " . (string) ($this->config["key"] ?? '')]);

        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            $this->error = curl_error($ch);
            $response    = false;
        }
        curl_close($ch);

        if ($response === false) return false;

        $data = Utility::jdecode((string) $response, true);
        if (!is_array($data)) {
            $this->error = "AcmeGeo risk lookup failed.";

            return false;
        }

        $score = (int) ($data["score"] ?? 0);

        return [
            // 'result' engeller, 'proxy' yalnız tanımlar. İkisini birleştirmeyin.
            'result'  => $score >= 60,
            'proxy'   => $score >= 30,
            'hosting' => (bool) ($data["datacenter"] ?? false),
            'score'   => $score,
            'as'      => isset($data["asn"]) ? trim("AS" . $data["asn"] . " " . ($data["asn_org"] ?? '')) : '',
        ];
    }
}
config.php
<?php
return [
    'website' => "AcmeGeo",   // Ayarlar açılır listesinin gösterdiği etiket
    'key'     => '',          // kayıt yolu tarafından geri yazılır, asla elle değil
];
pages/settings.php
<div class="row mb-3 pb-3 pt-3 border-bottom">
    <label for="acmegeo_key" class="col-sm-1 col-form-label text-sm-end">
        <span class="d-block fw-semibold">Api Key</span>
    </label>
    <div class="col-sm-11">
        <!-- Ad, config anahtarıdır: ip_api_config[key] config.php dosyasına 'key' olarak düşer. -->
        <input type="text" class="form-control" id="acmegeo_key"
               name="ip_api_config[key]"
               value="<?php echo $module->config["key"] ?? ''; ?>">
    </div>
</div>
çekirdek tarafı, kopyalamayın
// helpers/Money.php - ziyaretçinin para birimi ülke kodundan gelir.
$info = UserManager::ip_info();

// Günlük kota tükendiğinde ya da modül yüklenemediğinde ip_info() ayrıca
// ['status' => 'error', 'message' => ...] döndürür: dolu, ama countryCode yok. Null-güvenli okuyun.
$needle = strtoupper($info["countryCode"] ?? '');

// classes/Auth.php - giriş konum denetimi, ülke ya da şehir hassasiyetinde.
$country = is_array($info) ? (string) ($info['countryCode'] ?? '') : '';
$city    = is_array($info) ? (string) ($info['city'] ?? '') : '';
if ($country === '') return false;   // ulaşılamayan bir sorgu asla değişiklik sayılmaz

Tuzaklar

Hata error özelliğiyle bildirilir, fırlatılmaz

Sağlama, ödeme ve servis sağlayıcı modülleri hata fırlatır ve çağıranları yakalar. Buradaki iki giriş noktası yakalamaz: boş bir dönüş arar, sonra $this->error okur. info() içinde doğan bir hata küresel işleyiciye kaçar. Özelliği yazın, false döndürün.

Zaman aşımını kısa tutun: sayfa yüklenirken çalışırsınız

Bu sorgular sıradan isteklerin içinde çalışır. Hazır modüller iki ile on saniye arası kullanır. İçinde "timed out" ya da "timeout" geçen mesaj bilerek false olarak yutulur.

Önbelleklenmiş bir sorgu değişikliğinizi gizler

Adres başına önbellek dosyalarının süresi yoktur, yani ilk çağrıdan sonra yeni kodunuza o adres için hiç ulaşılmaz. Ölçmeden önce iki dosyayı da silin.

Yalnız tek modül etkindir

Ödeme yöntemlerinin aksine bu, modules/ip altında saklanan tekil bir seçimdir. Modülünüzü kurmak hiçbir şeyi açmaz; önce operatörün onu seçmesi gerekir.

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.