WordPress’te Zamanlanmış Görev ile Otomatik İçerik Temizleme

WordPress siteni yönetirken en can sıkıcı işlerden biri eski, gereksiz ya da taslak olarak kalan içerikleri temizlemektir. Bunu her hafta elle yapmak hem zaman kaybı hem de insan hatasına açık bir süreçtir. İşte bu noktada WordPress’in WP-Cron sistemi ve functions.php dosyasına ekleyeceğin birkaç satır kod işini tamamen otomatize edebilir. Bu yazıda gerçek dünya senaryolarına dayanan, production ortamında kullanabileceğin zamanlanmış görev örneklerini adım adım inceleyeceğiz.

WP-Cron Nedir ve Nasıl Çalışır?

WordPress’in kendi içinde barındırdığı bir cron sistemi vardır. Sunucu tarafındaki gerçek cron ile karıştırılmamalıdır. WP-Cron, bir ziyaretçi siteye geldiğinde tetiklenir ve o anda zamanı gelen görevleri çalıştırır. Bu yapı küçük siteler için yeterliyken, trafiği az olan sitelerde görevler tam zamanında çalışmayabilir.

Eğer production ortamındaysan ve görevlerin dakikası dakikasına çalışmasını istiyorsan, önce wp-config.php dosyasına şunu ekle:

define('DISABLE_WP_CRON', true);

Sonra sunucundaki gerçek cron tablosuna şu satırı ekle:

*/5 * * * * wget -q -O - https://siteadin.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

Ya da wp-cli kullanıyorsan daha temiz bir yöntem:

*/5 * * * * cd /var/www/html && wp cron event run --due-now --allow-root > /dev/null 2>&1

Bu kurulumu yaptıktan sonra functions.php dosyasına ekleyeceğin her zamanlanmış görev artık güvenilir şekilde çalışacaktır.

Özel Cron Aralığı Tanımlama

WordPress varsayılan olarak hourly, twicedaily ve daily aralıklarını tanır. Bunların dışında bir aralık istiyorsan önce bunu kaydetmen gerekir.

// functions.php dosyasina ekle
add_filter('cron_schedules', 'custom_cron_intervals');

function custom_cron_intervals($schedules) {
    // Her 15 dakikada bir
    $schedules['every_15_minutes'] = array(
        'interval' => 15 * 60,
        'display'  => __('Her 15 Dakikada Bir')
    );

    // Her 3 gunde bir
    $schedules['every_three_days'] = array(
        'interval' => 3 * 24 * 60 * 60,
        'display'  => __('Her 3 Gunde Bir')
    );

    // Haftalik
    $schedules['weekly'] = array(
        'interval' => 7 * 24 * 60 * 60,
        'display'  => __('Haftalik')
    );

    return $schedules;
}

Bu adımı atlamak en yaygın hatalardan biridir. Özel aralık tanımlamadan zamanlanmış görev kurmaya çalışırsan WordPress seni sessizce görmezden gelir ve logda hiçbir şey göremezsin.

Senaryo 1: Eski Taslak Yazıları Temizleme

Bir blog ya da haber sitesi yönetiyorsun ve editörler sürekli taslak yazı açıp yarım bırakıyor. Zamanla bu taslaklar binlerce satıra ulaşıyor ve veritabanını şişiriyor.

// Zamanlanmis gorevi kaydet - once kontrol et, sonra kaydet
add_action('wp', 'schedule_draft_cleanup');

function schedule_draft_cleanup() {
    if (!wp_next_scheduled('cleanup_old_drafts_hook')) {
        wp_schedule_event(time(), 'weekly', 'cleanup_old_drafts_hook');
    }
}

// Asil temizleme fonksiyonu
add_action('cleanup_old_drafts_hook', 'cleanup_old_drafts');

function cleanup_old_drafts() {
    // 90 gunden eski taslak yazilar
    $args = array(
        'post_type'      => 'post',
        'post_status'    => 'draft',
        'posts_per_page' => 50,
        'date_query'     => array(
            array(
                'before'    => '90 days ago',
                'inclusive' => true,
            ),
        ),
        'fields' => 'ids',
    );

    $old_drafts = get_posts($args);

    if (empty($old_drafts)) {
        return;
    }

    $deleted_count = 0;

    foreach ($old_drafts as $post_id) {
        // wp_delete_post ikinci parametre true = cop kutusunu atlayarak tamamen sil
        $result = wp_delete_post($post_id, true);
        if ($result) {
            $deleted_count++;
        }
    }

    // Loglama - debug modunda ise kaydet
    if (defined('WP_DEBUG') && WP_DEBUG) {
        error_log(
            '[Draft Cleanup] ' . date('Y-m-d H:i:s') .
            ' - Silinen taslak sayisi: ' . $deleted_count
        );
    }
}

Bu fonksiyon her hafta çalışır ve 90 günden eski taslakları temizler. posts_per_page değerini 50 ile sınırladık çünkü büyük sitelerde toplu silme işlemi memory sorununa yol açabilir.

Senaryo 2: WooCommerce Eski Sepet Oturumlarını Temizleme

WooCommerce çok ciddi bir veri kirliliği kaynağıdır. Kullanıcılar sepete ürün ekleyip siteyi terk ettiğinde wp_woocommerce_sessions tablosu şişmeye başlar. Orta ölçekli bir e-ticaret sitesinde bu tablo aylar içinde gigabaytlara ulaşabilir.

add_action('wp', 'schedule_wc_session_cleanup');

function schedule_wc_session_cleanup() {
    if (!wp_next_scheduled('cleanup_wc_sessions_hook')) {
        wp_schedule_event(time(), 'daily', 'cleanup_wc_sessions_hook');
    }
}

add_action('cleanup_wc_sessions_hook', 'cleanup_woocommerce_sessions');

function cleanup_woocommerce_sessions() {
    global $wpdb;

    // WooCommerce aktif degilse calisma
    if (!class_exists('WooCommerce')) {
        return;
    }

    // 48 saatten eski ve bos sepetlere ait oturumlar
    $table_name = $wpdb->prefix . 'woocommerce_sessions';

    $deleted = $wpdb->query(
        $wpdb->prepare(
            "DELETE FROM {$table_name}
             WHERE session_expiry < %d
             AND session_key NOT IN (
                 SELECT user_login FROM {$wpdb->users}
             )",
            time()
        )
    );

    // Ek olarak: 2 gunden eski misafir oturumlari
    $two_days_ago = time() - (2 * DAY_IN_SECONDS);

    $wpdb->query(
        $wpdb->prepare(
            "DELETE FROM {$table_name}
             WHERE session_expiry < %d
             LIMIT 1000",
            $two_days_ago
        )
    );

    if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
        error_log('[WC Session Cleanup] Silinen oturum: ' . $deleted);
    }
}

LIMIT 1000 eklemek önemlidir. Yüz binlerce satırlı bir tabloyu tek sorguda temizlemeye kalkarsan MySQL zaman aşımına uğrar ve işlem yarıda kalır.

Senaryo 3: Otomatik Geçici Sayfa ve Post Meta Temizliği

Bazı eklentiler geçici meta veriler bırakır. Bunlar wp_postmeta tablosunu gereksiz yere şişirir.

add_action('wp', 'schedule_postmeta_cleanup');

function schedule_postmeta_cleanup() {
    if (!wp_next_scheduled('cleanup_orphan_postmeta_hook')) {
        // Her 3 gunde bir calis
        wp_schedule_event(time(), 'every_three_days', 'cleanup_orphan_postmeta_hook');
    }
}

add_action('cleanup_orphan_postmeta_hook', 'cleanup_orphan_postmeta');

function cleanup_orphan_postmeta() {
    global $wpdb;

    // Artik var olmayan postlara ait meta verileri temizle
    $wpdb->query(
        "DELETE pm FROM {$wpdb->postmeta} pm
         LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
         WHERE p.ID IS NULL
         LIMIT 500"
    );

    // Belirli eklenti meta anahtarlarini temizle (ornek: eski SEO verileri)
    $meta_keys_to_clean = array(
        '_yoast_wpseo_linkdex_temp',
        '_edit_lock',
        '_pingme',
        '_encloseme'
    );

    foreach ($meta_keys_to_clean as $meta_key) {
        $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM {$wpdb->postmeta}
                 WHERE meta_key = %s
                 AND post_id NOT IN (
                     SELECT ID FROM {$wpdb->posts}
                     WHERE post_status = 'publish'
                 )
                 LIMIT 200",
                $meta_key
            )
        );
    }
}

Senaryo 4: Süresi Dolmuş WooCommerce Kuponlarını Arşivleme

WooCommerce kuponu yönetiminde sık karşılaşılan bir sorun: kampanya dönemlerinde yüzlerce kupon oluşturuluyor, kampanya bittikten sonra kimse silmüyor. Bunlar admin panelinde gereksiz yer kaplar.

add_action('wp', 'schedule_expired_coupon_cleanup');

function schedule_expired_coupon_cleanup() {
    if (!wp_next_scheduled('cleanup_expired_coupons_hook')) {
        wp_schedule_event(time(), 'weekly', 'cleanup_expired_coupons_hook');
    }
}

add_action('cleanup_expired_coupons_hook', 'cleanup_expired_coupons');

function cleanup_expired_coupons() {
    if (!class_exists('WooCommerce')) {
        return;
    }

    $today = date('Y-m-d');

    // Suresi dolmus kuponlari bul
    $expired_coupons = get_posts(array(
        'post_type'      => 'shop_coupon',
        'post_status'    => 'publish',
        'posts_per_page' => 30,
        'meta_query'     => array(
            array(
                'key'     => 'date_expires',
                'value'   => time(),
                'compare' => '<',
                'type'    => 'NUMERIC',
            ),
            array(
                'key'     => 'date_expires',
                'value'   => '',
                'compare' => '!=',
            ),
        ),
        'fields' => 'ids',
    ));

    if (empty($expired_coupons)) {
        return;
    }

    foreach ($expired_coupons as $coupon_id) {
        // Silmek yerine taslaga al - geri almak istersen kolaylasin
        wp_update_post(array(
            'ID'          => $coupon_id,
            'post_status' => 'draft',
        ));

        // Kupon adindan haberdar olmak icin meta ekle
        update_post_meta($coupon_id, '_archived_date', $today);
    }

    error_log(
        '[Coupon Cleanup] ' . count($expired_coupons) .
        ' kupon arsivlendi. Tarih: ' . $today
    );
}

Burada silinmek yerine draft statüsüne alındığına dikkat et. Bu bir güvenlik tercihi. Yanlışlıkla silinmiş bir kuponu geri almak çok zordur, ama taslaktaki bir kuponu yeniden yayınlamak birkaç tıkla olur.

Senaryo 5: WordPress Revizyonlarını Kontrollü Temizleme

WordPress her kaydetmede bir revizyon oluşturur. Aktif bir sitede bu revizyonlar wp_posts tablosunun yarısını oluşturabilir. Önce wp-config.php ile revizyon sayısını sınırlamak iyi bir pratiktir:

# wp-config.php
define('WP_POST_REVISIONS', 5);

Ama önceden birikmiş revizyonlar için zamanlanmış görev şart:

add_action('wp', 'schedule_revision_cleanup');

function schedule_revision_cleanup() {
    if (!wp_next_scheduled('cleanup_old_revisions_hook')) {
        wp_schedule_event(time(), 'weekly', 'cleanup_old_revisions_hook');
    }
}

add_action('cleanup_old_revisions_hook', 'cleanup_old_revisions');

function cleanup_old_revisions() {
    global $wpdb;

    // Her post icin en son 5 revizyonu tut, gerisini sil
    // Once silinecek ID'leri bul
    $revisions_to_delete = $wpdb->get_col(
        "SELECT r.ID
         FROM {$wpdb->posts} r
         WHERE r.post_type = 'revision'
         AND r.ID NOT IN (
             SELECT ID FROM (
                 SELECT ID
                 FROM {$wpdb->posts}
                 WHERE post_type = 'revision'
                 AND post_parent = r.post_parent
                 ORDER BY post_date DESC
                 LIMIT 5
             ) AS recent_revisions
         )
         LIMIT 100"
    );

    if (empty($revisions_to_delete)) {
        return;
    }

    $deleted = 0;
    foreach ($revisions_to_delete as $revision_id) {
        // wp_delete_post_revision kullan - postmeta'yi da temizler
        $result = wp_delete_post_revision((int) $revision_id);
        if (!is_wp_error($result)) {
            $deleted++;
        }
    }

    error_log('[Revision Cleanup] Silinen revizyon: ' . $deleted);
}

Zamanlanmış Görevleri İzleme ve Debug Etme

Görevlerin çalışıp çalışmadığını anlamak başlı başına bir iştir. WP-CLI kullanıyorsan hayat çok kolaylaşır:

# Tum zamanlanmis gorevleri listele
wp cron event list

# Belirli bir gorev ne zaman calisacak?
wp cron event list | grep cleanup

# Gorevi hemen tetikle (test icin)
wp cron event run cleanup_old_drafts_hook

# Zamanlanmis gorev var mi kontrol et
wp cron event list --format=table

Admin panelinde görselleştirmek istersen WP Crontrol eklentisi ideal bir araçtır. Hangi görevin ne zaman çalışacağını, daha önce hata verip vermediğini gösterir.

Görevleri Plugin Deactivation’da Temizleme

Bu çok önemli bir detay: Tema ya da eklenti devre dışı bırakıldığında zamanlanmış görevleri kaldırmazsan WordPress veritabanında çalışamayacak görevler birikir.

// Tema devre disi birakildiginda cron gorevlerini temizle
add_action('switch_theme', 'cleanup_all_scheduled_tasks');

// Dogrudan cagirmak icin yardimci fonksiyon
function cleanup_all_scheduled_tasks() {
    $hooks = array(
        'cleanup_old_drafts_hook',
        'cleanup_wc_sessions_hook',
        'cleanup_orphan_postmeta_hook',
        'cleanup_expired_coupons_hook',
        'cleanup_old_revisions_hook',
    );

    foreach ($hooks as $hook) {
        $timestamp = wp_next_scheduled($hook);
        if ($timestamp) {
            wp_unschedule_event($timestamp, $hook);
        }
        // Alternatif: tum zamanlamalari kaldir
        wp_clear_scheduled_hook($hook);
    }
}

Eğer bu kodu bir eklentide kullanıyorsan register_deactivation_hook ile bağlamak daha doğrudur. Ama functions.php özelinde switch_theme action’ı işini görür.

Performans ve Güvenlik İpuçları

Zamanlanmış görev yazarken dikkat etmen gereken birkaç kritik nokta var:

  • Toplu silme işlemlerinde LIMIT kullan: Yüz binlerce satırlık tablolarda limitsiz DELETE sorgusu MySQL’i kilitleyebilir ve sitenin çökmesine neden olur.
  • Her görevi ayrı hook’a bağla: Tek bir büyük fonksiyona her şeyi tıkıştırma. Bir hata durumunda hangisinin çakıştığını anlamak zorlaşır.
  • $wpdb->prepare() kullanmayı ihmal etme: Dinamik değer geçirdiğin her sorguda bu zorunludur. SQL injection riskini sıfırlar.
  • error_log ile izleme ekle: Production’da WP_DEBUG kapalı olsa bile error_log() çalışır. /var/log/php_errors.log ya da WordPress’in kendi debug log dosyasına yazar.
  • Çalışma zamanını dağıt: Tüm görevleri gece 02:00’a programlama. Aynı saatte çakışan büyük sorgular sunucuyu patlatır. Birini 02:00’a, diğerini 03:00’e, bir diğerini 04:00’e koy.
  • Test ortamında dene: Production’da yeni bir silme görevi ilk kez çalıştırmadan önce mutlaka yerel ortamda ya da staging’de test et.
  • Veritabanı yedeği al: İlk çalıştırma öncesi mysqldump ile anlık yedek almak seni olası bir felaketten kurtarır.
# Calismadan once hizli yedek
mysqldump -u db_user -p db_name wp_posts wp_postmeta > /backup/pre_cleanup_$(date +%Y%m%d).sql

Sonuç

WordPress ve WooCommerce sitelerinde veri kirliliği sessiz sedasız büyüyen bir sorundur. Bugün 50ms’de yüklenen bir sayfa, 6 ay sonra kontrolsüz büyümüş bir veritabanıyla 3 saniyeye çıkabilir ve hosting sağlayıcından “veritabanı boyut uyarısı” maili almak can sıkıcıdır.

functions.php içine yerleştireceğin birkaç düzine satır kod bu problemi tamamen ortadan kaldırır. Önemli olan kurulumu bir kez doğru yapmak: önce gerçek sunucu cron’u ile WP-Cron’u entegre et, sonra her görevi kendi hook’una bağla, LIMIT ve prepare() kullanmayı alışkanlık haline getir, ve her görevi error_log ile izlenebilir yap.

Bu yaklaşımı uygulayan sitelerde ortalama veritabanı büyüme hızının önemli ölçüde düştüğünü ve sorgu sürelerinin kısaldığını bizzat gözlemledim. Taslak temizliği için 90 gün iyi bir başlangıç noktasıdır ama kendi iş süreçlerine göre bu değeri ayarlamaktan çekinme. Kod hazır, tek yapman gereken functions.php dosyasını açmak.

Bir yanıt yazın

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