# Kancalar Nasıl Çalışır

https://dev.wisecp.com/tr/kancalar-nasil-calisir

Çekirdek dosyalarına dokunmadan sistemin akışına girmenin yolu: bir ad, bir sıra numarası ve çalışacak bir işlev.

## Genel Bakış

Kanca, çekirdeğin durup **"burada birinin söyleyeceği var mı?"** diye sorduğu noktadır. Sipariş kaydedilir ve sorar; fatura toplamı hesaplanır ve sorar; yönetim ekranı çizilmeden önce sorar. Siz bir işlev bırakırsınız, çekirdek o noktaya geldiğinde onu çağırır.

Bunun aldığı yer şudur: çekirdeği düzenlemeden davranış eklenir. Düzenlenen bir çekirdek dosyası ilk güncellemede geri gelir; kanca dinleyicisi kendi dosyanızda durur ve güncellemeden etkilenmez. Modüllerin sisteme bağlanma biçimi de budur.

Bu kurulumda **1012 kanca noktası** vardır ve konuya göre on yedi alana ayrılır. Adın önündeki sözcük ne için açıldığını söyler.

- **action**: Olan bitti, haber veriliyor. Dönüşünüze bakılmaz — dış bir sisteme yazmak, kayıt düşmek, bildirim atmak için. **307 kanca.**
- **filter**: Bir değer elinizden geçiyor ve onu **değiştirebilirsiniz**: tutar, liste, sorgu, şablon verisi. **215 kanca.**
- **gate**: Bir işlem yapılmadan önce izin soruluyor; **hayır** diyerek durdurabilirsiniz. **133 kanca.**
- **ui**: Bir ekranın belirli yerine kendi işaretlemenizi koyarsınız. En kalabalık aile: **346 kanca.**
- **register**: Sisteme yeni bir yetenek tanıtırsınız: yol, pano parçası, rapor. **11 kanca.**

## Ön Koşullar

- Dinleyicinizin yaşayacağı bir dosya: kurulumun kendi `coremio/hooks` dizininde bir `.php` dosyası ya da bir modülün `hooks.php` dosyası.
- Kurulumu tamamlanmış bir sistem. Kurulum sihirbazı bitmeden hiçbir dinleyici yüklenmez (aşağıdaki tuzaklara bakın).
- Bağlanacağınız kancanın **tam adı**. Ad yanlışsa hiçbir şey olmaz ve hata da almazsınız.

## Yapı

Dinleyici dosyaları **kendiliğinden** okunur; hiçbir yere kayıt yaptırmazsınız. İki yer taranır ve ikisi de aynı anda yüklenir.

```bash
coremio/hooks/*.php                     # kurulumun kendi dinleyicileri
coremio/modules/{Tip}/{Ad}/hooks.php    # her modülün kendi dinleyicileri
```

Yükleme **ilk kanca çalıştığında** olur, sayfa açılışında değil. Dosyalar bir kez okunur ve o istek boyunca bellekte kalır. Yani bir dinleyici eklemek için hiçbir şeyi yeniden başlatmanız gerekmez: dosyayı koyarsınız, sonraki istekte çalışır.

Bir kancanın kaç dinleyicisi olduğu sınırsızdır. Hepsi **sıra numarasına göre** çalışır; küçük sayı önce gider.

## Referans

### Dinleyici kaydetme

```php
// Hook sinifi — coremio/classes/Hook.php
static function add(string $name, int $priority, callable|array $properties): void;
```

- **$name**: Bağlanılacak kancanın tam adı. Doğrulanmaz — yanlış ad sessizce hiçbir şeye bağlanır.
- **$priority**: Çalışma sırası; küçük olan önce. **Aynı sayı iki kez verilemez**: ikinci dinleyici bir sonraki boş sayıya kaydırılır, yani kayıt sırası belirleyici olur.
- **$properties**: Çalışacak şey. Dört biçimden biri (aşağıda).

### Dört dinleyici biçimi

```php
// 1 — kapanış: en yaygını
Hook::add('action:order.created', 10, function ($order) {
    Crm::push($order['id']);
});

// 2 — sınıf metodu: sınıf BIR KEZ kurulur ve tüm kancalarda paylaşılır
Hook::add('filter:invoice.late_fee_amount', 10, ['class' => 'AcmeBilling', 'method' => 'adjust']);

// 3 — statik metot: nesne kurulmaz
Hook::add('gate:order.checkout', 10, ['class' => 'AcmeGuard', 'method::static' => 'allow']);

// 4 — kurucu: metot anahtari YOKSA sinif kurulur ve NESNE donus degeri olur
Hook::add('register:routes', 10, ['class' => 'AcmeRoutes']);
```

> **5.0-beta.4'ten itibaren düz PHP çağrılabilirleri de çalışır**
> 
> 5.0-beta.4'ten itibaren dinleyici herhangi bir PHP çağrılabiliri de olabilir: `[$nesne, 'metot']`, `['AcmeGuard', 'allow']`, `'AcmeGuard::allow'` metni ya da `__invoke` taşıyan bir nesne. 5.0-beta.3'e kadar bu biçimler kabul ediliyor ama **hiç çalışmıyordu**: her çağrı düşüyor, yalnız hata kaydında "Hook execution error" görünüyordu. Eklentiniz eski sürümlerde de çalışacaksa yukarıdaki dört biçimden birini ya da `AcmeGuard::allow(...)` yazımını kullanın.

### Çekirdek kancayı nasıl çalıştırır

```php
static function run(string $name, mixed ...$args): array;
static function runRefs(string $name, mixed &...$args): array;
static function runDetailed(string $name, mixed ...$args): array;
```

- **Hook::run()**: Argümanların **kopyasını** geçirir. Dinleyicinin değişkende yaptığı değişiklik çağırana dönmez; dönüşler bir dizide toplanır.
- **Hook::runRefs()**: Argümanları **referansla** geçirir: dinleyici imzasında `&$x` yazarsa çağıranın değişkenini değiştirir. `filter` ailesinin çalışma biçimi budur.
- **Hook::runDetailed()**: Her dinleyicinin **kaynak dosyası, satırı, dönüşü ve hatası** ile döner. Yönetim panelindeki "bunu kim dinliyor" ekranı bunu kullanır; teşhis için de doğru araç budur.

Üçünün de ortak davranışı: bir dinleyici hata fırlatırsa **yakalanır, kaydedilir ve sıradakine geçilir**. Tek bir bozuk dinleyici ne çekirdeği durdurur ne de diğer dinleyicileri engeller.

## Örnek

Bir dosya, üç dinleyici, üç farklı iş: olayı yakalamak, değeri değiştirmek ve bir işlemi durdurmak.

```php
<?php

// 1 — OLAY: siparis yazildi, disari haber ver. Donusumuze bakilmaz.
Hook::add('action:order.created', 10, function ($order) {
    Crm::push((int) $order['id'], $order);
});

// 2 — DEGER: listeyi degistir. Ilk argumana & koyarsak cagiranin degiskeni degisir.
Hook::add('filter:admin.table.rows', 10, function (&$row, $table) {
    if ($table === 'services') $row['name'] = strtoupper($row['name'] ?? '');
});

// 3 — IZIN: bos olmayan bir string donmek islemi durdurur ve o metin kullaniciya gosterilir.
Hook::add('gate:order.checkout', 10, function ($member, $items) {
    if (Blocklist::has((int) ($member['id'] ?? 0)))
        return 'Bu hesap siparis veremez.';
    return null;                    // null ya da bos string: akis devam eder
});
```

Dosyayı koyduğunuz an biter. Kayıt, derleme ya da yeniden başlatma yoktur; bir sonraki istekte üçü de çalışır.

## Tuzaklar

> **Yanlış ad hiçbir şey söylemez**
> 
> Var olmayan bir kanca adına dinleyici bağlamak **hata vermez**; dinleyici yalnızca hiç çalışmaz. Bir dinleyici "çalışmıyorsa" ilk bakılacak yer koda değil **ada**dır; adı kanca dizininden doğrulayın, hafızadan yazmayın.

> **Boş argüman geçilmez, sonrakiler kayar**
> 
> Çekirdek dinleyicinin parametrelerini **sırayla** bağlar ve boş (`null`) bir argümanı atlar. Atlanan yer kapanmaz: **ondan sonraki argümanlar bir sola kayar** ve ikinci parametre birincinin yerine düşer. İsteğe bağlı bir bağlam değerini boş geçmeyin; ya hiç geçmeyin ya da boş dize, boş dizi gibi bir yer tutucu verin.

> **Dönüş dizisi dinleyici sırasıyla hizalı değil**
> 
> Toplanan dizi yalnız **boş olmayan** dönüşleri taşır. Üç dinleyiciden biri boş dönerse dizide iki eleman olur ve **birinci eleman artık birinci dinleyici değildir**. "İlk sonucu al" varsayımı yalnız tek dinleyicili kancalarda güvenlidir.

> **Fazladan parametre dinleyiciyi düşürür**
> 
> Kanca iki değer fırlatırken üç parametreli bir dinleyici yazarsanız çağrı hata verir; hata yakalanır, kaydedilir ve **dinleyici atlanır**. Dışarıdan görünen şey "çalışmıyor"dur. Dinleyicinin kaç değer aldığını kanca kaydından doğrulayın; fazlasını değil, **eksiğini** yazmak güvenlidir.

> **Addaki önek niyeti söyler, mekanizmayı değil**
> 
> Bir ad `filter:` ile başlıyor diye değerin referansla geldiği **garanti değildir**. Ölçüldüğünde `filter` ailesinin bir bölümü kopyayla çağrılıyordu. Bir kancanın değeri gerçekten değiştirip değiştiremeyeceğini adından çıkarmayın; kanca kaydındaki mekanizma satırından okuyun.

> **Kurulum bitmeden hiçbir dinleyici yüklenmez**
> 
> Kurulum damgası boşken kanca dosyalarının **hiçbiri** okunmaz. Kurulum sihirbazı akışına kanca ile müdahale etmeyi beklemeyin; orası kanca öncesi topraktır.

## İlgili Makaleler

- [Kanca Dinleyicisi Yazma](https://dev.wisecp.com/tr/kanca-dinleyicisi-yazma)
- [Kanca Alanları](https://dev.wisecp.com/tr/kanca-alanlari)
- [Sık Kullanılan Kanca Senaryoları](https://dev.wisecp.com/tr/sik-kullanilan-kanca-senaryolari)
