Medya Boyutunu Sınırlayan WordPress Eklentisi Yapımı
Sunucu tarafında medya boyutlarını kontrol etmek, özellikle paylaşımlı hosting ortamlarında veya çok kullanıcılı WordPress kurulumlarında sıkça ihmal edilen ama sonradan ciddi sorunlara yol açan bir konudur. Kullanıcılar 50MB’lık video dosyası yüklemeye çalışırken sunucu belleği dolup taşıyor, disk kotaları aşılıyor ve bir anda tüm site çökmüş oluyor. Bu tür senaryoları yaşadıktan sonra “neden baştan önlem almadım” sorusuyla yüzleşmek zorunda kalıyorsunuz.
Bu yazıda, WordPress’in kendi medya yükleme mekanizmasına müdahale eden, dosya boyutunu sunucu tarafında sınırlayan ve yöneticiye özelleştirme imkanı sunan bir eklenti geliştireceğiz. Sadece “işte kod, kopyala yapıştır” yaklaşımıyla değil, her adımı neden yaptığımızı açıklayarak ilerleyeceğiz.
Eklentinin Genel Mimarisi
WordPress medya yükleme süreci birkaç katmandan geçer: tarayıcı tarafı JavaScript kontrolleri, PHP’nin upload_max_filesize ve post_max_size direktifleri ve son olarak WordPress’in kendi filtre/hook sistemi. Bizim eklentimiz bu katmanların en güveniliri olan WordPress hook sistemini kullanacak.
Eklentimiz şu bileşenlerden oluşacak:
- Core sınıf: Boyut limiti kontrolü ve dosya filtreleme
- Admin sayfası: Limitin yapılandırılabileceği ayar ekranı
- JavaScript katmanı: Tarayıcı tarafı ön kontrol (UX için)
- Bildirim sistemi: Yöneticiye ve kullanıcıya anlamlı hata mesajları
Dosya yapısı şu şekilde olacak:
wp-content/plugins/media-size-limiter/
├── media-size-limiter.php # Ana eklenti dosyası
├── includes/
│ ├── class-media-limiter.php # Core sınıf
│ └── class-admin-settings.php # Admin ayarları
├── assets/
│ └── js/
│ └── upload-check.js # Frontend kontrol
└── readme.txt
Ana Eklenti Dosyası
Eklentinin giriş noktası olan media-size-limiter.php dosyasıyla başlayalım. Bu dosya WordPress’e eklentiyi tanıtır ve sınıfları yükler.
<?php
/**
* Plugin Name: Media Size Limiter
* Plugin URI: https://example.com/media-size-limiter
* Description: Medya yüklemelerinde dosya boyutunu sunucu tarafında sınırlar.
* Version: 1.0.0
* Author: Sysadmin Blog
* License: GPL-2.0+
* Text Domain: media-size-limiter
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
define( 'MSL_VERSION', '1.0.0' );
define( 'MSL_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'MSL_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
require_once MSL_PLUGIN_DIR . 'includes/class-media-limiter.php';
require_once MSL_PLUGIN_DIR . 'includes/class-admin-settings.php';
function msl_init() {
$limiter = new MSL_Media_Limiter();
$settings = new MSL_Admin_Settings();
$limiter->init();
$settings->init();
}
add_action( 'plugins_loaded', 'msl_init' );
register_activation_hook( __FILE__, 'msl_activate' );
function msl_activate() {
if ( ! get_option( 'msl_max_size_mb' ) ) {
add_option( 'msl_max_size_mb', 5 );
add_option( 'msl_allowed_types', 'jpg,jpeg,png,gif,pdf' );
add_option( 'msl_notify_admin', '1' );
}
}
register_deactivation_hook( __FILE__, 'msl_deactivate' );
function msl_deactivate() {
// Seçeneği temizlemek istemiyorsak burayı boş bırakıyoruz
// Veriler kalıcı silinsin istiyorsak uninstall.php kullanmalıyız
}
ABSPATH kontrolü güvenlik açısından kritik; bu satır olmadan dosya doğrudan tarayıcıdan çağrıldığında PHP kodunuz açıkta çalışır. plugins_loaded hook’u ise init‘ten önce ateşlenir, bu yüzden çoklu dil desteği için de idealdir.
Core Sınıf: MSL_Media_Limiter
Asıl iş bu sınıfta yapılıyor. WordPress’in wp_handle_upload_prefilter hook’u, dosya diske yazılmadan önce devreye girer; bu da bizim için altın değerinde.
<?php
// includes/class-media-limiter.php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class MSL_Media_Limiter {
private $max_size_bytes;
private $allowed_types;
public function init() {
$max_mb = (int) get_option( 'msl_max_size_mb', 5 );
$this->max_size_bytes = $max_mb * 1024 * 1024;
$types_string = get_option( 'msl_allowed_types', 'jpg,jpeg,png,gif,pdf' );
$this->allowed_types = array_map( 'trim', explode( ',', strtolower( $types_string ) ) );
add_filter( 'wp_handle_upload_prefilter', array( $this, 'check_upload' ) );
add_filter( 'upload_size_limit', array( $this, 'set_upload_limit' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
}
public function check_upload( $file ) {
// Dosya boyutu kontrolü
if ( $file['size'] > $this->max_size_bytes ) {
$max_mb = round( $this->max_size_bytes / 1024 / 1024, 1 );
$file_mb = round( $file['size'] / 1024 / 1024, 1 );
$file['error'] = sprintf(
__( 'Dosya boyutu (%sMB) izin verilen maksimum boyutu (%sMB) aşıyor.', 'media-size-limiter' ),
$file_mb,
$max_mb
);
if ( get_option( 'msl_notify_admin' ) === '1' ) {
$this->notify_admin( $file );
}
return $file;
}
// Dosya tipi kontrolü
$ext = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
if ( ! in_array( $ext, $this->allowed_types, true ) ) {
$file['error'] = sprintf(
__( '"%s" uzantısı bu sitede desteklenmiyor. İzin verilen uzantılar: %s', 'media-size-limiter' ),
$ext,
implode( ', ', $this->allowed_types )
);
return $file;
}
return $file;
}
public function set_upload_limit( $limit ) {
return min( $limit, $this->max_size_bytes );
}
private function notify_admin( $file ) {
$admin_email = get_option( 'admin_email' );
$site_name = get_bloginfo( 'name' );
$current_user = wp_get_current_user();
$subject = sprintf( '[%s] Büyük dosya yükleme girişimi engellendi', $site_name );
$message = sprintf(
"Merhaba,nn%s sitesinde aşağıdaki yükleme denemesi engellendi:nn" .
"Kullanıcı: %s (%s)n" .
"Dosya Adı: %sn" .
"Dosya Boyutu: %sMBn" .
"Zaman: %snn" .
"Bu e-posta otomatik olarak gönderilmiştir.",
$site_name,
$current_user->display_name,
$current_user->user_email,
sanitize_text_field( $file['name'] ),
round( $file['size'] / 1024 / 1024, 1 ),
current_time( 'mysql' )
);
wp_mail( $admin_email, $subject, $message );
}
public function enqueue_scripts( $hook ) {
if ( 'post.php' === $hook || 'post-new.php' === $hook || 'upload.php' === $hook ) {
wp_enqueue_script(
'msl-upload-check',
MSL_PLUGIN_URL . 'assets/js/upload-check.js',
array( 'jquery' ),
MSL_VERSION,
true
);
wp_localize_script(
'msl-upload-check',
'mslConfig',
array(
'maxSizeMB' => (int) get_option( 'msl_max_size_mb', 5 ),
'allowedTypes' => $this->allowed_types,
'errorSize' => __( 'Dosya çok büyük!', 'media-size-limiter' ),
'errorType' => __( 'Desteklenmeyen dosya türü!', 'media-size-limiter' ),
)
);
}
}
}
Burada dikkat edilmesi gereken bir nokta var: wp_handle_upload_prefilter filtresi, $file['error'] alanına herhangi bir değer atandığında yüklemeyi durduruyor. Bu davranış WordPress çekirdeğine gömülü, yani biz sadece bu mekanizmayı kullanıyoruz.
Admin Ayarları Sayfası
Limiti sabit kodlamak yerine yöneticinin yapılandırabilmesi için bir ayar sayfası ekleyelim. WordPress Settings API’yi kullanmak, güvenli ve standart bir yol sunar.
<?php
// includes/class-admin-settings.php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class MSL_Admin_Settings {
public function init() {
add_action( 'admin_menu', array( $this, 'add_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
add_filter( 'plugin_action_links_' . plugin_basename( MSL_PLUGIN_DIR . 'media-size-limiter.php' ),
array( $this, 'add_settings_link' ) );
}
public function add_menu() {
add_options_page(
__( 'Medya Boyut Sınırı', 'media-size-limiter' ),
__( 'Medya Limiti', 'media-size-limiter' ),
'manage_options',
'media-size-limiter',
array( $this, 'render_page' )
);
}
public function register_settings() {
register_setting( 'msl_settings_group', 'msl_max_size_mb', array(
'type' => 'integer',
'sanitize_callback' => array( $this, 'sanitize_max_size' ),
'default' => 5,
) );
register_setting( 'msl_settings_group', 'msl_allowed_types', array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_allowed_types' ),
'default' => 'jpg,jpeg,png,gif,pdf',
) );
register_setting( 'msl_settings_group', 'msl_notify_admin', array(
'type' => 'string',
'default' => '1',
) );
add_settings_section(
'msl_main_section',
__( 'Genel Ayarlar', 'media-size-limiter' ),
null,
'media-size-limiter'
);
add_settings_field(
'msl_max_size_mb',
__( 'Maksimum Dosya Boyutu (MB)', 'media-size-limiter' ),
array( $this, 'render_max_size_field' ),
'media-size-limiter',
'msl_main_section'
);
add_settings_field(
'msl_allowed_types',
__( 'İzin Verilen Uzantılar', 'media-size-limiter' ),
array( $this, 'render_allowed_types_field' ),
'media-size-limiter',
'msl_main_section'
);
add_settings_field(
'msl_notify_admin',
__( 'Engelleme Bildirimi', 'media-size-limiter' ),
array( $this, 'render_notify_field' ),
'media-size-limiter',
'msl_main_section'
);
}
public function sanitize_max_size( $value ) {
$value = absint( $value );
if ( $value < 1 ) { $value = 1; }
if ( $value > 100 ) { $value = 100; }
return $value;
}
public function sanitize_allowed_types( $value ) {
$types = array_map( 'trim', explode( ',', strtolower( $value ) ) );
$clean = array_filter( $types, function( $t ) {
return preg_match( '/^[a-z0-9]+$/', $t );
} );
return implode( ',', $clean );
}
public function render_max_size_field() {
$val = (int) get_option( 'msl_max_size_mb', 5 );
echo '<input type="number" name="msl_max_size_mb" value="' . esc_attr( $val ) . '" min="1" max="100" /> MB';
echo '<p class="description">' . __( 'PHP yapılandırmanızdaki upload_max_filesize değerinden yüksek ayarlayamazsınız.', 'media-size-limiter' ) . '</p>';
}
public function render_allowed_types_field() {
$val = get_option( 'msl_allowed_types', 'jpg,jpeg,png,gif,pdf' );
echo '<input type="text" name="msl_allowed_types" value="' . esc_attr( $val ) . '" size="50" />';
echo '<p class="description">' . __( 'Virgülle ayrılmış uzantılar. Örnek: jpg,png,pdf', 'media-size-limiter' ) . '</p>';
}
public function render_notify_field() {
$val = get_option( 'msl_notify_admin', '1' );
echo '<input type="checkbox" name="msl_notify_admin" value="1" ' . checked( '1', $val, false ) . ' />';
echo '<label>' . __( 'Engelleme girişimlerinde yöneticiye e-posta gönder', 'media-size-limiter' ) . '</label>';
}
public function render_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields( 'msl_settings_group' );
do_settings_sections( 'media-size-limiter' );
submit_button();
?>
</form>
</div>
<?php
}
public function add_settings_link( $links ) {
$settings_link = '<a href="' . admin_url( 'options-general.php?page=media-size-limiter' ) . '">'
. __( 'Ayarlar', 'media-size-limiter' ) . '</a>';
array_unshift( $links, $settings_link );
return $links;
}
}
Sanitize fonksiyonlarına dikkat edin. msl_max_size_mb için absint kullanıyoruz ve mantıklı bir aralıkla sınırlıyoruz. msl_allowed_types için ise yalnızca alfanümerik karakterlere izin veriyoruz; birisi uzantı alanına ../../etc/passwd yazmaya çalışırsa temizlenip atılıyor.
JavaScript ile Tarayıcı Tarafı Kontrol
Sunucu tarafı kontrolümüz zaten güvenli, ama kullanıcı 50MB dosya seçip “Yükle” düğmesine bastıktan sonra sunucudan hata mesajı almak sinir bozucu. Bunu önceden yakalayalım.
// assets/js/upload-check.js
(function($) {
'use strict';
var maxBytes = mslConfig.maxSizeMB * 1024 * 1024;
function checkFile(file) {
var ext = file.name.split('.').pop().toLowerCase();
if (file.size > maxBytes) {
var fileMB = (file.size / 1024 / 1024).toFixed(1);
var maxMB = mslConfig.maxSizeMB;
return mslConfig.errorSize + ' (' + fileMB + 'MB / Max: ' + maxMB + 'MB)';
}
if (mslConfig.allowedTypes.indexOf(ext) === -1) {
return mslConfig.errorType + ' (.' + ext + ')';
}
return null;
}
// Klasik medya yükleyici için
$(document).on('change', 'input[type="file"]', function() {
var files = this.files;
if (!files || !files.length) { return; }
var errors = [];
for (var i = 0; i < files.length; i++) {
var err = checkFile(files[i]);
if (err) { errors.push(files[i].name + ': ' + err); }
}
if (errors.length > 0) {
alert(errors.join('n'));
$(this).val('');
}
});
// Plupload (WordPress medya kütüphanesi) için
$(document).on('plupload-init', function(e, uploader) {
uploader.bind('FilesAdded', function(up, files) {
var toRemove = [];
$.each(files, function(i, file) {
var mockFile = { name: file.name, size: file.size };
var err = checkFile(mockFile);
if (err) {
toRemove.push(file);
console.warn('[MSL] Dosya engellendi:', file.name, err);
}
});
if (toRemove.length > 0) {
$.each(toRemove, function(i, file) {
up.removeFile(file);
});
alert(mslConfig.errorSize + 'n' + toRemove.length + ' dosya kaldırıldı.');
}
});
});
})(jQuery);
WooCommerce Ürün Görselleri için Özel Kural
Eğer WooCommerce kullanıyorsanız, ürün görselleri için daha sıkı bir limit koymak mantıklı olabilir. Bu durumu ana sınıfa ekleyebileceğiniz bir genişletme olarak düşünebilirsiniz.
// Bu kodu class-media-limiter.php içindeki init() metoduna ekleyin
add_filter( 'woocommerce_product_image_upload_error', array( $this, 'check_woo_product_image' ), 10, 2 );
// Ve sınıfa bu metodu ekleyin:
public function check_woo_product_image( $error, $file ) {
$woo_limit_bytes = 2 * 1024 * 1024; // WooCommerce görselleri için 2MB
if ( $file['size'] > $woo_limit_bytes ) {
return sprintf(
__( 'Ürün görseli 2MB'dan büyük olamaz. Yüklediğiniz dosya: %sMB', 'media-size-limiter' ),
round( $file['size'] / 1024 / 1024, 1 )
);
}
return $error;
}
Alternatif olarak, context bilgisini wp_handle_upload_prefilter üzerinden almak isterseniz $_REQUEST içindeki action parametresine bakabilirsiniz; WooCommerce yüklemeleri genellikle farklı bir action değeriyle gelir.
Eklentiyi Test Etmek
Geliştirme tamamlandıktan sonra birkaç senaryo test edilmeli. WP-CLI ile hızlıca test ortamı oluşturabilirsiniz:
# Test için geçici bir WordPress kurulumu oluştur
wp core download --path=/tmp/wp-test
cd /tmp/wp-test
wp config create --dbname=wp_test --dbuser=root --dbpass=root
wp core install --url=localhost/wp-test --title="Test" --admin_user=admin --admin_password=admin [email protected]
# Eklentiyi kopyala ve aktif et
cp -r /path/to/media-size-limiter wp-content/plugins/
wp plugin activate media-size-limiter
# Limit ayarını yap (5MB)
wp option update msl_max_size_mb 5
wp option update msl_allowed_types "jpg,jpeg,png,gif,pdf"
# 6MB'lık test dosyası oluştur
dd if=/dev/urandom bs=1M count=6 | base64 > /tmp/test-large.txt
# WP-CLI ile yükleme testi (başarısız olmalı)
wp media import /tmp/test-large.txt --title="Test Large File"
Logları izlerken şunu kontrol edin: hata mesajının doğru döndüğünü ve dosyanın wp-content/uploads altında oluşturulmadığını görmelisiniz.
Gerçek Dünya Senaryosu: Çok Kullanıcılı Blog Platformu
Birden fazla editörün içerik yüklediği bir haber portalı düşünelim. Editörler zaman zaman ham fotoğraf makinesi çıktılarını (20-30MB JPEG) doğrudan sisteme atmaya çalışıyordu. Bu durum:
- Sunucunun PHP bellek limitini zorluyordu
wp-content/uploadsdizininde gereksiz depolama tüketiyordu- Sayfa yükleme performansını dolaylı olarak etkiliyordu
Bu eklentiyi 8MB limit ve yalnızca görsel uzantılarına kısıtlanmış şekilde devreye aldıktan sonra editörler sistemi zorlamadan önce görseli optimize etmeye başladı. Yönetici bildirim e-postası da “kim ne yüklemeye çalışıyor” görünürlüğünü sağladı.
Burada önemli bir nokta: PHP’nin upload_max_filesize direktifini de uyumlu hale getirmek gerekiyor. WordPress hook’u PHP katmanından sonra çalıştığı için PHP seviyesinde dosya reddedilirse WordPress hook’a bile ulaşamaz. Bunu wp-config.php veya .htaccess ile ayarlayın:
# .htaccess içine (Apache)
php_value upload_max_filesize 10M
php_value post_max_size 12M
# Veya wp-config.php içine
@ini_set( 'upload_max_filesize', '10M' );
@ini_set( 'post_max_size', '12M' );
PHP limitini WordPress limitinizden biraz yüksek tutmak iyi bir pratik; böylece PHP dosyayı kabul ediyor, asıl sınırlamayı ise kendi kodumuza bırakıyoruz.
Sonuç
Bu eklenti, WordPress’in hook sistemini kullanarak medya boyutunu sunucu tarafında kontrol etmenin temiz ve genişletilebilir bir örneğini sunuyor. Kritik noktalara bakacak olursak:
- Güvenlik önce gelir: Sanitize callback’leri atlamamak,
ABSPATHkontrolü yapmak vecurrent_user_canile izin kontrolleri her zaman zorunlu - Çift katman kontrol: JavaScript tarafı UX için, PHP tarafı güvenlik için; ikisinin birlikte çalışması ideal kullanıcı deneyimini sağlıyor
- Anlamlı hata mesajları: “Hata oluştu” yerine “6.2MB dosya, 5MB limitini aşıyor” mesajı editörün sorunu anlayıp çözmesini sağlıyor
- Settings API kullanımı: Sabit kodlanmış değerler yerine veritabanından okunan konfigürasyon, eklentiyi farklı ortamlarda yeniden kullanılabilir kılıyor
Eklentiyi buradan alıp kendi ihtiyaçlarınıza göre genişletebilirsiniz: rol bazlı limitler (editöre 5MB, yöneticiye 20MB), dosya türüne göre farklı boyut kuralları veya yükleme geçmişi loglama gibi özellikler eklemek oldukça doğal bir sonraki adım olur.
