WordPress Cron Job Tanımlama: wp_schedule_event Kullanımı
WordPress ile ciddi bir site yönetiyorsan, er ya da geç arka planda otomatik çalışması gereken işlerle karşılaşırsın. Veritabanı temizliği, rapor üretimi, harici API senkronizasyonu, eski dosyaların silinmesi… Bunların hepsini manuel yapmak hem yorucu hem de güvenilmez. İşte tam bu noktada WordPress’in kendi zamanlama sistemi olan WP-Cron devreye giriyor ve wp_schedule_event() fonksiyonu bu sistemin kalbi haline geliyor.
WP-Cron Nedir, Nasıl Çalışır?
Klasik bir Linux cron job’undan farklı olarak WP-Cron, zaman tabanlı değil, ziyaretçi tabanlı çalışır. Yani WordPress, her sayfa yüklendiğinde zamanı gelmiş işler var mı diye kontrol eder ve varsa onları çalıştırır. Bu yaklaşımın avantajı kurulum gerektirmemesidir; dezavantajı ise düşük trafikli sitelerde zamanlanmış işlerin gecikmeli çalışabileceğidir.
WP-Cron’un çalışma akışı şöyledir:
- Kullanıcı siteye istek atar
- WordPress
wp-cron.phpdosyasını tetikler - Zamanı gelmiş hook’lar kontrol edilir
- İlgili callback fonksiyonlar çalıştırılır
Bu sistemi yönetmek için üç temel fonksiyon kullanırız: wp_schedule_event(), wp_clear_scheduled_hook() ve wp_next_scheduled(). Bu yazıda bunların tamamını gerçek senaryolarla inceleyeceğiz.
wp_schedule_event() Fonksiyonu
wp_schedule_event() fonksiyonu, belirli bir hook’u tekrarlayan aralıklarla çalıştırmak üzere zamanlamak için kullanılır.
Temel sözdizimi:
wp_schedule_event( $timestamp, $recurrence, $hook, $args, $wp_error );
Parametreler şu şekilde açıklanabilir:
- $timestamp: İşin ilk çalışacağı Unix zaman damgası.
time()fonksiyonuyla şimdiki zamanı kullanabilirsin. - $recurrence: Tekrar aralığı. Varsayılan olarak
hourly,twicedaily,daily,weeklydeğerlerini kullanabilirsin ya da özel aralık tanımlayabilirsin. - $hook: Tetiklenecek action hook’un adı. Bu ismi daha sonra
add_action()ile bağlarsın. - $args: Hook’a geçirilecek argümanlar dizisi. Opsiyoneldir.
- $wp_error:
trueolarak ayarlanırsa hata durumundaWP_Errornesnesi döner. WordPress 5.7 ile gelmiştir.
Fonksiyon başarılı olursa true, zaten zamanlanmışsa false döner.
Temel Kullanım: İlk Cron Job
En basit senaryodan başlayalım. Günde bir kez çalışan ve log dosyasına kayıt düşen bir cron job yazalım:
// functions.php içine ekle
function sitem_gunluk_kontrol() {
$log_mesaji = current_time('mysql') . ' - Gunluk kontrol calisti.' . PHP_EOL;
file_put_contents(
WP_CONTENT_DIR . '/logs/gunluk-kontrol.log',
$log_mesaji,
FILE_APPEND | LOCK_EX
);
}
add_action( 'sitem_gunluk_kontrol_hook', 'sitem_gunluk_kontrol' );
// Cron job'u zamanla - sadece bir kez kayıt et
function sitem_cron_job_aktifles() {
if ( ! wp_next_scheduled( 'sitem_gunluk_kontrol_hook' ) ) {
wp_schedule_event( time(), 'daily', 'sitem_gunluk_kontrol_hook' );
}
}
add_action( 'wp', 'sitem_cron_job_aktifles' );
Burada dikkat edilmesi gereken kritik nokta: wp_next_scheduled() kontrolü olmadan her sayfa yüklemesinde wp_schedule_event() çağırırsan, sisteme binlerce tekrarlayan kayıt eklersin. Bu kontrol olmadan ilerleme.
Cron Job’u Temizlemek: Deactivation Hook
Plugin geliştiriyorsan, plugin devre dışı bırakıldığında cron job’unu temizlemen gerekir. Aksi takdirde sistemde “hayalet” cron görevleri kalır:
// Plugin devre dışı bırakıldığında cron'u temizle
function sitem_plugin_deaktif() {
$timestamp = wp_next_scheduled( 'sitem_gunluk_kontrol_hook' );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, 'sitem_gunluk_kontrol_hook' );
}
// Ya da tüm zamanlamaları tek seferde temizle
wp_clear_scheduled_hook( 'sitem_gunluk_kontrol_hook' );
}
register_deactivation_hook( __FILE__, 'sitem_plugin_deaktif' );
wp_clear_scheduled_hook() kullanmak daha güvenlidir çünkü argümansız çağrıldığında o hook için tüm zamanlamaları siler.
Özel Tekrar Aralıkları Tanımlamak
WordPress’in varsayılan aralıkları (hourly, twicedaily, daily, weekly) çoğu zaman yetmez. 5 dakikada bir, 15 dakikada bir ya da 2 haftada bir çalışan işlere ihtiyaç duyarsın. Bunun için cron_schedules filter’ını kullanırsın:
// Özel cron aralıkları ekle
function sitem_ozel_cron_araliklari( $schedules ) {
// Her 5 dakikada bir
$schedules['her_5_dakika'] = array(
'interval' => 300,
'display' => __( 'Her 5 Dakikada Bir' ),
);
// Her 15 dakikada bir
$schedules['ceyrek_saatte_bir'] = array(
'interval' => 900,
'display' => __( 'Her 15 Dakikada Bir' ),
);
// Her 2 haftada bir
$schedules['iki_haftada_bir'] = array(
'interval' => 1209600,
'display' => __( 'İki Haftada Bir' ),
);
return $schedules;
}
add_filter( 'cron_schedules', 'sitem_ozel_cron_araliklari' );
// Bu özel aralığı kullanan bir cron job
function sitem_5_dakika_job_aktifles() {
if ( ! wp_next_scheduled( 'sitem_api_senkron_hook' ) ) {
wp_schedule_event( time(), 'her_5_dakika', 'sitem_api_senkron_hook' );
}
}
add_action( 'wp', 'sitem_5_dakika_job_aktifles' );
Gerçek Dünya Senaryosu 1: WooCommerce Sipariş Temizliği
E-ticaret sitelerinde beklemedeki siparişler zamanla birikir ve veritabanını şişirir. Şu senaryo bunu otomatik temizler:
// 30 günden eski "pending" siparişleri temizle
function sitem_eski_siparis_temizle() {
if ( ! class_exists( 'WooCommerce' ) ) {
return;
}
$otuz_gun_once = strtotime( '-30 days' );
$eski_siparisler = wc_get_orders( array(
'status' => 'pending',
'date_created' => '<' . $otuz_gun_once,
'limit' => 50, // Tek seferde maksimum 50 işle
'return' => 'ids',
) );
if ( empty( $eski_siparisler ) ) {
return;
}
foreach ( $eski_siparisler as $siparis_id ) {
$siparis = wc_get_order( $siparis_id );
if ( $siparis ) {
$siparis->update_status( 'cancelled', 'Otomatik iptal: 30 gun icinde odeme yapilmadi.' );
}
}
// İşlemi logla
error_log( sprintf(
'[WC Temizlik] %d adet eski siparis iptal edildi. Tarih: %s',
count( $eski_siparisler ),
current_time( 'mysql' )
) );
}
add_action( 'sitem_eski_siparis_temizle_hook', 'sitem_eski_siparis_temizle' );
function sitem_wc_cron_aktifles() {
if ( ! wp_next_scheduled( 'sitem_eski_siparis_temizle_hook' ) ) {
// Her gece yarısı çalışacak şekilde ayarla
$gece_yarisi = strtotime( 'midnight tomorrow' );
wp_schedule_event( $gece_yarisi, 'daily', 'sitem_eski_siparis_temizle_hook' );
}
}
add_action( 'init', 'sitem_wc_cron_aktifles' );
Bu örnekte dikkat etmeni istediğim birkaç nokta var. limit parametresi çok önemli; 10.000 siparişi tek seferde işlemeye kalkışırsan sunucu timeout alırsın. Küçük parçalar halinde işle ve gerekirse bir sonraki çalışmada kalanları işle.
Gerçek Dünya Senaryosu 2: Harici API Senkronizasyonu
Diyelim ki bir stok yönetim API’siyle ürün stok miktarlarını senkronize etmen gerekiyor:
// API'den stok verisi çek ve güncelle
function sitem_stok_senkronizasyon() {
// Son senkronizasyon zamanını kontrol et
$son_senkron = get_option( 'sitem_son_stok_senkron', 0 );
$su_an = time();
// Güvenlik: Son 10 dakika içinde çalıştıysa atla
if ( ( $su_an - $son_senkron ) < 600 ) {
return;
}
$api_url = 'https://api.ornekdepo.com/v1/stok';
$api_key = get_option( 'sitem_api_key' );
$yanit = wp_remote_get( $api_url, array(
'timeout' => 30,
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
),
) );
if ( is_wp_error( $yanit ) ) {
error_log( '[Stok Senkron] API hatasi: ' . $yanit->get_error_message() );
return;
}
$http_kodu = wp_remote_retrieve_response_code( $yanit );
if ( $http_kodu !== 200 ) {
error_log( '[Stok Senkron] Beklenmeyen HTTP kodu: ' . $http_kodu );
return;
}
$veri = json_decode( wp_remote_retrieve_body( $yanit ), true );
if ( empty( $veri['urunler'] ) ) {
return;
}
foreach ( $veri['urunler'] as $urun ) {
$urun_id = wc_get_product_id_by_sku( $urun['sku'] );
if ( $urun_id ) {
update_post_meta( $urun_id, '_stock', $urun['miktar'] );
wc_update_product_stock_status( $urun_id, $urun['miktar'] > 0 ? 'instock' : 'outofstock' );
}
}
// Son senkronizasyon zamanını kaydet
update_option( 'sitem_son_stok_senkron', $su_an );
error_log( sprintf(
'[Stok Senkron] %d urun guncellendi. Zaman: %s',
count( $veri['urunler'] ),
current_time('mysql')
) );
}
add_action( 'sitem_stok_senkron_hook', 'sitem_stok_senkronizasyon' );
function sitem_stok_cron_aktifles() {
if ( ! wp_next_scheduled( 'sitem_stok_senkron_hook' ) ) {
wp_schedule_event( time(), 'ceyrek_saatte_bir', 'sitem_stok_senkron_hook' );
}
}
add_action( 'wp', 'sitem_stok_cron_aktifles' );
WP-Cron’un Zayıf Noktası: Düşük Trafik Sorunu
WP-Cron’un en büyük sorunu ziyaretçi bağımlılığıdır. Sabah 3’te kimse sitende değilse, o saatte çalışması gereken iş çalışmaz. Bu problemi çözmenin iki yolu var.
Yol 1: wp-config.php’de WP-Cron’u devre dışı bırak ve gerçek cron ile tetikle
// wp-config.php dosyasına ekle
define( 'DISABLE_WP_CRON', true );
Sonra sunucu tarafında gerçek bir cron job tanımla:
# crontab -e ile düzenle
# Her 5 dakikada bir wp-cron.php'yi tetikle
*/5 * * * * wget -q -O - https://siteniz.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
# Ya da WP-CLI kullanıyorsan (çok daha temiz)
*/5 * * * * cd /var/www/html && wp cron event run --due-now --allow-root >> /var/log/wp-cron.log 2>&1
WP-CLI kullanımı çok daha tercih edilir bir yöntemdir çünkü HTTP isteği atmak yerine doğrudan PHP üzerinden çalışır, timeout sorunları olmaz ve loglama çok daha kolaydır.
Yol 2: Alternatif Action Scheduler Kullanımı
WooCommerce ile çalışıyorsan Action Scheduler kütüphanesi zaten yüklüdür ve WP-Cron’dan çok daha güvenilirdir. Ama bu konu başlı başına ayrı bir yazıyı hak ediyor.
Cron Job Durumunu Kontrol Etmek
Cron job’larının gerçekten çalışıp çalışmadığını izlemek için birkaç yardımcı fonksiyon var:
// Bir sonraki çalışma zamanını görüntüle
function sitem_cron_durum_goster() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$hooklar = array(
'sitem_gunluk_kontrol_hook',
'sitem_stok_senkron_hook',
'sitem_eski_siparis_temizle_hook',
);
echo '<div class="sitem-cron-durum">';
echo '<h3>Cron Job Durumu</h3>';
foreach ( $hooklar as $hook ) {
$sonraki = wp_next_scheduled( $hook );
if ( $sonraki ) {
$kalan_sure = human_time_diff( time(), $sonraki );
echo sprintf(
'<p><strong>%s</strong>: %s sonra calısacak (%s)</p>',
esc_html( $hook ),
esc_html( $kalan_sure ),
esc_html( date( 'd.m.Y H:i:s', $sonraki ) )
);
} else {
echo sprintf(
'<p><strong>%s</strong>: <span style="color:red">Zamanlanmamis!</span></p>',
esc_html( $hook )
);
}
}
echo '</div>';
}
// Admin dashboard widget'ına ekle
function sitem_dashboard_widget_ekle() {
wp_add_dashboard_widget(
'sitem_cron_durum_widget',
'Cron Job Durumu',
'sitem_cron_durum_goster'
);
}
add_action( 'wp_dashboard_setup', 'sitem_dashboard_widget_ekle' );
Argümanlı Cron Job Kullanımı
Bazen aynı callback’i farklı parametrelerle çalıştırmak istersin. Örneğin farklı kategorilerdeki içerikleri ayrı ayrı işlemek:
// Kategori bazlı içerik güncelleme cron job'u
function sitem_kategori_guncelle( $kategori_id, $mod ) {
$icerikler = get_posts( array(
'post_type' => 'post',
'category' => $kategori_id,
'posts_per_page' => 20,
'post_status' => 'publish',
) );
foreach ( $icerikler as $icerik ) {
// mod'a göre farklı işlem yap
if ( $mod === 'yenile' ) {
clean_post_cache( $icerik->ID );
} elseif ( $mod === 'guncelle' ) {
wp_update_post( array( 'ID' => $icerik->ID ) );
}
}
error_log( sprintf(
'[Kategori Guncelle] Kategori: %d, Mod: %s, Islem: %d icerik',
$kategori_id,
$mod,
count( $icerikler )
) );
}
add_action( 'sitem_kategori_guncelle_hook', 'sitem_kategori_guncelle', 10, 2 );
// Birden fazla farklı parametre ile zamanla
function sitem_coklu_cron_aktifles() {
$gorevler = array(
array( 'kategori_id' => 5, 'mod' => 'yenile' ),
array( 'kategori_id' => 12, 'mod' => 'guncelle' ),
array( 'kategori_id' => 8, 'mod' => 'yenile' ),
);
foreach ( $gorevler as $gorev ) {
$args = array( $gorev['kategori_id'], $gorev['mod'] );
if ( ! wp_next_scheduled( 'sitem_kategori_guncelle_hook', $args ) ) {
wp_schedule_event( time(), 'daily', 'sitem_kategori_guncelle_hook', $args );
}
}
}
add_action( 'init', 'sitem_coklu_cron_aktifles' );
Argüman kullanan cron job’larını temizlerken dikkatli olman gerekir. wp_clear_scheduled_hook() fonksiyonuna aynı argümanları geçmen gerekir, aksi takdirde yalnızca o argümanlarla eşleşen kayıt silinir:
// Argümanlı cron'u temizleme
function sitem_arguman_cron_temizle() {
$gorevler = array(
array( 5, 'yenile' ),
array( 12, 'guncelle' ),
array( 8, 'yenile' ),
);
foreach ( $gorevler as $args ) {
wp_clear_scheduled_hook( 'sitem_kategori_guncelle_hook', $args );
}
}
register_deactivation_hook( __FILE__, 'sitem_arguman_cron_temizle' );
Hata Ayıklama İpuçları
WP-Cron ile çalışırken en sık karşılaşılan sorunlar ve çözümleri:
- Cron çalışmıyor: Önce
wp_next_scheduled()ile zamanlanmış mı kontrol et. WP Crontrol eklentisi bu konuda görsel bir arayüz sunar. - Timeout sorunları: Uzun süren işleri parçalara böl, her çalışmada kaldığı yerden devam et.
set_time_limit(0)kullanmaktan kaçın, bunun yerine işlem boyutunu küçült. - Çift çalışma:
wp_next_scheduled()kontrolünüwpaction’ı yerineinitaction’ına bağlayan geliştiriciler çift kayıt sorunuyla karşılaşır. Her ikisi de çalışır amawpdaha geç tetiklendiği için kontrol daha güvenli gerçekleşir. - Staging ortamı sorunu: Canlı veritabanını staging’e aldığında cron kayıtları da gelir. Staging’de canlı API’lere istek atmamak için ortam kontrolü ekle.
// Ortam kontrolü ile güvenli cron
function sitem_guvenli_cron_kontrol() {
// Sadece production'da çalıştır
if ( defined('WP_ENV') && WP_ENV !== 'production' ) {
return false;
}
return true;
}
Sonuç
wp_schedule_event() doğru kullanıldığında WordPress sitelerinde güçlü bir otomasyon altyapısı kurmana olanak tanır. Ancak birkaç altın kuralı aklında tutman gerekiyor:
- Her cron kaydı öncesinde mutlaka
wp_next_scheduled()kontrolü yap - Plugin’i kapatırken
register_deactivation_hookile cron’ları temizle - Uzun süren işlemleri küçük parçalara böl
- Düşük trafikli ya da kritik zamanlamalar gerektiren sitelerde DISABLE_WP_CRON ile gerçek server cron’una geç
- WP-CLI’nin
wp cron event runkomutunu sevmeye başla, hayatını kolaylaştırır - Her şeyi logla; sabah uyandığında ne çalışıp ne çalışmadığını bilmek istersin
WP-Cron mükemmel değil ama WordPress ekosistemi içinde kalarak arka plan işlerini yönetmenin en pratik yolu. Gerçek sunucu cron ile birleştirdiğinde ise oldukça güvenilir bir sistem elde edersin.
