Aktivasyon ve Deaktivasyon Hook: WordPress Kurulum Yönetimi
Bir WordPress eklentisi geliştirirken en çok gözden kaçan ama en kritik parçalardan biri aktivasyon ve deaktivasyon hook’larıdır. Yıllarca eklenti geliştirdikten sonra şunu net olarak söyleyebilirim: Bu hook’ları doğru kullanmayan eklentiler ya kullanıcı veri tabanını kirletiyor, ya silme sonrası iz bırakıyor, ya da kurulum sırasında gerekli tabloları oluşturmayı unutuyor. Bu yazıda bu mekanizmayı baştan sona ele alacağız.
Aktivasyon ve Deaktivasyon Hook’ları Nedir?
WordPress, bir eklenti aktive edildiğinde veya deaktive edildiğinde belirli fonksiyonları tetikler. Bu fonksiyonlar sayesinde eklentinizin düzgün çalışması için gerekli altyapıyı kurabilir, eklenti devre dışı bırakıldığında ise geçici verileri temizleyebilirsiniz.
İki temel hook vardır:
- register_activation_hook: Eklenti aktive edildiğinde çalışır
- register_deactivation_hook: Eklenti deaktive edildiğinde çalışır
Bir de silme işlemi için register_uninstall_hook veya uninstall.php dosyası vardır, onu da ele alacağız.
Bu hook’ların kritik bir özelliği var: Normal WordPress hook sistemiyle (add_action, add_filter) çalışmıyorlar. Doğrudan ana eklenti dosyasına, yani eklentinin __FILE__ sabitine bağlanmaları gerekiyor. Bu detayı kaçırırsanız hook’larınız hiç çalışmaz.
Temel Kullanım Yapısı
Önce iskelet yapıya bakalım. Ana eklenti dosyanız (genellikle my-plugin/my-plugin.php) şu şekilde organize olmalı:
<?php
/**
* Plugin Name: Benim Eklentim
* Description: Örnek eklenti
* Version: 1.0.0
* Author: Ahmet Yılmaz
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Aktivasyon hook'u
register_activation_hook( __FILE__, 'benim_eklentim_activate' );
// Deaktivasyon hook'u
register_deactivation_hook( __FILE__, 'benim_eklentim_deactivate' );
function benim_eklentim_activate() {
// Aktivasyon işlemleri
}
function benim_eklentim_deactivate() {
// Deaktivasyon işlemleri
}
Basit görünüyor, değil mi? Ama şeytan ayrıntıda gizli. Özellikle büyük eklentilerde bu fonksiyonların içi çok karmaşık bir hale gelebiliyor.
Aktivasyon Hook’u: Ne Yapmalı?
Aktivasyon hook’u genellikle şu işler için kullanılır:
- Özel veritabanı tabloları oluşturma
- Varsayılan seçenekleri kaydetme
- Gerekli dosya/dizin yapısını oluşturma
- Zamanlanmış görevleri (cron) planlama
- Kapasiteleri (capabilities) ve rolleri tanımlama
Özel Veritabanı Tablosu Oluşturma
Bu en yaygın kullanım senaryosu. $wpdb->get_charset_collate() ve dbDelta() fonksiyonlarını mutlaka kullanın:
function benim_eklentim_activate() {
global $wpdb;
$tablo_adi = $wpdb->prefix . 'eklenti_loglar';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $tablo_adi (
id bigint(20) NOT NULL AUTO_INCREMENT,
kullanici_id bigint(20) NOT NULL,
eylem varchar(100) NOT NULL,
tarih datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id),
KEY kullanici_id (kullanici_id)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
// Tablo versiyonunu kaydet
add_option( 'benim_eklentim_db_version', '1.0' );
}
dbDelta() fonksiyonu hakkında önemli bir not: Bu fonksiyon tablo yoksa oluşturur, varsa günceller. Ama SQL yazım kurallarına çok duyarlıdır. Sütun tanımlarından sonra virgül kullanımı, büyük-küçük harf duyarlılığı gibi konulara dikkat edin. Yanlış formatta yazılmış SQL, dbDelta() tarafından sessizce görmezden gelinebilir.
Varsayılan Seçenekleri Kaydetme
add_option kullanın, update_option değil. Neden? Çünkü add_option sadece değer yoksa ekler, zaten varsa değiştirmez. Bu sayede kullanıcının önceki ayarlarını ezmemiş olursunuz:
function benim_eklentim_activate() {
// Varsayılan seçenekler
$varsayilan_ayarlar = array(
'aktif' => true,
'log_seviyesi' => 'warning',
'max_kayit' => 1000,
'email_bildirimi' => get_option( 'admin_email' ),
);
add_option( 'benim_eklentim_ayarlar', $varsayilan_ayarlar );
add_option( 'benim_eklentim_kurulum_tarihi', current_time( 'mysql' ) );
// Cron görevi planla
if ( ! wp_next_scheduled( 'benim_eklentim_gunluk_temizlik' ) ) {
wp_schedule_event( time(), 'daily', 'benim_eklentim_gunluk_temizlik' );
}
}
Flush Rewrite Rules
Özel post type veya taxonomy kaydeden eklentiler için aktivasyon sonunda rewrite kurallarını yenilemeniz şart. Yoksa özel URL’leriniz 404 verecek:
function benim_eklentim_activate() {
// Önce custom post type'ı kaydet
benim_eklentim_post_type_kaydet();
// Sonra rewrite kurallarını yenile
flush_rewrite_rules();
}
Deaktivasyon Hook’u: Ne Yapmalı?
Deaktivasyon, silme değildir. Kullanıcı eklentiyi geçici olarak kapatmış olabilir. Bu yüzden deaktivasyon hook’unda veritabanı tablolarını veya ayarları silmemelisiniz. Bunun için uninstall mekanizması var.
Deaktivasyon için uygun işlemler:
- Zamanlanmış cron görevlerini iptal etme
- Geçici verileri (transients) temizleme
- Aktif oturumları veya kilitli kaynakları serbest bırakma
function benim_eklentim_deactivate() {
// Cron görevini iptal et
$timestamp = wp_next_scheduled( 'benim_eklentim_gunluk_temizlik' );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, 'benim_eklentim_gunluk_temizlik' );
}
// Tüm zamanlanmış görevleri temizle (toplu)
wp_clear_scheduled_hook( 'benim_eklentim_gunluk_temizlik' );
wp_clear_scheduled_hook( 'benim_eklentim_haftalik_rapor' );
// Geçici verileri temizle
delete_transient( 'benim_eklentim_cache' );
delete_transient( 'benim_eklentim_api_yanit' );
// Rewrite kurallarını yenile
flush_rewrite_rules();
}
Uninstall: Kalıcı Temizlik
Şimdi gelelim çoğu eklentinin ihmal ettiği kısma. Kullanıcı eklentiyi sildiğinde tüm verilerin temizlenmesi için iki yöntem var.
Birinci yöntem: register_uninstall_hook
register_uninstall_hook( __FILE__, 'benim_eklentim_uninstall' );
function benim_eklentim_uninstall() {
global $wpdb;
// Seçenekleri sil
delete_option( 'benim_eklentim_ayarlar' );
delete_option( 'benim_eklentim_db_version' );
delete_option( 'benim_eklentim_kurulum_tarihi' );
// Özel tabloyu sil
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}eklenti_loglar" );
// Tüm kullanıcı meta verilerini temizle
$wpdb->delete(
$wpdb->usermeta,
array( 'meta_key' => 'benim_eklentim_tercihler' )
);
}
İkinci yöntem: uninstall.php dosyası (önerilen)
Ana eklenti dizinine uninstall.php adında bir dosya koyun. WordPress bu dosyayı otomatik olarak bulur ve eklenti silindiğinde çalıştırır. Bu yöntem daha güvenli çünkü eklenti kodu yüklenmeden önce çalışıyor:
<?php
// uninstall.php
// WordPress'in bu dosyayı doğrudan çağırdığından emin ol
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
global $wpdb;
// Çoklu site desteği
if ( is_multisite() ) {
$bloglar = get_sites( array( 'fields' => 'ids' ) );
foreach ( $bloglar as $blog_id ) {
switch_to_blog( $blog_id );
benim_eklentim_verileri_temizle();
restore_current_blog();
}
} else {
benim_eklentim_verileri_temizle();
}
function benim_eklentim_verileri_temizle() {
global $wpdb;
delete_option( 'benim_eklentim_ayarlar' );
delete_option( 'benim_eklentim_db_version' );
$wpdb->query(
"DROP TABLE IF EXISTS {$wpdb->prefix}eklenti_loglar"
);
}
Nesne Yönelimli Yaklaşım
Modern eklenti geliştirmede hook’ları sınıf içinde kullanmak çok daha düzenli bir yapı sağlar. Ama burada kritik bir tuzak var:
<?php
// YANLIŞ KULLANIM - Bu çalışmaz!
class BenimEklentim {
public function __construct() {
register_activation_hook( __FILE__, array( $this, 'activate' ) );
}
public function activate() {
// Bu fonksiyon ÇALIŞMAZ çünkü __FILE__ yanlış dosyayı gösteriyor
}
}
Doğru kullanım şöyle olmalı. Hook kaydını ana eklenti dosyasından yapın, sınıf içinden değil:
<?php
// my-plugin.php (ana dosya)
require_once plugin_dir_path( __FILE__ ) . 'includes/class-benim-eklentim.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-activator.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-deactivator.php';
register_activation_hook( __FILE__, array( 'Benim_Eklentim_Activator', 'activate' ) );
register_deactivation_hook( __FILE__, array( 'Benim_Eklentim_Deactivator', 'deactivate' ) );
// includes/class-activator.php
class Benim_Eklentim_Activator {
public static function activate() {
global $wpdb;
$tablo_adi = $wpdb->prefix . 'eklenti_loglar';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $tablo_adi (
id bigint(20) NOT NULL AUTO_INCREMENT,
mesaj text NOT NULL,
seviye varchar(20) NOT NULL DEFAULT 'info',
olusturma_tarihi datetime NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
update_option( 'benim_eklentim_version', BENIM_EKLENTIM_VERSION );
}
}
// includes/class-deactivator.php
class Benim_Eklentim_Deactivator {
public static function deactivate() {
wp_clear_scheduled_hook( 'benim_eklentim_cron' );
flush_rewrite_rules();
}
}
Veritabanı Güncelleme Senaryosu
Eklentinizin yeni sürümünde veritabanı şemasını değiştirmeniz gerektiğinde ne yapacaksınız? Bu gerçek hayatta çok sık karşılaşılan bir senaryo. Bunun için versiyon kontrolü yapın:
function benim_eklentim_veritabani_guncelle() {
$mevcut_versiyon = get_option( 'benim_eklentim_db_version', '0' );
$hedef_versiyon = '2.0';
if ( version_compare( $mevcut_versiyon, $hedef_versiyon, '>=' ) ) {
return; // Güncelleme gerekmiyor
}
global $wpdb;
$tablo_adi = $wpdb->prefix . 'eklenti_loglar';
// 1.0'dan 2.0'a geçiş: yeni sütun ekle
if ( version_compare( $mevcut_versiyon, '2.0', '<' ) ) {
$wpdb->query(
"ALTER TABLE $tablo_adi
ADD COLUMN kullanici_adi varchar(60) NOT NULL DEFAULT ''
AFTER kullanici_id"
);
}
update_option( 'benim_eklentim_db_version', $hedef_versiyon );
}
// Bu fonksiyonu plugins_loaded hook'una bağla, aktivasyon hook'una değil
add_action( 'plugins_loaded', 'benim_eklentim_veritabani_guncelle' );
Bu pattern çok önemli. Veritabanı güncellemelerini aktivasyon hook’una değil, plugins_loaded hook’una bağlayın. Çünkü aktivasyon hook’u sadece eklenti aktive edildiğinde çalışır, sürüm güncellemelerinde çalışmaz.
Güvenlik Kontrolleri
Hook’lar içinde de güvenlik kontrolü yapmayı unutmayın. Özellikle doğrudan çağrılma ihtimaline karşı:
function benim_eklentim_activate() {
// Sadece admin kullanıcılar aktive edebilir
if ( ! current_user_can( 'activate_plugins' ) ) {
wp_die(
__( 'Bu işlem için yetkiniz yok.', 'benim-eklentim' ),
__( 'Yetki Hatası', 'benim-eklentim' ),
array( 'back_link' => true )
);
}
// PHP versiyon kontrolü
if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
wp_die(
sprintf(
__( 'Bu eklenti PHP 7.4 veya üstü gerektirir. Mevcut PHP versiyonunuz: %s', 'benim-eklentim' ),
PHP_VERSION
)
);
}
// WordPress versiyon kontrolü
global $wp_version;
if ( version_compare( $wp_version, '5.8', '<' ) ) {
wp_die(
__( 'Bu eklenti WordPress 5.8 veya üstü gerektirir.', 'benim-eklentim' )
);
}
// Bağımlı eklenti kontrolü
if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
wp_die(
__( 'Bu eklenti WooCommerce gerektirir. Lütfen önce WooCommerce'i aktive edin.', 'benim-eklentim' )
);
}
// Asıl aktivasyon işlemleri...
benim_eklentim_tablolari_olustur();
benim_eklentim_varsayilanlari_kaydet();
flush_rewrite_rules();
}
Multisite Desteği
WordPress Multisite kurulumlarında aktivasyon hook’u ağ genelinde mi yoksa sadece aktif sitede mi çalışacak? Bunu kontrol etmeniz lazım:
function benim_eklentim_activate( $network_wide ) {
if ( is_multisite() && $network_wide ) {
// Ağdaki tüm sitelerde aktivasyon yap
$siteler = get_sites( array(
'fields' => 'ids',
'number' => 0, // Tümünü al
) );
foreach ( $siteler as $site_id ) {
switch_to_blog( $site_id );
benim_eklentim_tek_site_aktivasyon();
restore_current_blog();
}
} else {
// Sadece mevcut sitede aktivasyon yap
benim_eklentim_tek_site_aktivasyon();
}
}
function benim_eklentim_tek_site_aktivasyon() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$tablo_adi = $wpdb->prefix . 'eklenti_loglar';
$sql = "CREATE TABLE $tablo_adi (
id bigint(20) NOT NULL AUTO_INCREMENT,
mesaj text NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
add_option( 'benim_eklentim_aktif', true );
}
register_activation_hook( __FILE__, 'benim_eklentim_activate' );
$network_wide parametresinin register_activation_hook tarafından otomatik olarak geçirildiğine dikkat edin. Bunu fonksiyon imzasına eklemeniz yeterli.
Sık Yapılan Hatalar
Yıllarca kod incelemesi yaparak gördüğüm en yaygın hatalar şunlar:
- Yanlış dosya referansı: Hook’u alt dizindeki bir dosyadan kaydetmek.
__FILE__o dosyayı gösterir, ana eklenti dosyasını değil. - dbDelta yerine direkt SQL:
CREATE TABLE IF NOT EXISTSkullanmak güncelleme senaryolarında işe yaramaz. - Deaktivasyon’da veri silmek: Kullanıcı eklentiyi yeniden aktive ettiğinde tüm ayarları sıfırdan girmek zorunda kalır.
- Flush rewrite rules eksikliği: Custom post type’lar 404 verir.
- Cron temizliği yapmamak: Deaktive edilen eklentinin cron’ları çalışmaya devam eder, hata logları dolar.
- Multisite desteğini atlamak: Ağ genelinde aktive edilen eklenti sadece ana sitede çalışır.
Sonuç
Aktivasyon ve deaktivasyon hook’ları, bir eklentinin “iyi vatandaş” olup olmadığını belirleyen en önemli mekanizmalar. Bunları doğru uygulamak kullanıcı deneyimini doğrudan etkiliyor. Eklentiniz aktive edildiğinde her şey yerli yerinde olmalı, deaktive edildiğinde sistem temiz kalmalı, silindiğinde ise hiçbir iz bırakmamalı.
Bu yazıda ele aldığımız konuları özetleyecek olursak:
register_activation_hookher zaman ana eklenti dosyasından kayıt edilmeli- Aktivasyonda tablo oluşturma, varsayılan ayarlar ve cron planlaması yapılmalı
- Deaktivasyonda sadece cron ve geçici veriler temizlenmeli, kalıcı veriler dokunulmamalı
- Kalıcı temizlik için
uninstall.phpdosyası kullanılmalı - Veritabanı şema güncellemeleri için versiyon kontrolü ve
plugins_loadedhook’u tercih edilmeli - OOP yapıda hook’ları ana dosyadan kaydetmek şart
- Multisite kurulumları için
$network_wideparametresi kontrol edilmeli
Bu temeli sağlam attığınızda eklentiniz hem daha güvenilir çalışır hem de WordPress Eklenti Dizini gereksinimlerini karşılar. Geri kalanı zaten sizin iş mantığınız.
