Action ve Filter Hook Farkı: WordPress Temel Kavramı

WordPress ile eklenti geliştirmeye başladığınızda, er ya da geç hook sistemiyle yüz yüze gelirsiniz. Ve çoğu zaman ilk karşılaşma şu soruyla olur: “Bu add_action mı olmalı, add_filter mı?” Yanlış seçerseniz kod çalışır ama beklediğiniz gibi davranmaz. Ya da hiç çalışmaz. Bu farkı net anlamak, WordPress geliştirmenin temel taşlarından biri.

Ben bu ikisini ilk öğrendiğimde resmi dokümantasyona baktım, “action bir şey yapar, filter bir değeri değiştirir” yazdı. Tamam, anladım sandım. Sonra pratikte yine karıştırdım. Asıl anlama iş başında, hataları görünce geldi. Bu yazıda o yolu biraz kısaltmaya çalışacağım.

Hook Sistemi Neden Var?

WordPress’in çekirdek dosyalarını doğrudan değiştirmeden sistemi genişletmemiz gerekiyor. Bunu mümkün kılan şey hook mimarisi. Hook’lar, WordPress’in belirli noktalarda “buraya bir şey eklemek isteyen var mı?” diye sorduğu yerler. Siz de “evet, ben varım” diyip kodunuzu o noktaya takıyorsunuz.

İki tür hook var: action ve filter. Kavramsal olarak farklı işler yapıyorlar ama teknik olarak çok benzer görünüyorlar. Bu benzerlik başlangıçta kafayı karıştırıyor.

Action Hook Nedir?

Action hook, WordPress’in belirli bir olayı gerçekleştirdiğinde “bu olay gerçekleşti, ilgilenen varsa şimdi devreye girebilir” dediği noktalardır. Burada önemli bir detay var: action hook’larda bir değer alıp değiştirip geri vermezsiniz. Yan etki (side effect) üretirsiniz. Dosya yazarsınız, e-posta gönderirsiniz, veritabanına kayıt atarsınız, HTML çıktısı üretirsiniz.

Action’ın temel özelliği şudur: fonksiyonunuz bir şey yapması için çağrılır, ama döndürdüğü değer WordPress tarafından kullanılmaz.

// Kullanıcı kayıt olduğunda hoş geldin e-postası gönder
add_action('user_register', 'hosgeldin_email_gonder');

function hosgeldin_email_gonder($user_id) {
    $user = get_userdata($user_id);
    $email = $user->user_email;
    $isim = $user->display_name;
    
    wp_mail(
        $email,
        'Aramıza Hoş Geldiniz!',
        "Merhaba $isim, hesabınız başarıyla oluşturuldu."
    );
    
    // Burada return kullansak da kullanmasak da fark etmez
    // WordPress bu fonksiyonun döndürdüğü değere bakmaz
}

Bu örnekte user_register action’ı tetiklendiğinde fonksiyonumuz çalışır. Fonksiyon e-posta gönderir ve biter. Döndürdüğü değerin önemi yok.

Sık Kullanılan Action Hook’lar

  • init: WordPress başladığında, ama header gönderilmeden önce. Custom post type, taxonomy kayıtları burada yapılır.
  • wp_enqueue_scripts: Script ve stil dosyalarını sıraya eklemek için.
  • admin_menu: Admin menüsüne yeni sayfa eklemek için.
  • save_post: Bir yazı kaydedildiğinde.
  • wp_footer: Footer’dan önce, HTML’e bir şey eklemek için.
  • plugins_loaded: Tüm eklentiler yüklendikten sonra.
// Admin paneline özel bir menü sayfası ekle
add_action('admin_menu', 'ozel_ayarlar_sayfasi_ekle');

function ozel_ayarlar_sayfasi_ekle() {
    add_menu_page(
        'Özel Ayarlar',
        'Özel Ayarlar',
        'manage_options',
        'ozel-ayarlar',
        'ozel_ayarlar_sayfasi_render',
        'dashicons-admin-settings',
        80
    );
}

function ozel_ayarlar_sayfasi_render() {
    echo '<div class="wrap"><h1>Özel Ayarlar Sayfası</h1></div>';
}

Burada admin_menu action’ı tetiklenince menü sayfası ekleme işlemi gerçekleşiyor. Bir değer döndürmüyoruz, bir iş yapıyoruz.

Filter Hook Nedir?

Filter hook ise farklı bir mantıkla çalışır. WordPress bir veriyi işlerken “bu veriyi kullanmadan önce değiştirmek isteyen var mı?” diye sorar. Siz o veriyi alır, işler, ve mutlaka geri döndürürsünüz. WordPress sizin döndürdüğünüz değeri kullanır.

Filter’ın en kritik kuralı şudur: fonksiyonunuz her zaman bir değer döndürmelidir. Return yazmayı unutursanız, o değer null olur ve muhtemelen bir şeyler bozulur.

// Yazı başlıklarının sonuna site adı ekle
add_filter('the_title', 'basliga_site_adi_ekle');

function basliga_site_adi_ekle($baslik) {
    // Değeri alıyoruz, değiştiriyoruz, GERİ DÖNDÜRÜYORuz
    return $baslik . ' | ' . get_bloginfo('name');
}

Burada the_title filter’ı tetiklendiğinde fonksiyonumuz çağrılır, başlık değerini alır, üzerine site adı ekler ve döndürür. WordPress artık bu değiştirilmiş başlığı kullanır.

Sık Kullanılan Filter Hook’lar

  • the_content: Yazı içeriği ekrana basmadan önce.
  • the_title: Yazı başlığı kullanılmadan önce.
  • wp_mail: E-posta gönderilmeden önce, mail parametrelerini değiştirmek için.
  • login_redirect: Giriş sonrası yönlendirme URL’sini değiştirmek için.
  • upload_mimes: İzin verilen dosya tiplerini değiştirmek için.
  • excerpt_length: Özet uzunluğunu değiştirmek için.
// Yazı özetini 20 kelimeyle sınırla
add_filter('excerpt_length', 'ozet_uzunlugunu_ayarla');

function ozet_uzunlugunu_ayarla($uzunluk) {
    // $uzunluk mevcut değer (varsayılan 55)
    // Yeni değeri döndürüyoruz
    return 20;
}
// SVG dosyası yüklemesine izin ver
add_filter('upload_mimes', 'svg_yuklemeye_izin_ver');

function svg_yuklemeye_izin_ver($mimes) {
    $mimes['svg'] = 'image/svg+xml';
    return $mimes; // Mutlaka döndür!
}

Aralarındaki Temel Fark: Tek Cümleyle

Eğer “bir işlem yap” diyorsanız action, “bir değer al, değiştir, geri ver” diyorsanız filter kullanırsınız.

Action’da return zorunlu değil, filter’da return olmadan olmaz.

Bu teoride net görünüyor. Ama pratikte kafa karıştıran durumlar çıkıyor. Mesela action hook’lardan da parametreler gelebiliyor. O zaman ne oluyor?

// save_post action'ı post ID ve post objesini parametre geçiyor
add_action('save_post', 'yazı_kaydedilince', 10, 2);

function yazı_kaydedilince($post_id, $post) {
    // $post_id ve $post bilgileri geliyor
    // Ama bunları değiştirip geri vermiyoruz
    // Bunları kullanarak başka bir iş yapıyoruz
    
    if ($post->post_status === 'publish') {
        // Slack'e bildirim gönder
        slack_bildirim_gonder("Yeni yazı yayınlandı: " . $post->post_title);
    }
}

Action’a parametre gelmesi onu filter yapmıyor. Hala bir olay gerçekleşiyor ve biz o olaya tepki veriyoruz, değer değiştirmiyoruz.

Öncelik Parametresi (Priority)

Her iki hook türünde de add_action ve add_filter fonksiyonlarının üçüncü parametresi öncelik (priority) belirler. Varsayılan değer 10’dur. Düşük sayı daha önce çalışır.

// Önce bu çalışır (priority: 5)
add_filter('the_content', 'icerige_giris_ekle', 5);

function icerige_giris_ekle($icerik) {
    return '<div class="giris-notu">Bu yazı hakkında:</div>' . $icerik;
}

// Sonra bu çalışır (priority: 15)
add_filter('the_content', 'icerige_footer_ekle', 15);

function icerige_footer_ekle($icerik) {
    return $icerik . '<div class="yazar-kutusu">Yazar bilgileri burada</div>';
}

Bu sıralama önemli. Bazen birden fazla eklenti aynı hook’u kullanıyordur ve hangi öncelikte çalıştığını bilmek debugging sürecinde hayat kurtarıyor.

Gerçek Dünya Senaryosu: WooCommerce Sipariş Yönetimi

Diyelim ki bir WooCommerce mağazanız var ve şu iki şeyi yapmak istiyorsunuz:

  1. Sipariş tamamlandığında kargo firmasının API’sine bildirim gönder (action)
  2. Sepet toplamına her zaman ekstra bir “hizmet bedeli” ekle (filter)
// 1. ACTION: Sipariş tamamlandığında kargo API'sine bildir
add_action('woocommerce_order_status_completed', 'kargo_api_bildirim_gonder');

function kargo_api_bildirim_gonder($order_id) {
    $order = wc_get_order($order_id);
    
    $kargo_verisi = array(
        'siparis_no' => $order_id,
        'musteri'   => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
        'adres'     => $order->get_shipping_address_1(),
        'sehir'     => $order->get_shipping_city(),
    );
    
    // Kargo API'sine POST isteği
    wp_remote_post('https://kargo-api.example.com/yeni-siparis', array(
        'body' => json_encode($kargo_verisi),
        'headers' => array('Content-Type' => 'application/json'),
    ));
    
    // Log kaydı
    error_log("Kargo bildirimi gönderildi: Sipariş #$order_id");
    
    // Return yazmak zorunda değiliz, ama bazen clarity için yazarız
}
// 2. FILTER: Sepet toplamına hizmet bedeli ekle
add_filter('woocommerce_cart_totals_coupon_label', 'sepet_hizmet_bedeli_etiketi', 10, 2);

// Daha doğrusu, fee eklemek için farklı bir yaklaşım:
add_action('woocommerce_cart_calculate_fees', 'hizmet_bedeli_ekle');

function hizmet_bedeli_ekle($cart) {
    if (is_admin() && !defined('DOING_AJAX')) {
        return;
    }
    
    $cart->add_fee('Hizmet Bedeli', 15.00, true);
}

Dikkat edin: woocommerce_cart_calculate_fees teknik olarak action, ama fee eklemek için cart objesini manipüle ediyoruz. WordPress/WooCommerce hook sisteminin bazı özel durumları bu şekilde çalışıyor.

do_action ve apply_filters ile Kendi Hook’larınızı Oluşturun

Başkalarının eklentilerini veya tema kodunuzu genişletilebilir yapmak istiyorsanız kendi hook’larınızı oluşturabilirsiniz.

// Kendi action hook'unuzu oluşturun
function ozel_eklentim_veri_isle($veri) {
    // Hook öncesinde bir şeyler yap
    
    // Diğerlerinin bu noktada devreye girmesine izin ver
    do_action('ozel_eklentim_veri_islenmeden_once', $veri);
    
    // Asıl işlemi yap
    $sonuc = veriyi_isle($veri);
    
    // Sonuca diğerlerinin müdahale etmesine izin ver (filter)
    $sonuc = apply_filters('ozel_eklentim_sonuc', $sonuc, $veri);
    
    do_action('ozel_eklentim_veri_islendi', $sonuc);
    
    return $sonuc;
}

Başka bir eklenti veya tema şimdi sizin hook’larınızı kullanabilir:

// Başka bir eklenti sizin filter'ınıza takılıyor
add_filter('ozel_eklentim_sonuc', 'sonucu_zenginlestir', 10, 2);

function sonucu_zenginlestir($sonuc, $orijinal_veri) {
    $sonuc['ekstra_alan'] = 'ekstra değer';
    return $sonuc;
}

Bu yaklaşım kodunuzu gerçekten genişletilebilir yapıyor. WordPress’in kendisi de tam olarak bunu yapıyor.

Yaygın Hatalar ve Dikkat Edilecekler

Filter’da return yazmayı unutmak en sık yapılan hata. Şöyle bir senaryo düşünün:

// YANLIŞ - return yok!
add_filter('the_title', 'baslik_buyut_hata');

function baslik_buyut_hata($baslik) {
    $yeni_baslik = strtoupper($baslik);
    // return yazmayı unuttuk
    // Tüm başlıklar artık boş görünecek
}

// DOĞRU
add_filter('the_title', 'baslik_buyut_dogru');

function baslik_buyut_dogru($baslik) {
    return strtoupper($baslik);
}

Action’da return yazmak zararsız ama yanıltıcı olabilir. Action hook’a bağladığınız fonksiyondan bir değer döndürmek teknik olarak hata vermez, ama döndürdüğünüz değer hiçbir yerde kullanılmaz. Bu durum kodunuzu okuyan birine yanlış sinyal verir.

Parametre sayısına dikkat edin. add_action ve add_filter‘ın dördüncü parametresi fonksiyona kaç parametre geçileceğini belirtir. Varsayılan 1’dir.

// save_post normalde 3 parametre geçer: post_id, post, update
// Ama varsayılan olarak sadece 1 tanesi fonksiyona iletilir
// Hepsini almak için 4. parametreyi belirtin

add_action('save_post', 'uc_parametre_al', 10, 3);

function uc_parametre_al($post_id, $post, $guncelleme_mi) {
    if ($guncelleme_mi) {
        // Bu bir güncelleme
    } else {
        // Bu yeni kayıt
    }
}

Debugging: Hook’larım Neden Çalışmıyor?

Bu soruyu kendime yüzlerce kez sordum. Kontrol listesi şöyle:

  • Hook adı doğru mu? Typo olabilir. wp_enqueue_script değil wp_enqueue_scripts (sonunda s var).
  • Hook henüz tetiklendi mi? init yerine wp_head‘e bağladınız ama init çoktan geçti mi?
  • Namespace veya prefix kullanıyor musunuz? Fonksiyon adı çakışması olabilir.
  • apply_filters yerine do_action mı kullandınız? Ya da tersi?
  • Filter’da return var mı?

Basit bir debug için şunu kullanabilirsiniz:

// Belirli bir hook'un çalışıp çalışmadığını test et
add_action('init', 'hook_test_init');

function hook_test_init() {
    error_log('init hook çalıştı: ' . current_filter());
    // current_filter() o an çalışan hook adını verir
}

// Kaç fonksiyonun belirli bir hook'a bağlı olduğunu gör
global $wp_filter;
if (isset($wp_filter['the_title'])) {
    error_log(print_r($wp_filter['the_title'], true));
}

Sonuç

Action ile filter arasındaki fark özünde şu: action bir tetikleyiciye tepki vermek için, filter bir değeri yakalamak ve değiştirmek için. Action “bir şey oldu, sen de bir şey yap” der. Filter “bu değeri kullanmadan önce değiştirmek ister misin?” der.

Pratikte bunu aklınızda tutun: fonksiyonunuz bir değer alıp onu değiştirip geri veriyorsa filter, başka bir iş yapıyorsa action. Ve filter’da return yazmayı asla unutmayın.

Bu iki kavramı sindirdikten sonra WordPress’in geri kalanı çok daha mantıklı hale geliyor. Custom plugin yazmak, mevcut eklentileri genişletmek, tema geliştirmek, hepsinin temeli bu hook sisteminde. Sağlam anladıktan sonra gerisi zaten geliyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir