Shortcode API ile WordPress Kısa Kod Geliştirme
WordPress eklenti geliştirmeye başladığımda, Shortcode API’sini ilk gördüğümde “bu kadar basit mi?” diye düşünmüştüm. Öyle basit ki tehlikeli. Yanlış kullanıldığında sitenizin performansını mahvedebilir, doğru kullanıldığında ise içerik editörlerinizin hayatını kurtarır. Yıllarca farklı projelerde shortcode geliştirdikten sonra öğrendiğim dersleri, karşılaştığım hataları ve gerçekten işe yarayan pattern’ları bu yazıda paylaşacağım.
Shortcode API Nedir ve Neden Önemlidir
WordPress’in veya gibi kısa kodlarını muhtemelen görmüşsünüzdür. İşte tam olarak bunları yaratan mekanizma Shortcode API’sidir. Temel mantık şu: post veya page içeriğine [benim_kodum] yazıyorsunuz, WordPress bunu işlerken bu etiketle kayıtlı olan PHP fonksiyonunuzu çağırıyor ve fonksiyonun return ettiği HTML ile değiştiriyor.
Bu mekanizmanın güçlü tarafı, teknik bilgisi olmayan içerik editörlerinin karmaşık dinamik içerikleri sayfalarına ekleyebilmesi. “Şu kategorideki son 5 ürünü burada göster” gibi bir ihtiyacı [son_urunler kategori="elektronik" adet="5"] şeklinde çözebilirsiniz.
Ama şunu baştan söyleyeyim: Shortcode, her şeyin çözümü değil. Blok editörünün (Gutenberg) yaygınlaştığı şu dönemde bazı use case’ler için custom block daha uygun olabilir. Ancak klasik editör kullanan projeler, page builder entegrasyonları ve eski içerik tabanlarını yönetenler için Shortcode hala çok değerli.
Temel Shortcode Kaydı
En basit haliyle bir shortcode şöyle kaydedilir:
<?php
function merhaba_dunya_shortcode() {
return '<p>Merhaba, Dünya!</p>';
}
add_shortcode( 'merhaba', 'merhaba_dunya_shortcode' );
Bu kadar. Artık herhangi bir post içine [merhaba] yazdığınızda “Merhaba, Dünya!” paragrafı görünecek. Ancak gerçek dünya bu kadar basit değil.
add_shortcode() fonksiyonu iki parametre alır: shortcode etiketi ve callback fonksiyonu. Etiket isimlendirmesinde dikkat edilmesi gereken birkaç nokta var. Küçük harf ve alt çizgi kullanın, boşluk ve özel karakter kullanmayın. Ayrıca mevcut WordPress shortcode’larıyla (gallery, caption, playlist, video, audio, embed) çakışmayın.
Attribut (Öznitelik) Kullanımı
Shortcode’ların asıl gücü attributlarda. shortcode_atts() fonksiyonu hem default değerleri yönetmek hem de güvenli attribute işleme için kritik.
<?php
function urun_listesi_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'kategori' => 'genel',
'adet' => 6,
'siralama' => 'date',
'goster' => 'true',
),
$atts,
'urun_listesi'
);
// String'den integer'a dönüşüm
$adet = absint( $atts['adet'] );
// Boolean kontrolü
$goster = filter_var( $atts['goster'], FILTER_VALIDATE_BOOLEAN );
$args = array(
'post_type' => 'product',
'posts_per_page' => $adet,
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => sanitize_text_field( $atts['kategori'] ),
),
),
'orderby' => sanitize_key( $atts['siralama'] ),
);
$query = new WP_Query( $args );
if ( ! $query->have_posts() ) {
return '<p>Bu kategoride ürün bulunamadı.</p>';
}
ob_start();
while ( $query->have_posts() ) {
$query->the_post();
// Template render
echo '<div class="urun-item">' . get_the_title() . '</div>';
}
wp_reset_postdata();
return ob_get_clean();
}
add_shortcode( 'urun_listesi', 'urun_listesi_shortcode' );
shortcode_atts() fonksiyonunun üçüncü parametresi olan shortcode adını (urun_listesi) mutlaka geçin. Bu parametre shortcode_atts_{shortcode_tag} filter hook’unu aktive eder ve başka geliştiricilerin veya sizin başka bir eklentiden bu default değerleri override etmesine imkan tanır.
İç İçe İçerik (Enclosing Shortcode)
Bazı shortcode’lar açılış ve kapanış etiketi arasındaki içeriği işler. Buna enclosing shortcode denir.
<?php
function bilgi_kutusu_shortcode( $atts, $content = null ) {
$atts = shortcode_atts(
array(
'tip' => 'info',
'baslik' => '',
),
$atts,
'bilgi_kutusu'
);
$izin_verilen_tipler = array( 'info', 'warning', 'success', 'error' );
$tip = in_array( $atts['tip'], $izin_verilen_tipler, true )
? $atts['tip']
: 'info';
$baslik_html = '';
if ( ! empty( $atts['baslik'] ) ) {
$baslik_html = '<h4 class="bilgi-baslik">'
. esc_html( $atts['baslik'] )
. '</h4>';
}
// İç içe shortcode'ların işlenmesi için do_shortcode kullanıyoruz
$icerik = do_shortcode( wp_kses_post( $content ) );
return sprintf(
'<div class="bilgi-kutusu bilgi-kutusu--%s">%s<div class="bilgi-icerik">%s</div></div>',
esc_attr( $tip ),
$baslik_html,
$icerik
);
}
add_shortcode( 'bilgi_kutusu', 'bilgi_kutusu_shortcode' );
Kullanımı şöyle olur:
[bilgi_kutusu tip="warning" baslik="Dikkat!"]
Bu işlemi yapmadan önce yedeğinizi aldığınızdan emin olun.
[/bilgi_kutusu]
$content parametresinin null olmasına dikkat edin. Shortcode [bilgi_kutusu] şeklinde kapatma etiketi olmadan da kullanılabilmesi için bu önemli.
Output Buffering ile Karmaşık Template’lar
Karmaşık HTML üretirken string concatenation yerine output buffering kullanmak kodu çok daha okunabilir kılar. Bunu zaten yukarıdaki örnekte gösterdim ama biraz daha ileri götürelim:
<?php
function takim_listesi_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'bolum' => '',
'kolon' => 3,
'resim' => 'true',
),
$atts,
'takim_listesi'
);
$kolon = min( max( absint( $atts['kolon'] ), 1 ), 6 );
$resim_goster = filter_var( $atts['resim'], FILTER_VALIDATE_BOOLEAN );
$args = array(
'post_type' => 'team_member',
'posts_per_page' => -1,
'orderby' => 'menu_order',
'order' => 'ASC',
);
if ( ! empty( $atts['bolum'] ) ) {
$args['meta_query'] = array(
array(
'key' => 'bolum',
'value' => sanitize_text_field( $atts['bolum'] ),
),
);
}
$uye_sorgu = new WP_Query( $args );
if ( ! $uye_sorgu->have_posts() ) {
return '';
}
ob_start();
?>
<div class="takim-listesi takim-listesi--kolon-<?php echo esc_attr( $kolon ); ?>">
<?php while ( $uye_sorgu->have_posts() ) : $uye_sorgu->the_post(); ?>
<div class="takim-uye">
<?php if ( $resim_goster && has_post_thumbnail() ) : ?>
<div class="takim-uye__resim">
<?php the_post_thumbnail( 'medium' ); ?>
</div>
<?php endif; ?>
<div class="takim-uye__bilgi">
<h3 class="takim-uye__ad"><?php the_title(); ?></h3>
<p class="takim-uye__unvan">
<?php echo esc_html( get_post_meta( get_the_ID(), 'unvan', true ) ); ?>
</p>
</div>
</div>
<?php endwhile; ?>
</div>
<?php
wp_reset_postdata();
return ob_get_clean();
}
add_shortcode( 'takim_listesi', 'takim_listesi_shortcode' );
Shortcode’lara Script ve Style Eklemek
Bu konuda çok yaygın bir hata görüyorum: wp_enqueue_scripts hook’una koyarak her sayfaya CSS/JS yüklemek. Shortcode sadece belirli sayfalarda kullanılacaksa bu gereksiz yük demek. Doğru yaklaşım conditional enqueue.
<?php
class Harita_Shortcode {
private static $yuklu = false;
public static function init() {
add_shortcode( 'harita', array( __CLASS__, 'render' ) );
}
public static function render( $atts ) {
$atts = shortcode_atts(
array(
'enlem' => '41.0082',
'boylam' => '28.9784',
'zoom' => 13,
'yukseklik' => '400px',
),
$atts,
'harita'
);
// Sadece shortcode kullanıldığında asset yükle
if ( ! self::$yuklu ) {
wp_enqueue_style(
'leaflet',
'https://unpkg.com/[email protected]/dist/leaflet.css',
array(),
'1.9.4'
);
wp_enqueue_script(
'leaflet',
'https://unpkg.com/[email protected]/dist/leaflet.js',
array(),
'1.9.4',
true
);
wp_enqueue_script(
'harita-shortcode',
plugin_dir_url( __FILE__ ) . 'assets/js/harita.js',
array( 'leaflet' ),
'1.0.0',
true
);
self::$yuklu = true;
}
$harita_id = 'harita-' . wp_unique_id();
return sprintf(
'<div id="%s" class="harita-konteyner"
style="height:%s"
data-enlem="%s"
data-boylam="%s"
data-zoom="%d">
</div>',
esc_attr( $harita_id ),
esc_attr( $atts['yukseklik'] ),
esc_attr( $atts['enlem'] ),
esc_attr( $atts['boylam'] ),
absint( $atts['zoom'] )
);
}
}
Harita_Shortcode::init();
Static $yuklu değişkeni sayesinde aynı sayfada birden fazla harita shortcode’u kullansanız bile asset’lar sadece bir kez yüklenir.
Güvenlik: Sanitization ve Escaping
Shortcode güvenliği konusunda iki temel kural var ve bunları hiç ihlal etmeyin:
Girdi temizleme (sanitization): Kullanıcıdan veya içerik editöründen gelen her veriyi kullanmadan önce temizleyin.
Çıktı kaçırma (escaping): HTML’e yazdırdığınız her değeri uygun escape fonksiyonuyla işleyin.
<?php
function guvenli_form_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'baslik' => 'İletişim Formu',
'buton_metin' => 'Gönder',
'hedef_email' => get_option( 'admin_email' ),
),
$atts,
'iletisim_formu'
);
// Email adresini doğrula
$email = sanitize_email( $atts['hedef_email'] );
if ( ! is_email( $email ) ) {
// Admin görebilsin ama ziyaretçi göremesin
if ( current_user_can( 'manage_options' ) ) {
return '<p style="color:red;">Geçersiz email adresi yapılandırması.</p>';
}
return '';
}
// Nonce ile CSRF koruması
$nonce = wp_create_nonce( 'iletisim_formu_nonce' );
return sprintf(
'<form class="iletisim-formu" method="post" action="">
<h3>%s</h3>
<input type="hidden" name="iletisim_nonce" value="%s">
<input type="hidden" name="hedef_email" value="%s">
<label>Adınız: <input type="text" name="gonderen_ad" required></label>
<label>Email: <input type="email" name="gonderen_email" required></label>
<label>Mesaj: <textarea name="mesaj" required></textarea></label>
<button type="submit">%s</button>
</form>',
esc_html( $atts['baslik'] ),
esc_attr( $nonce ),
esc_attr( $email ),
esc_html( $atts['buton_metin'] )
);
}
add_shortcode( 'iletisim_formu', 'guvenli_form_shortcode' );
Performans: Caching ile Shortcode Hızlandırma
Shortcode her sayfa yüklemesinde çalışır. Eğer içinde ağır database sorguları varsa bu ciddi performans sorunu yaratır. Transient API ile caching mutlaka düşünülmeli:
<?php
function populer_yazilar_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'adet' => 5,
'sure' => 7, // Gün cinsinden
'cache' => 'true',
),
$atts,
'populer_yazilar'
);
$adet = absint( $atts['adet'] );
$sure = absint( $atts['sure'] );
$cache_aktif = filter_var( $atts['cache'], FILTER_VALIDATE_BOOLEAN );
$cache_anahtari = 'populer_yazilar_' . md5( serialize( $atts ) );
// Cache'den almayı dene
if ( $cache_aktif ) {
$cachelenmis = get_transient( $cache_anahtari );
if ( false !== $cachelenmis ) {
return $cachelenmis;
}
}
// WooCommerce ürün görüntülenme sayısına göre sıralama
$args = array(
'post_type' => 'post',
'posts_per_page' => $adet,
'meta_key' => 'post_views_count',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'date_query' => array(
array(
'after' => $sure . ' days ago',
),
),
);
$sorgu = new WP_Query( $args );
if ( ! $sorgu->have_posts() ) {
return '';
}
ob_start();
echo '<ul class="populer-yazilar">';
while ( $sorgu->have_posts() ) {
$sorgu->the_post();
printf(
'<li><a href="%s">%s</a></li>',
esc_url( get_permalink() ),
esc_html( get_the_title() )
);
}
echo '</ul>';
wp_reset_postdata();
$cikti = ob_get_clean();
// Cache'e kaydet (saniye cinsinden: gün * 86400)
if ( $cache_aktif ) {
set_transient( $cache_anahtari, $cikti, $sure * DAY_IN_SECONDS );
}
return $cikti;
}
add_shortcode( 'populer_yazilar', 'populer_yazilar_shortcode' );
Cache invalidation için içerik güncellendiğinde ilgili transient’ları temizleyen hook’lar eklemeyi de unutmayın:
<?php
function populer_yazilar_cache_temizle( $post_id ) {
global $wpdb;
// İlgili tüm transient'ları temizle
$wpdb->query(
"DELETE FROM {$wpdb->options}
WHERE option_name LIKE '_transient_populer_yazilar_%'
OR option_name LIKE '_transient_timeout_populer_yazilar_%'"
);
}
add_action( 'save_post', 'populer_yazilar_cache_temizle' );
add_action( 'deleted_post', 'populer_yazilar_cache_temizle' );
Shortcode’ları Widget ve Page Builder ile Entegre Etmek
Geliştirdiğiniz shortcode’ların Elementor, WPBakery veya Divi gibi sayfa yapıcılarda da çalışmasını istiyorsunuz. Bunların çoğu do_shortcode() destekler ama bazen shortcode çıktısının bir widget içinde de kullanılması gerekiyor. Bunun için do_shortcode() kullanımını bilen basit bir text widget yeterli, ancak kendi widget’ınızı yazıyorsanız:
<?php
// Shortcode'u programatik olarak çalıştırma
$cikti = do_shortcode( '[populer_yazilar adet="3" sure="30"]' );
// Sidebar widget içinde
class Populer_Yazilar_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'populer_yazilar_widget',
'Popüler Yazılar',
array( 'description' => 'Son dönemin popüler yazılarını listeler.' )
);
}
public function widget( $args, $instance ) {
$adet = ! empty( $instance['adet'] ) ? absint( $instance['adet'] ) : 5;
echo $args['before_widget'];
if ( ! empty( $instance['baslik'] ) ) {
echo $args['before_title'] . esc_html( $instance['baslik'] ) . $args['after_title'];
}
// Shortcode mantığını direkt çağır, do_shortcode değil
echo populer_yazilar_shortcode( array( 'adet' => $adet ) );
echo $args['after_widget'];
}
}
add_action( 'widgets_init', function() {
register_widget( 'Populer_Yazilar_Widget' );
} );
Dikkat ettiniz mi? Widget içinde do_shortcode('[populer_yazilar...]') çağırmak yerine fonksiyonu direkt çağırdım. Bu hem daha temiz hem de shortcode parse overhead’ini ortadan kaldırıyor.
Yaygın Hatalar ve Çözümleri
Yıllarca shortcode geliştirirken aynı hataları defalarca gördüm. Bunları şöyle özetleyebilirim:
echokullanmak: Shortcode fonksiyonu içindeechokullanmayın, her zamanreturnkullanın.echokullandığınızda içerik sayfanın tepesinde belirir, shortcode’un bulunduğu yerde değil.
wp_reset_postdata()unutmak:WP_Querykullandığınızda mutlakawp_reset_postdata()çağırın. Unutursanız sayfa geri kalan tüm sorgular için bozuk veri döner.
- Shortcode içinde shortcode: Enclosing shortcode’larda
$contentiçindeki shortcode’ları işlemek içindo_shortcode($content)çağırmayı unutmayın.
- Shortcode etiketinde büyük harf: WordPress etiketleri küçük harfe dönüştürür, büyük harfli etiket kaydedemezsiniz.
- Widget alanlarında shortcode çalışmıyor: Widget metin alanlarında shortcode çalışmıyorsa
add_filter('widget_text', 'do_shortcode')ekleyebilirsiniz.
- Recursive shortcode problemi: Kendi kendini çağıran shortcode döngüsü sunucuyu çökertir.
do_shortcode()çağrılarını dikkatli yapın.
Sonuç
Shortcode API, WordPress ekosisteminde hala güçlü ve geçerli bir araç. Temel prensiplere sadık kaldığınızda, yani her zaman return kullanmak, inputları sanitize etmek, outputları escape etmek, wp_reset_postdata() çağırmak ve ağır sorgularda caching eklemek, ciddi sorunlarla karşılaşmazsınız.
Gutenberg’in yaygınlaşmasıyla “shortcode öldü mü?” sorusu çok soruluyor. Cevabım net: hayır. Özellikle klasik editör kullanan kurumsal projeler, çok sayıda editörün içerik girdiği siteler ve page builder ile birlikte çalışılan projeler için shortcode hala en pratik çözüm. Bununla birlikte, yeni başlayan bir proje için uzun vadeli mimari düşünüyorsanız ve block editor’ü tam benimseyecekseniz, Gutenberg block geliştirmeyi de öğrenmenizi tavsiye ederim. İkisi birbirini tamamlıyor, ikisi de toolkit’inizde olmalı.
