🎣 فصل ۹: Hooks و Filters – قلب توسعه وردپرس
Hooks (قلابها) سیستم رویدادمحور وردپرسه که بهت اجازه میده بدون دستکاری کد اصلی، رفتار وردپرس رو تغییر بدی. یاد گرفتن این مفهوم تو رو از یه کاربر معمولی به یه توسعهدهنده وردپرس تبدیل میکنه.
Hooks (قلابها) سیستم رویدادمحور وردپرسه که بهت اجازه میده بدون دستکاری کد اصلی، رفتار وردپرس رو تغییر بدی. یاد گرفتن این مفهوم تو رو از یه کاربر معمولی به یه توسعهدهنده وردپرس تبدیل میکنه.
💡 مفهوم Hook
تصور کن وردپرس یه قطار بزرگه که از ایستگاههای مختلف رد میشه. توی هر ایستگاه میتونی سوار بشی و کاری بکنی – یا حتی محموله رو تغییر بدی.
دو نوع Hook
- Action: «وقتی این اتفاق افتاد، این کار رو هم بکن» (مثل فایر کردن یه ایمیل)
- Filter: «وقتی این داده داره رد میشه، میخوام تغییرش بدم» (مثل کم کردن قیمت محصول)
Action = اجرا کردن یه کار. Filter = تغییر دادن یه مقدار.
⚡ Action ها
وردپرس در نقاط کلیدی اجرا، یه action رو «فایر» میکنه. تو میتونی یه تابع به اون action متصل کنی تا اجرا بشه.
ساختار
add_action(
'hook_name', // اسم هوک
'callback_func', // تابع callback
10, // priority (پیشفرض ۱۰)
1 // تعداد argument (پیشفرض ۱)
);
مثال: ارسال ایمیل بعد از انتشار نوشته
function notify_admin_new_post( $post_id, $post ) {
// فقط برای پستهای منتشر شده
if ( $post->post_status !== 'publish' ) {
return;
}
$admin_email = get_option( 'admin_email' );
$subject = 'نوشته جدید: ' . $post->post_title;
$message = "نوشته جدیدی منتشر شد:nn";
$message .= 'عنوان: ' . $post->post_title . "n";
$message .= 'لینک: ' . get_permalink( $post_id );
wp_mail( $admin_email, $subject, $message );
}
add_action( 'wp_insert_post', 'notify_admin_new_post', 10, 2 );
Action های مهم چرخه عمر
| Action | کِی فایر میشه؟ |
|---|---|
plugins_loaded |
بعد از لود شدن همه افزونهها |
after_setup_theme |
بعد از لود قالب |
init |
وردپرس کاملاً راهاندازی شده |
wp_loaded |
کوئری اصلی هنوز اجرا نشده |
wp |
کوئری اصلی اجرا شد |
wp_head |
توی <head> سایت |
wp_body_open |
بلافاصله بعد از <body> |
wp_footer |
قبل از </body> |
do_action – فراخوانی یه action
وقتی توسعهدهندهای، میتونی action سفارشی بسازی که بقیه بهش متصل بشن:
// در یه افزونه
function my_special_event() {
// یه کار اصلی...
// به دیگران اجازه بده اینجا چیزی اضافه کنن
do_action( 'my_plugin_event_happened', $data );
}
// حالا یکی دیگه میتونه:
add_action( 'my_plugin_event_happened', function( $data ) {
// واکنش به این event
} );
🔧 Filter ها
Filter یه مقدار رو میگیره، تغییر میده و برمیگردونه. همیشه باید مقدار رو return کنی.
ساختار
add_filter(
'filter_name',
'callback_func',
10, // priority
1 // تعداد argument
);
function callback_func( $value ) {
// تغییر $value
return $value; // ⚠️ حتماً return کن
}
مثال ۱: تغییر متن نوشته
// اضافه کردن متن به انتهای هر نوشته
function add_signature_to_content( $content ) {
// فقط در single
if ( is_single() && in_the_loop() && is_main_query() ) {
$signature = '<p class="signature">
نوشتهشده توسط محمدعلی ناظری |
<a href="https://icsd.ir">ICSD</a>
</p>';
$content .= $signature;
}
return $content;
}
add_filter( 'the_content', 'add_signature_to_content' );
مثال ۲: تغییر طول چکیده
function custom_excerpt_length( $length ) {
return 30; // ۳۰ کلمه به جای ۵۵
}
add_filter( 'excerpt_length', 'custom_excerpt_length' );
function custom_excerpt_more( $more ) {
return '... <a href="' . get_permalink() . '">ادامه مطلب</a>';
}
add_filter( 'excerpt_more', 'custom_excerpt_more' );
Filter های پرکاربرد
| Filter | چی رو فیلتر میکنه؟ |
|---|---|
the_content |
محتوای نوشته |
the_title |
عنوان نوشته |
the_excerpt |
چکیده |
excerpt_length |
طول چکیده |
body_class |
کلاسهای body |
post_class |
کلاسهای نوشته |
wp_title |
عنوان صفحه |
upload_mimes |
فرمتهای آپلود مجاز |
login_redirect |
ریدایرکت بعد از ورود |
comment_text |
متن دیدگاه |
apply_filters – فراخوانی filter
// در افزونهت
$price = 100000;
$price = apply_filters( 'my_plugin_price', $price, $product_id );
echo $price;
// حالا یکی دیگه میتونه قیمت رو تغییر بده:
add_filter( 'my_plugin_price', function( $price, $product_id ) {
if ( get_post_meta( $product_id, '_special', true ) ) {
return $price * 0.9; // ۱۰٪ تخفیف
}
return $price;
}, 10, 2 );
🎚 Priority و Arguments
Priority
عددی که ترتیب اجرا رو مشخص میکنه. عدد کمتر = اول اجرا. پیشفرض ۱۰.
add_action( 'init', 'function_a', 5 ); // اول
add_action( 'init', 'function_b', 10 ); // دوم (پیشفرض)
add_action( 'init', 'function_c', 99 ); // آخر
Arguments
تعداد argument هایی که هوک ارسال میکنه:
// این هوک ۳ argument داره
add_action( 'save_post', 'my_save_handler', 10, 3 );
function my_save_handler( $post_id, $post, $update ) {
// $post_id: شناسه
// $post: آبجکت کامل
// $update: آپدیت یا جدید
}
remove_action و remove_filter
گاهی میخوای یه هوک قبلی رو حذف کنی:
// حذف emoji های وردپرس
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
// حذف فید RSS
remove_action( 'wp_head', 'feed_links', 2 );
// ⚠️ نکته: priority باید دقیقاً همون priority اضافه کردن باشه
⭐ هوکهای پرکاربرد
برای استایل و اسکریپت
function my_enqueue_assets() {
wp_enqueue_style( 'main', get_stylesheet_uri() );
wp_enqueue_script( 'main', '/path/to/script.js' );
}
add_action( 'wp_enqueue_scripts', 'my_enqueue_assets' );
// برای ادمین
add_action( 'admin_enqueue_scripts', 'my_admin_assets' );
// برای صفحه ورود
add_action( 'login_enqueue_scripts', 'my_login_assets' );
برای ذخیره/بروزرسانی نوشته
function my_save_meta( $post_id ) {
// جلوگیری از autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
// بررسی nonce
if ( ! isset( $_POST['my_nonce'] ) ||
! wp_verify_nonce( $_POST['my_nonce'], 'my_action' ) ) {
return;
}
// بررسی دسترسی
if ( ! current_user_can( 'edit_post', $post_id ) ) return;
// ذخیره
if ( isset( $_POST['custom_field'] ) ) {
update_post_meta( $post_id, '_custom_field',
sanitize_text_field( $_POST['custom_field'] ) );
}
}
add_action( 'save_post', 'my_save_meta' );
// فقط برای CPT خاص
add_action( 'save_post_icsd_product', 'my_save_meta' );
برای ورود/خروج کاربر
// بعد از ورود
add_action( 'wp_login', function( $user_login, $user ) {
// لاگ ورود
error_log( 'User logged in: ' . $user_login );
}, 10, 2 );
// بعد از خروج
add_action( 'wp_logout', function( $user_id ) {
// پاک کردن کش کاربر
} );
// ریدایرکت بعد از ورود
add_filter( 'login_redirect', function( $redirect, $request, $user ) {
if ( ! is_wp_error( $user ) && in_array( 'subscriber', $user->roles ) ) {
return home_url(); // مشترکین به خانه برن
}
return $redirect;
}, 10, 3 );
🛠 ساخت هوک سفارشی
وقتی افزونه میسازی، با ساخت هوک سفارشی، به دیگران اجازه میدی افزونهت رو extend کنن:
class My_Donation_Plugin {
public function process_donation( $amount, $donor_email ) {
// قبل از پردازش
do_action( 'my_donation_before_process', $amount, $donor_email );
// فیلتر مبلغ (مثلاً برای کسر کارمزد)
$amount = apply_filters( 'my_donation_amount', $amount );
// پردازش اصلی
$result = $this->send_to_gateway( $amount );
// بعد از پردازش
do_action( 'my_donation_after_process', $result, $amount, $donor_email );
return $result;
}
}
// حالا توسعهدهنده دیگه میتونه:
add_action( 'my_donation_after_process', function( $result, $amount, $email ) {
// ارسال ایمیل تشکر
wp_mail( $email, 'تشکر از کمک شما', "مبلغ {$amount} دریافت شد." );
}, 10, 3 );
این الگو دقیقاً برای افزونهت کاربرد داره! میتونی هوکهایی مثل pdp_before_donation و pdp_after_donation بذاری تا توسعهدهندهها بتونن سیستم تو رو extend کنن.
🎯 مثالهای واقعی
مثال ۱: تغییر طول رمز عبور پیشفرض
add_filter( 'random_password', function( $password, $length ) {
return wp_generate_password( 16, true, false );
}, 10, 2 );
مثال ۲: محدودیت آپلود فایل
add_filter( 'wp_handle_upload_prefilter', function( $file ) {
$max_size = 5 * 1024 * 1024; // ۵ مگابایت
if ( $file['size'] > $max_size ) {
$file['error'] = 'فایل خیلی بزرگه! حداکثر ۵ مگابایت';
}
return $file;
} );
مثال ۳: ثبت فعالیت کاربر
class User_Activity_Logger {
public function __construct() {
add_action( 'wp_login', array( $this, 'log_login' ), 10, 2 );
add_action( 'wp_logout', array( $this, 'log_logout' ) );
add_action( 'save_post', array( $this, 'log_post_save' ), 10, 2 );
add_action( 'wp_trash_post', array( $this, 'log_post_trash' ) );
add_action( 'comment_post', array( $this, 'log_comment' ) );
}
private function log( $action, $data = array() ) {
$user = wp_get_current_user();
$log_entry = array(
'time' => current_time( 'mysql' ),
'user_id' => $user->ID ?? 0,
'user' => $user->user_login ?? 'guest',
'action' => $action,
'data' => $data,
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
);
$logs = get_option( 'user_activity_log', array() );
$logs[] = $log_entry;
// فقط ۱۰۰۰ ورودی آخر
if ( count( $logs ) > 1000 ) {
$logs = array_slice( $logs, -1000 );
}
update_option( 'user_activity_log', $logs );
}
public function log_login( $login, $user ) {
$this->log( 'login', array( 'user' => $login ) );
}
public function log_logout() {
$this->log( 'logout' );
}
public function log_post_save( $post_id, $post ) {
if ( wp_is_post_revision( $post_id ) ) return;
$this->log( 'save_post', array(
'post_id' => $post_id,
'title' => $post->post_title,
) );
}
public function log_post_trash( $post_id ) {
$this->log( 'trash_post', array( 'post_id' => $post_id ) );
}
public function log_comment( $comment_id ) {
$this->log( 'comment', array( 'comment_id' => $comment_id ) );
}
}
new User_Activity_Logger();
📝 خلاصه فصل
- Action برای اجرای کار، Filter برای تغییر مقدار
- Filter حتماً مقدار رو return کنه
- priority پایینتر = اول اجرا
- هوکهای مهم: init، wp_head، wp_footer، save_post
- توی افزونهت hook بذار تا extensible باشه
- برای بررسی هوکهای موجود: developer.wordpress.org/reference/hooks