WordPress Çoklu Site Ağında Özel Fonksiyon Tanımlama
WordPress Multisite kurulumunda her sitenin kendi functions.php dosyası olduğunu biliyorsunuzdur. Ama ya onlarca siteniz varsa? Her birine ayrı ayrı aynı fonksiyonu eklemek hem zaman kaybı hem de bakım kabusu. İşte tam bu noktada multisite ağında merkezi ve özel fonksiyon tanımlama sanatına giriyoruz.
Bu yazıda gerçek dünya senaryoları üzerinden, bir WordPress Multisite ağında nasıl etkili, sürdürülebilir ve güvenli özel fonksiyonlar tanımlayabileceğinizi adım adım göstereceğim.
WordPress Multisite Ağı Mimarisini Anlamak
Fonksiyon yazmaya geçmeden önce, multisite ağının nasıl çalıştığını netleştirmek gerekiyor. WordPress Multisite kurulumunda üç farklı seviye var:
- Network Admin Seviyesi: Tüm ağı yöneten super admin
- Site Admin Seviyesi: Tek bir alt siteyi yöneten site yöneticisi
- Kullanıcı Seviyesi: Normal üyeler
Fonksiyonlarınızı nereye yazdığınız, bu hiyerarşide neyi etkileyeceğinizi doğrudan belirliyor. Bir fonksiyon mu-plugins klasörüne yazıldığında tüm ağı etkilerken, bir alt sitenin functions.php dosyasına yazılan fonksiyon sadece o siteyi etkiler.
Mu-Plugins Klasörü: Ağ Geneli Fonksiyonların Evi
wp-content/mu-plugins/ klasörü, “Must-Use Plugins” anlamına gelir ve bu klasördeki PHP dosyaları otomatik olarak yüklenir, devre dışı bırakılamaz. Multisite için ideal yer burasıdır.
# mu-plugins klasörü yoksa oluşturun
mkdir -p /var/www/html/wp-content/mu-plugins
# Ağ geneli fonksiyon dosyası oluşturun
touch /var/www/html/wp-content/mu-plugins/network-functions.php
# Doğru izinleri ayarlayın
chmod 644 /var/www/html/wp-content/mu-plugins/network-functions.php
chown www-data:www-data /var/www/html/wp-content/mu-plugins/network-functions.php
Temel Yapıyı Kurmak
Her şeyden önce, fonksiyonlarınızı organize edecek bir yapı kurmanız gerekiyor. Tek bir devasa dosya yerine, modüler bir yaklaşım benimseyin.
# Organize bir klasör yapısı oluşturun
mkdir -p /var/www/html/wp-content/mu-plugins/network-core
touch /var/www/html/wp-content/mu-plugins/network-core/user-functions.php
touch /var/www/html/wp-content/mu-plugins/network-core/content-functions.php
touch /var/www/html/wp-content/mu-plugins/network-core/security-functions.php
touch /var/www/html/wp-content/mu-plugins/network-loader.php
Ana yükleyici dosyası şöyle görünmeli:
# network-loader.php içeriğini oluşturun
cat > /var/www/html/wp-content/mu-plugins/network-loader.php << 'EOF'
<?php
/**
* Network Functions Loader
* Tüm ağ geneli fonksiyonları yükler
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$network_modules = array(
'user-functions',
'content-functions',
'security-functions',
);
foreach ( $network_modules as $module ) {
$module_path = __DIR__ . '/network-core/' . $module . '.php';
if ( file_exists( $module_path ) ) {
require_once $module_path;
}
}
EOF
Site ID’ye Göre Koşullu Fonksiyon Çalıştırma
Gerçek hayatta en sık ihtiyaç duyulan şey: “Şu fonksiyon sadece 3. sitede çalışsın.” İşte burada get_current_blog_id() devreye giriyor.
Diyelim ki 5 siteniz var ve sadece e-ticaret sitenizde (site ID: 3) özel bir stok kontrolü fonksiyonu çalışsın:
cat > /var/www/html/wp-content/mu-plugins/network-core/content-functions.php << 'EOF'
<?php
/**
* İçerik ve site özelinde fonksiyonlar
*/
/**
* Belirli bir site ID'si için kontrol yardımcısı
*/
function is_network_site( $site_id ) {
return (int) get_current_blog_id() === (int) $site_id;
}
/**
* Birden fazla site için kontrol
*/
function is_any_network_site( array $site_ids ) {
return in_array( (int) get_current_blog_id(), array_map( 'intval', $site_ids ) );
}
// Sadece site ID 3 (e-ticaret) için stok uyarısı
add_action( 'woocommerce_single_product_summary', 'network_low_stock_notice', 25 );
function network_low_stock_notice() {
if ( ! is_network_site( 3 ) ) {
return;
}
global $product;
if ( $product && $product->get_stock_quantity() < 5 && $product->get_stock_quantity() > 0 ) {
echo '<div class="low-stock-warning">Stokta sadece '
. esc_html( $product->get_stock_quantity() )
. ' adet kaldı!</div>';
}
}
// Site ID 1 ve 2 için özel başlık meta
add_action( 'wp_head', 'network_custom_meta_tags' );
function network_custom_meta_tags() {
if ( ! is_any_network_site( array( 1, 2 ) ) ) {
return;
}
echo '<meta name="network-group" content="primary-sites">' . "n";
}
EOF
Ağ Geneli Kullanıcı Yönetimi Fonksiyonları
Multisite’ın en güçlü özelliklerinden biri, bir kullanıcının ağdaki birden fazla siteye üye olabilmesidir. Bunu yönetmek için özel fonksiyonlar şart.
cat > /var/www/html/wp-content/mu-plugins/network-core/user-functions.php << 'EOF'
<?php
/**
* Ağ geneli kullanıcı yönetimi fonksiyonları
*/
/**
* Kullanıcıyı ağdaki tüm sitelere otomatik ekle
* Senaryo: Kurumsal intranet - yeni çalışan tüm sitelere erişmeli
*/
function network_add_user_to_all_sites( $user_id, $role = 'subscriber' ) {
if ( ! is_multisite() ) {
return false;
}
$sites = get_sites( array(
'number' => 100,
'fields' => 'ids',
) );
foreach ( $sites as $site_id ) {
switch_to_blog( $site_id );
if ( ! is_user_member_of_blog( $user_id, $site_id ) ) {
add_user_to_blog( $site_id, $user_id, $role );
}
restore_current_blog();
}
return true;
}
/**
* Yeni kullanıcı kaydında otomatik ağ üyeliği
* Kurumsal senaryo: HR sistemi entegrasyonu
*/
add_action( 'user_register', 'network_auto_assign_new_user', 10, 1 );
function network_auto_assign_new_user( $user_id ) {
$user = get_userdata( $user_id );
// Şirket e-posta domainiyle kayıt olan kullanıcıları otomatik ekle
$company_domain = '@sirketim.com';
if ( strpos( $user->user_email, $company_domain ) !== false ) {
network_add_user_to_all_sites( $user_id, 'editor' );
// Super admin'e bildirim gönder
$admin_email = get_site_option( 'admin_email' );
wp_mail(
$admin_email,
'Yeni Kurumsal Kullanıcı',
sprintf(
'%s (%s) tüm ağa editor olarak eklendi.',
$user->display_name,
$user->user_email
)
);
}
}
/**
* Kullanıcının ağdaki tüm sitelerden rollerini al
*/
function network_get_user_roles_across_sites( $user_id ) {
$roles = array();
$sites = get_sites( array( 'number' => 100 ) );
foreach ( $sites as $site ) {
switch_to_blog( $site->blog_id );
$user = new WP_User( $user_id, '', $site->blog_id );
if ( ! empty( $user->roles ) ) {
$roles[ $site->blog_id ] = array(
'site_name' => get_bloginfo( 'name' ),
'roles' => $user->roles,
);
}
restore_current_blog();
}
return $roles;
}
EOF
Güvenlik Odaklı Ağ Fonksiyonları
Güvenlik fonksiyonlarını merkezi olarak yönetmek, multisite’ın en büyük avantajlarından biri. Bir güvenlik açığı kapandığında tüm ağı tek yerden koruyorsunuz.
cat > /var/www/html/wp-content/mu-plugins/network-core/security-functions.php << 'EOF'
<?php
/**
* Ağ geneli güvenlik fonksiyonları
*/
/**
* Belirli IP bloklarını tüm ağda engelle
* Senaryo: Sürekli saldırı yapan IP aralıklarını blokla
*/
add_action( 'init', 'network_block_malicious_ips', 1 );
function network_block_malicious_ips() {
// Sadece admin ajax ve login sayfasında uygula
if ( ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) &&
! strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) ) {
return;
}
$blocked_ranges = get_site_option( 'network_blocked_ip_ranges', array() );
if ( empty( $blocked_ranges ) ) {
return;
}
$visitor_ip = $_SERVER['REMOTE_ADDR'];
foreach ( $blocked_ranges as $range ) {
if ( network_ip_in_range( $visitor_ip, $range ) ) {
wp_die(
'Erişim reddedildi.',
'Engellendi',
array( 'response' => 403 )
);
}
}
}
/**
* IP aralığı kontrolü (CIDR notasyonu destekli)
*/
function network_ip_in_range( $ip, $range ) {
if ( strpos( $range, '/' ) === false ) {
return $ip === $range;
}
list( $subnet, $bits ) = explode( '/', $range );
$ip_long = ip2long( $ip );
$subnet_long = ip2long( $subnet );
$mask = -1 << ( 32 - $bits );
$subnet_long &= $mask;
return ( $ip_long & $mask ) === $subnet_long;
}
/**
* Başarısız giriş denemelerini ağ genelinde logla
*/
add_action( 'wp_login_failed', 'network_log_failed_logins' );
function network_log_failed_logins( $username ) {
$log_entry = array(
'time' => current_time( 'mysql' ),
'username' => sanitize_text_field( $username ),
'ip' => $_SERVER['REMOTE_ADDR'],
'site_id' => get_current_blog_id(),
'ua' => substr( $_SERVER['HTTP_USER_AGENT'], 0, 200 ),
);
$logs = get_site_option( 'network_failed_logins', array() );
array_unshift( $logs, $log_entry );
// Son 1000 kaydı tut
$logs = array_slice( $logs, 0, 1000 );
update_site_option( 'network_failed_logins', $logs );
}
/**
* Ağ genelinde dosya düzenlemeyi devre dışı bırak
* Production ortamında kritik güvenlik önlemi
*/
add_action( 'init', 'network_disable_file_editing' );
function network_disable_file_editing() {
if ( ! defined( 'DISALLOW_FILE_EDIT' ) ) {
define( 'DISALLOW_FILE_EDIT', true );
}
}
EOF
WooCommerce Multisite Entegrasyonu
Multisite üzerinde WooCommerce çalıştırıyorsanız, özellikle şu senaryo çok yaygın: Ana sitede ürün kataloğu var, alt siteler bu ürünleri kendi ağırlıklarıyla satıyor. Merkezi stok yönetimi için özel fonksiyonlar yazalım.
cat > /var/www/html/wp-content/mu-plugins/network-core/woocommerce-network.php << 'EOF'
<?php
/**
* WooCommerce Ağ Entegrasyonu
* Merkezi ürün kataloğu - Ana site ID: 1
*/
define( 'NETWORK_MAIN_STORE_ID', 1 );
/**
* Ana sitedeki ürün bilgisini al
* Senaryo: Franchise modeli - merkezi fiyatlandırma
*/
function network_get_master_product_data( $product_sku ) {
switch_to_blog( NETWORK_MAIN_STORE_ID );
$product_id = wc_get_product_id_by_sku( $product_sku );
$data = null;
if ( $product_id ) {
$product = wc_get_product( $product_id );
$data = array(
'id' => $product_id,
'name' => $product->get_name(),
'price' => $product->get_price(),
'stock' => $product->get_stock_quantity(),
'description' => $product->get_short_description(),
'sku' => $product->get_sku(),
);
}
restore_current_blog();
return $data;
}
/**
* Alt sitelerde fiyatı ana siteyle senkronize et
* Cronjob ile tetiklenir
*/
function network_sync_prices_from_master() {
$sites = get_sites( array(
'number' => 100,
'site__not_in' => array( NETWORK_MAIN_STORE_ID ),
) );
$synced_count = 0;
foreach ( $sites as $site ) {
switch_to_blog( $site->blog_id );
if ( ! class_exists( 'WooCommerce' ) ) {
restore_current_blog();
continue;
}
$products = wc_get_products( array(
'limit' => -1,
'status' => 'publish',
) );
foreach ( $products as $product ) {
$sku = $product->get_sku();
$master_data = network_get_master_product_data( $sku );
if ( $master_data ) {
$product->set_price( $master_data['price'] );
$product->set_regular_price( $master_data['price'] );
$product->save();
$synced_count++;
}
}
restore_current_blog();
}
// Senkronizasyon logunu güncelle
update_site_option( 'last_price_sync', array(
'time' => current_time( 'mysql' ),
'count' => $synced_count,
) );
return $synced_count;
}
// Günlük fiyat senkronizasyonu için cron
add_action( 'network_daily_price_sync', 'network_sync_prices_from_master' );
if ( ! wp_next_scheduled( 'network_daily_price_sync' ) ) {
wp_schedule_event( strtotime( 'midnight' ), 'daily', 'network_daily_price_sync' );
}
EOF
Network Admin Paneline Özel Sayfa Ekleme
Tüm bu fonksiyonları yönetmek için Network Admin paneline özel bir yönetim sayfası eklemek hayat kurtarır.
cat >> /var/www/html/wp-content/mu-plugins/network-loader.php << 'EOF'
/**
* Network Admin yönetim sayfası
*/
add_action( 'network_admin_menu', 'network_functions_admin_menu' );
function network_functions_admin_menu() {
add_menu_page(
'Ağ Yönetimi',
'Ağ Fonksiyonları',
'manage_network',
'network-functions-manager',
'network_functions_admin_page',
'dashicons-networking',
30
);
}
function network_functions_admin_page() {
if ( ! current_user_can( 'manage_network' ) ) {
wp_die( 'Bu sayfaya erişim yetkiniz yok.' );
}
// Form işleme
if ( isset( $_POST['network_blocked_ips_nonce'] ) &&
wp_verify_nonce( $_POST['network_blocked_ips_nonce'], 'update_blocked_ips' ) ) {
$raw_ips = sanitize_textarea_field( $_POST['blocked_ip_ranges'] );
$ip_list = array_filter( array_map( 'trim', explode( "n", $raw_ips ) ) );
update_site_option( 'network_blocked_ip_ranges', $ip_list );
echo '<div class="updated"><p>Engelli IP listesi güncellendi.</p></div>';
}
$blocked_ips = get_site_option( 'network_blocked_ip_ranges', array() );
$failed_logins = get_site_option( 'network_failed_logins', array() );
$last_sync = get_site_option( 'last_price_sync', null );
?>
<div class="wrap">
<h1>Ağ Fonksiyonları Yönetimi</h1>
<h2>Engelli IP Aralıkları</h2>
<form method="post">
<?php wp_nonce_field( 'update_blocked_ips', 'network_blocked_ips_nonce' ); ?>
<textarea name="blocked_ip_ranges" rows="10" cols="50"
placeholder="Her satıra bir IP veya CIDR aralığı (örn: 192.168.1.0/24)"
><?php echo esc_textarea( implode( "n", $blocked_ips ) ); ?></textarea>
<br>
<input type="submit" class="button button-primary" value="Kaydet">
</form>
<h2>Son Başarısız Giriş Denemeleri (Son 10)</h2>
<ul>
<?php foreach ( array_slice( $failed_logins, 0, 10 ) as $log ) : ?>
<li>
<strong><?php echo esc_html( $log['time'] ); ?></strong> -
Kullanıcı: <?php echo esc_html( $log['username'] ); ?> |
IP: <?php echo esc_html( $log['ip'] ); ?> |
Site: <?php echo esc_html( $log['site_id'] ); ?>
</li>
<?php endforeach; ?>
</ul>
<?php if ( $last_sync ) : ?>
<h2>Son Fiyat Senkronizasyonu</h2>
<p>
Zaman: <?php echo esc_html( $last_sync['time'] ); ?> |
Güncellenen ürün: <?php echo esc_html( $last_sync['count'] ); ?>
</p>
<?php endif; ?>
</div>
<?php
}
EOF
Dosya İzinleri ve Güvenlik Kontrolleri
Tüm bu dosyaları yerleştirdikten sonra izinleri doğru ayarlamak kritik:
# Tüm mu-plugins dosyaları için doğru izinler
find /var/www/html/wp-content/mu-plugins -type f -name "*.php" -exec chmod 644 {} ;
find /var/www/html/wp-content/mu-plugins -type d -exec chmod 755 {} ;
# Sahipliği web sunucusu kullanıcısına ver
chown -R www-data:www-data /var/www/html/wp-content/mu-plugins/
# Yazma izinlerini sadece gerekli dizinlere ver
# mu-plugins klasörünün root'una yazma izni verme!
chmod 755 /var/www/html/wp-content/mu-plugins/
# Syntax hatası kontrolü yap
php -l /var/www/html/wp-content/mu-plugins/network-loader.php
php -l /var/www/html/wp-content/mu-plugins/network-core/user-functions.php
php -l /var/www/html/wp-content/mu-plugins/network-core/security-functions.php
php -l /var/www/html/wp-content/mu-plugins/network-core/content-functions.php
# WP-CLI ile fonksiyonların yüklenip yüklenmediğini test et
wp --url=https://siteniz.com eval 'echo function_exists("network_add_user_to_all_sites") ? "OK" : "FAIL";'
Yaygın Hatalar ve Çözümleri
Multisite fonksiyon geliştirirken sıkça karşılaşılan sorunlar:
- switch_to_blog() sonrası restore_current_blog() unutmak: Her
switch_to_blog()çağrısından sonra mutlakarestore_current_blog()kullanın. Aksi halde sonraki database sorguları yanlış blog context’inde çalışır. - get_option() yerine get_site_option() kullanmamak: Ağ geneli ayarlar için
get_site_option()veupdate_site_option()kullanın.get_option()sadece aktif sitenin ayarlarını okur. - current_user_can() kontrolleri eksik bırakmak: Network admin fonksiyonlarında mutlaka
manage_networkyetki kontrolü yapın. - WP_User nesnesini yanlış blog context’inde oluşturmak:
WP_User($user_id, '', $blog_id)şeklinde blog ID’yi açıkça belirtin. - Cron olaylarını her sayfa yüklemesinde tekrar kaydetmeye çalışmak: Her zaman
wp_next_scheduled()ile kontrol edin, zaten kayıtlıysa tekrar eklemeyin. - Büyük ağlarda get_sites() limit parametresini unutmak: Varsayılan limit 100’dür, daha büyük ağlarda
'number' => -1veya sayfalama kullanın.
Sonuç
WordPress Multisite ağında merkezi fonksiyon yönetimi, başlangıçta karmaşık görünse de doğru yapılandırıldığında inanılmaz zaman ve emek tasarrufu sağlıyor. mu-plugins klasörünü modüler bir yapıyla kullanmak, switch_to_blog() ve restore_current_blog() ikilisine dikkat etmek, ağ geneli ayarlar için site_option fonksiyonlarını tercih etmek, temel başarı kriterleriniz bunlar.
Özellikle 10’un üzerinde siteye sahip bir ağ yönetiyorsanız, bu yazıdaki yaklaşımı benimsemek ilk haftadan itibaren fark yaratacaktır. Güvenlik güncellemelerini tek bir dosyadan yapabilmek, kullanıcı yönetimini merkezi hale getirmek ve WooCommerce fiyatlarını otomatik senkronize etmek, multisite’ın sunduğu en güçlü avantajlar.
Kendi ağınızda bu yapıyı kurarken önce test ortamında deneyin, php -l ile syntax kontrolü yapın ve her önemli değişiklik öncesinde veritabanı yedeği almayı ihmal etmeyin.
