add_filter ile WordPress Çıktısını Değiştirme
WordPress’in kalbi hook sistemidir. Bunu anlamadan eklenti yazmak, motoru anlamadan araba kullanmaya benzer; bir yere kadar gidersin ama ilk virajda kayarsın. add_filter fonksiyonu da bu sistemin belki en kullanışlı parçası. Çıktıyı değiştirmek, içeriği filtrelemek, başka eklentilerin ürettiği veriyi şekillendirmek için sürekli başvurduğum bir mekanizma. Bu yazıda sizi teoriden çok pratiğe götüreceğim, çünkü filter hook’ların gerçek gücü ancak sahada görülüyor.
add_filter Nedir ve Nasıl Çalışır
WordPress çalışırken pek çok veriyi işleyip ekrana basar. Bu veriyi işlenirken yakalamak ve değiştirmek istiyorsanız filter hook’ları devreye girer. add_action‘dan farkı şu: action bir şeyin gerçekleştiğini haber verir, filter ise size veriyi uzatır, “bunu al, değiştir, geri ver” der.
Temel sözdizimi şu şekildedir:
add_filter( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );
Parametreleri tek tek açıklayayım:
- $hook_name: Hangi filtreye bağlanmak istediğinizi belirtir. Örneğin
the_content,the_title,wp_titlegibi WordPress çekirdeğinin tanımladığı hook adları. - $callback: Veriyi işleyecek fonksiyon. İsimli bir fonksiyon, anonim fonksiyon veya sınıf metodu olabilir.
- $priority: Aynı hook’a bağlanmış birden fazla callback varsa hangi sırada çalışacağını belirler. Düşük sayı önce çalışır. Varsayılan 10’dur.
- $accepted_args: Callback’inizin kaç parametre alacağını söyler. Bazı hook’lar filtrelenen verinin yanında ek parametreler de geçirir.
En kritik kural şu: filter callback’iniz her zaman bir değer döndürmek zorundadır. Return etmeyi unutursanız, ilgili içerik tamamen boşalır ve saatler süren hata ayıklama seanslarına girersiniz. Bu hatayı ben de yaptım, muhtemelen her WordPress geliştiricisi bir kez yapar.
İlk Adım: the_content Filtresi
En sık kullanılan filter hook tartışmasız the_content‘tir. Blog yazılarınızın sonuna otomatik imza eklemek, belirli kelimeleri vurgulu göstermek veya içeriğe dinamik bir kutu eklemek için mükemmeldir.
<?php
function yazar_imzasi_ekle( $content ) {
if ( is_single() && in_the_loop() && is_main_query() ) {
$imza = '<div class="yazar-imzasi" style="border-top: 1px solid #ddd; margin-top: 20px; padding-top: 15px;">';
$imza .= '<p><strong>Bu yazıyı faydalı buldunuz mu?</strong> Yorum bırakmayı unutmayın.</p>';
$imza .= '</div>';
$content .= $imza;
}
return $content;
}
add_filter( 'the_content', 'yazar_imzasi_ekle' );
Buradaki is_single() && in_the_loop() && is_main_query() kontrolüne dikkat edin. Bu kontrol olmadan imzanız widget alanlarında, sayfanın başka bölgelerinde, hatta e-posta bildirimlerinde de görünebilir. Bu üçlü kontrol gerçek hayatta canınızı bir çok kereden kurtarır.
the_title Filtresiyle Başlıkları Şekillendirmek
Özellikle çok yazarlı sitelerde veya belirli kategorilerdeki yazıları görsel olarak ayırt etmek istediğinizde başlık filtresi işe yarar.
<?php
function kategori_rozeti_ekle( $title, $id = null ) {
if ( ! is_admin() && $id && is_singular() ) {
$categories = get_the_category( $id );
foreach ( $categories as $category ) {
if ( $category->slug === 'duyuru' ) {
$title = '<span class="duyuru-rozeti">🔔 Duyuru</span> ' . $title;
break;
}
}
}
return $title;
}
add_filter( 'the_title', 'kategori_rozeti_ekle', 10, 2 );
Burada accepted_args parametresini 2 yaptığımıza dikkat edin. the_title hook’u ikinci argüman olarak post ID’sini de geçirir. Bunu yakalamadan get_the_category() fonksiyonunu doğru çağıramazsınız.
is_admin() kontrolünü eklemeyi de ihmal etmeyin. Yoksa yönetici panelindeki yazı listesinde de rozet göreceksiniz, bu hem çirkin görünür hem de beklenmedik sorunlara yol açabilir.
excerpt_length ve excerpt_more: Özet Metinlerini Kontrol Etmek
Haberler, ürün listeleri veya blog anasayfaları için excerpt (özet) uzunluğunu kontrol etmek istediğinizde filter hook’lar biçilmiş kaftandır. WordPress varsayılan olarak 55 kelimelik özet üretir ve sonuna [...] koyar. İkisi de değiştirilebilir.
<?php
function ozet_uzunlugu_ayarla( $length ) {
if ( is_home() || is_archive() ) {
return 30;
}
return $length;
}
add_filter( 'excerpt_length', 'ozet_uzunlugu_ayarla', 999 );
function ozet_sonu_degistir( $more ) {
return '... <a href="' . get_permalink() . '" class="devami-oku">Devamını Oku →</a>';
}
add_filter( 'excerpt_more', 'ozet_sonu_degistir' );
Priority değerini 999 yaptım, çünkü bazı tema ve eklentiler de bu hook’u kullanır. Yüksek bir priority değeri sizin filtrenizin en son çalışmasını garantiler. Bu küçük ama önemli bir ayrıntı.
wp_nav_menu_items ile Menüye Dinamik Öğe Eklemek
Menülere WordPress yönetim panelinden öğe eklemek kolaydır, ama giriş yapmış kullanıcılara farklı, misafir kullanıcılara farklı menü öğeleri göstermek isterseniz filter devreye girer.
<?php
function dinamik_menu_ogeleri( $items, $args ) {
if ( $args->theme_location === 'primary' ) {
if ( is_user_logged_in() ) {
$dashboard_url = admin_url();
$cikis_url = wp_logout_url( home_url() );
$items .= '<li class="menu-item"><a href="' . esc_url( $dashboard_url ) . '">Hesabım</a></li>';
$items .= '<li class="menu-item"><a href="' . esc_url( $cikis_url ) . '">Çıkış Yap</a></li>';
} else {
$giris_url = wp_login_url( get_permalink() );
$items .= '<li class="menu-item"><a href="' . esc_url( $giris_url ) . '">Giriş Yap</a></li>';
}
}
return $items;
}
add_filter( 'wp_nav_menu_items', 'dinamik_menu_ogeleri', 10, 2 );
$args->theme_location kontrolü kritik. Sitenizdeki tüm menülere değil, yalnızca istediğiniz menüye müdahale edersiniz. Birden fazla menü konumu olan temalarda bu kontrolü yapmadan her menü etkilenebilir.
login_errors: Giriş Hata Mesajlarını Gizlemek
Güvenlik odaklı bir filtre örneği vereyim. WordPress varsayılan olarak giriş sayfasında oldukça bilgi verici hatalar gösterir: “Bu kullanıcı adı kayıtlı değil” veya “Şifre yanlış” gibi. Bu mesajlar brute-force saldırganlarına ipucu verir.
<?php
function giris_hatalarini_gizle( $error ) {
$hata_mesaji = '<strong>HATA</strong>: Kullanıcı adı veya şifre hatalı.';
return $hata_mesaji;
}
add_filter( 'login_errors', 'giris_hatalarini_gizle' );
Tek satırlık bir değişiklik ama güvenlik açısından önemi büyük. Benzer şekilde login_message hook’unu kullanarak giriş sayfasına özel bir uyarı mesajı da ekleyebilirsiniz.
Gerçek Dünya Senaryosu: WooCommerce Ürün Fiyatlarını Filtrelemek
Şimdi biraz daha karmaşık bir örneğe geçelim. Bir WooCommerce mağazasında toptan müşterilere belirli bir indirim uygulamak istediğinizi düşünelim. Fiyatı ödeme anında değil, görüntüleme anında da değiştirmek istiyorsunuz.
<?php
function toptan_musteri_fiyati( $price, $product ) {
if ( ! is_user_logged_in() ) {
return $price;
}
$user = wp_get_current_user();
$roller = (array) $user->roles;
if ( in_array( 'toptan_musteri', $roller ) ) {
$ham_fiyat = $product->get_price();
$indirimli = $ham_fiyat * 0.80; // yüzde 20 indirim
$price = wc_price( $indirimli );
}
return $price;
}
add_filter( 'woocommerce_get_price_html', 'toptan_musteri_fiyati', 10, 2 );
Bu örnek birkaç konuyu aynı anda gösteriyor. İkinci argüman olarak $product nesnesini alıyoruz, bu yüzden accepted_args değeri 2. Kullanıcı rolünü kontrol ediyoruz ve yalnızca ilgili kullanıcılara farklı fiyat gösteriyoruz. wc_price() fonksiyonu WooCommerce’in kendi para birimi formatlamasını kullanıyor, böylece çıktı tutarlı kalıyor.
Bir uyarı: Bu yöntem yalnızca görüntülenen fiyatı değiştirir. Sepet ve ödeme fiyatını değiştirmek için woocommerce_cart_item_price ve woocommerce_product_get_price hook’larını da ele almanız gerekir. Sadece görsel fiyatı değiştirip ödeme fiyatını değiştirmezseniz müşteri şikayetleriyle karşılaşırsınız.
Priority Yönetimi ve Hook Çakışmaları
Birden fazla eklentinin aynı hook’u kullandığı durumlarda priority çakışmaları baş ağrısına dönüşebilir. Bunu sistematik yönetmek için şu yaklaşımı benimsiyorum:
<?php
// Erken çalışması gereken filtreler: 1-9
add_filter( 'the_content', 'veri_sanitize_et', 5 );
// Normal filtreler: 10-20
add_filter( 'the_content', 'icerige_reklam_ekle', 15 );
// Geç çalışması gerekenler: 20+
add_filter( 'the_content', 'son_dokunuslari_yap', 25 );
function veri_sanitize_et( $content ) {
// Bu filtre içerik temizleme işlemi yapar
$content = wp_kses_post( $content );
return $content;
}
function icerige_reklam_ekle( $content ) {
if ( is_single() && in_the_loop() ) {
$reklam = '<div class="reklam-alani"><!-- reklam kodu --></div>';
$content = $content . $reklam;
}
return $content;
}
function son_dokunuslari_yap( $content ) {
// Tüm diğer filtreler işlendikten sonra çalışır
$content = str_replace( 'eski-class', 'yeni-class', $content );
return $content;
}
Bir eklenti başka bir eklentinin çıktısını işleyecekse priority sırasına dikkat etmek zorundasınız. Aksi halde “neden benim filtrem çalışmıyor?” sorusuyla saatlerce uğraşabilirsiniz.
remove_filter ile Hook’ları Kaldırmak
add_filter kadar önemli bir konu da remove_filter. Özellikle üçüncü parti eklentilerin uyguladığı filtreleri kaldırmak ya da geçici devre dışı bırakmak için kullanılır.
<?php
function sorunlu_filtreyi_kaldir() {
// Üçüncü parti eklentinin eklediği filtreyi kaldır
remove_filter( 'the_content', 'baska_eklenti_fonksiyonu', 10 );
}
add_action( 'init', 'sorunlu_filtreyi_kaldir' );
Sınıf metotları söz konusu olduğunda işler biraz karmaşıklaşır:
<?php
function sinif_metodunu_kaldir() {
global $baska_eklenti_instance;
if ( isset( $baska_eklenti_instance ) ) {
remove_filter( 'the_content', array( $baska_eklenti_instance, 'icerik_degistir' ), 10 );
}
}
add_action( 'wp_loaded', 'sinif_metodunu_kaldir' );
Sınıf metodunu kaldırmak için instance’ı elde etmeniz gerekir. Bunu yapmanın yolu eklentinin nasıl yapılandırıldığına bağlıdır; kimi eklentiler global değişken kullanır, kimileri singleton pattern uygular.
Filtreleri OOP Yaklaşımıyla Yazmak
Ölçeklendirilebilir eklentiler geliştiriyorsanız filter hook’larınızı fonksiyon yerine sınıf içinde organize etmek çok daha temiz bir yapı sağlar.
<?php
class Icerik_Yoneticisi {
public function __construct() {
add_filter( 'the_content', array( $this, 'icerik_iyilestir' ), 10, 1 );
add_filter( 'the_excerpt', array( $this, 'ozet_iyilestir' ), 10, 1 );
add_filter( 'the_title', array( $this, 'baslik_iyilestir' ), 10, 2 );
}
public function icerik_iyilestir( $content ) {
if ( ! is_singular() ) {
return $content;
}
$content = $this->lazi_yukleme_ekle( $content );
return $content;
}
public function ozet_iyilestir( $excerpt ) {
return $excerpt . ' <span class="ozet-devam">...</span>';
}
public function baslik_iyilestir( $title, $id = null ) {
if ( $id && get_post_meta( $id, '_one_cikan', true ) ) {
return '⭐ ' . $title;
}
return $title;
}
private function lazi_yukleme_ekle( $content ) {
return preg_replace(
'/<img((?!.*bloadingb)[^>]*)>/i',
'<img$1 loading="lazy">',
$content
);
}
}
$icerik_yoneticisi = new Icerik_Yoneticisi();
Bu yaklaşımla tüm ilgili filtreler tek bir sınıf altında toplanır, private metodlarla yardımcı fonksiyonları saklarsınız ve test edilebilirlik çok artar.
Hata Ayıklama: Filtrelerim Neden Çalışmıyor?
Bu soruyu sormayan WordPress geliştiricisi yoktur. En yaygın nedenler şunlardır:
- Return eksikliği: Callback fonksiyonu return etmezse içerik boşalır. Her zaman
return $deger;kullanın. - Hook adı yanlış:
the_contnetyerinethe_contentyazmak gibi yazım hataları sessiz sedasız başarısız olur. Hook adlarını kopyala-yapıştır yapın, elle yazmayın. - Priority çakışması: Başka bir eklenti sizin filtrenizden sonra aynı veriyi eziyordur. Priority değerini yükseltin ve test edin.
- Kapsam sorunu:
is_single(),is_admin()gibi kontroller beklenmedik durumlarda filtrenizin devre dışı kalmasına yol açabilir. Debug için kontrolleri geçici olarak kaldırıp test edin. - Hook çok erken yükleniyor: Bazı hook’lar henüz WordPress tam olarak hazır olmadan tetiklenebilir.
plugins_loadedveyainitaction’larını beklemeniz gerekebilir.
Hızlı debug için şu yöntemi kullanıyorum:
<?php
function filter_debug( $content ) {
// Geliştirme ortamında filtrenin çalışıp çalışmadığını test et
if ( defined('WP_DEBUG') && WP_DEBUG ) {
error_log( 'Filter çalıştı. İçerik uzunluğu: ' . strlen( $content ) );
}
return $content;
}
add_filter( 'the_content', 'filter_debug', 999 );
error_log ile wp-content/debug.log dosyasına yazarak filtrenin tetiklenip tetiklenmediğini doğrulayabilirsiniz.
Sonuç
add_filter, WordPress mimarisinin en zarif parçalarından biridir. Çekirdeği değiştirmeden, tema dosyalarına dokunmadan sistemi şekillendirebilmenizi sağlar. Ama bu güç sorumlulukla gelir: her filtrede return yapmak, admin panelini beklenmedik değişikliklerden korumak, priority’yi bilinçli seçmek ve üçüncü parti eklentilerle çakışmaları gözetmek zorundasınız.
Özellikle WooCommerce gibi hook yoğunluğu yüksek sistemlerde tek bir yanlış priority değeri ciddi sorunlara yol açabilir. Üretim ortamında değişiklik yapmadan önce staging ortamında test etmeyi alışkanlık edinin.
Eklenti geliştirirken filter hook’larını sınıf yapısı içinde organize edin, fonksiyon adlarınızı benzersiz prefix ile isimlendirin (namespace çakışmalarını önlemek için) ve her zaman “bu filtre sadece burada mı çalışmalı?” sorusunu sorun. Bu üç alışkanlık, bakımı kolay ve güvenilir eklentiler yazmanızı sağlar.
