در سال‌ها کار روی پروژه‌های وردپرسی، از افزونه‌های کوچک تا پلتفرم‌های سازمانی، یک الگو را بارها دیده‌ام: توسعه‌دهندگانی که هوک‌های وردپرس را به‌عنوان «قابلیت جانبی» می‌بینند، کدی می‌نویسند که ماه اول کار می‌کند و ماه سوم با هر آپدیت می‌شکند. در مقابل، توسعه‌دهندگانی که هوک‌ها را به‌عنوان پایه معماری درک می‌کنند، کدی می‌نویسند که سال‌ها پایدار می‌ماند. تفاوت، در شناخت syntax نیست؛ در تفکر معماری و انتخاب درست بین اکشن و فیلتر، ترتیب اجرا و نحوه حذف است. این مقاله، همان روشی است که در پروژه‌های واقعی برای استفاده صحیح از هوک‌های وردپرس اجرا می‌کنم. اگر با مفاهیم پایه آشنا نیستید، پیش از ادامه افزونه وردپرس چیست، ساختار هسته وردپرس و نحوه استفاده از توابع وردپرس را بخوانید.

هوک در وردپرس دقیقاً چیست؟

هوک، مکانیزم رسمی وردپرس برای اتصال کد شما به هسته است، بدون دست‌کاری مستقیم هسته. وردپرس در نقاط مختلف اجرای خود، «رویدادهایی» را اعلام می‌کند. کد شما می‌تواند در آن نقاط، تابعی را اجرا کند یا داده‌ای را تغییر دهد. این مکانیزم، دلیل اصلی انعطاف وردپرس است: هزاران افزونه و قالب می‌توانند بدون تعارض با هسته و با یکدیگر، در نقاط مشخصی وارد شوند. جایگاه این مفهوم در معماری کلی وردپرس را در ساختار هسته وردپرس توضیح داده‌ام.

هوک‌ها در دو نوع اصلی وجود دارند که در بخش بعد تفاوتشان را مرور می‌کنم. اما نکته مهم‌تر از تفاوت، درک این است که هوک‌ها «قابلیت جانبی» نیستند؛ پایه معماری هر افزونه و قالب حرفه‌ای‌اند. اگر با مفهوم کلی افزونه آشنا نیستید، افزونه وردپرس چیست را ببینید.

هوک‌ها، تفاوت بین کدی که هسته وردپرس را دست‌کاری می‌کند و کدی که با هسته هم‌راستا کار می‌کند. اولی در آپدیت می‌شکند، دومی سال‌ها می‌ماند.

اکشن در برابر فیلتر: تفاوت واقعی

دو نوع هوک در وردپرس:

  • اکشن (Action): در لحظه مشخصی از اجرای وردپرس، کد شما اجرا می‌شود. اکشن، داده‌ای را «تغییر نمی‌دهد»؛ فقط کاری را «انجام می‌دهد». مثال: ذخیره یک فایل لاگ، ارسال ایمیل، ثبت یک رکورد. هوک‌های پرکاربرد اکشن: init، wp_enqueue_scripts، save_post، wp_footer.
  • فیلتر (Filter): در لحظه مشخصی از اجرای وردپرس، داده‌ای به کد شما پاس داده می‌شود، شما آن را تغییر می‌دهید و برمی‌گردانید. فیلتر، داده‌ای را «تغییر می‌دهد». مثال: اضافه کردن متن به محتوای نوشته، تغییر عنوان صفحه، دست‌کاری منو. هوک‌های پرکاربرد فیلتر: the_content، the_title، wp_nav_menu_items، excerpt_length.

اشتباه رایج: استفاده از اکشن برای تغییر داده، یا استفاده از فیلتر برای انجام کار. این اشتباه، هم کد را غیرقابل نگهداری می‌کند و هم باعث باگ‌های عجیب در ترتیب اجرا می‌شود. تفاوت این دو، در syntax نیست؛ در فلسفه معماری است. یک قاعده ساده که در تیم‌های خودم آموزش می‌دهم: اگر می‌خواهید «کاری انجام شود»، اکشن؛ اگر می‌خواهید «داده تغییر کند»، فیلتر.

استفاده صحیح از add_action

الگوی پایه add_action در وردپرس:

add_action( $hook_name, $callback, $priority, $accepted_args );

add_action( 'init', 'myplugin_register_post_type' );
add_action( 'wp_enqueue_scripts', 'myplugin_enqueue_assets', 10, 1 );

سه نکته در استفاده صحیح:

  • انتخاب هوک درست: هوک init برای ثبت نوع‌نوشته، تاکسونومی و شورت‌کد؛ هوک wp_enqueue_scripts برای asset front؛ هوک admin_menu برای منوی پیشخوان؛ هوک save_post برای ذخیره داده نوشته. انتخاب هوک اشتباه، باعث می‌شود کد شما در لحظه‌ای اجرا شود که داده مورد نیاز آماده نیست. مثال رایج: ثبت نوع‌نوشته روی هوک after_setup_theme به‌جای init.
  • استفاده از پیشوند یکتا در callback: نام تابع callback باید پیشوند اختصاصی داشته باشد تا با افزونه‌های دیگر تعارض نکند. این یکی از معیارهای استاندارد کدنویسی وردپرس است.
  • استفاده از متد کلاس به‌جای تابع سراسری: در افزونه‌های حرفه‌ای، callback یک متد کلاس است، نه تابع سراسری. الگوی دقیق در توسعه افزونه وردپرس از صفر.

یک نکته تخصصی که در پروژه‌های بزرگ دیده‌ام: استفاده از closure (تابع ناشناس) در add_action راحت است اما امکان حذف بعدی را از بین می‌برد. اگر ممکن است کسی بعداً بخواهد این هوک را حذف کند — مثلاً یک افزونه دیگر — از callback نام‌دار (تابع یا متد کلاس) استفاده کنید. این تصمیم کوچک، در انعطاف پروژه در ماه ششم اثر محسوس دارد.

استفاده صحیح از add_filter

الگوی پایه add_filter:

add_filter( $hook_name, $callback, $priority, $accepted_args );

add_filter( 'the_content', 'myplugin_append_signature' );
add_filter( 'excerpt_length', 'myplugin_change_excerpt_length', 20, 1 );

سه نکته در استفاده صحیح:

  • بازگرداندن مقدار: تابع callback فیلتر، باید مقدار را «برگرداند»، نه چاپ کند. اشتباه رایج: استفاده از echo به‌جای return. این اشتباه، در اکثر موارد باعث می‌شود تغییر شما اعمال نشود یا در جای اشتباه ظاهر شود.
  • پذیرش پارامترها: اگر فیلتر سه پارامتر دارد، باید پارامتر سوم را در accepted_args اعلام کنید. نبود این پارامتر، یعنی callback شما فقط پارامتر اول را می‌بیند. مثال: فیلتر the_content با یک پارامتر، فیلتر wp_nav_menu_items با دو پارامتر.
  • پرهیز از تغییر پیش‌فرض‌های سراسری: در فیلترها، همیشه فرض کنید که افزونه‌های دیگر هم روی همان فیلتر نشسته‌اند. کد شما باید فقط داده خود را تغییر دهد، نه اینکه فرض کند تنها کد روی آن فیلتر است. این قاعده، از تعارض‌های پیچیده در سازگاری افزونه‌ها جلوگیری می‌کند.

اولویت اجرا و ترتیب هوک‌ها

سومین پارامتر add_action و add_filter، priority است. پیش‌فرض ۱۰ است. هرچه priority کمتر باشد، تابع زودتر اجرا می‌شود. اهمیت این پارامتر در سه سناریو:

  • اتصال به هوک‌هایی که در فاز تنظیمات اجرا می‌شوند: هوک after_setup_theme قبل از init اجرا می‌شود. اگر کد شما به تنظیمات قالب وابسته است، باید در همان after_setup_theme و با priority درست اجرا شود.
  • اجرای callback بعد از callback افزونه دیگر: اگر می‌خواهید فیلتر شما بعد از یک افزونه خاص اجرا شود، priority بالاتری از آن بدهید (مثلاً ۲۰ یا ۳۰). این کار در پروژه‌هایی که افزونه‌ای روی فیلتر the_content قرار دارد، بسیار کاربردی است.
  • فاز bootstrap افزونه: در bootstrap افزونه، معمولاً از priority پیش‌فرض استفاده می‌کنیم، اما برای اتصال به هوک‌های پایه‌ای مثل plugins_loaded، ممکن است priority پایین‌تر لازم باشد.

یک نکته مهم که در پروژه‌های تیمی زیاد دیده‌ام: نبود مستندسازی priority در کد. اگر priority شما ۲۰ یا ۳۰ است، دلیلش را در کامنت بنویسید. سه ماه بعد، خودتان هم نمی‌دانید چرا ۲۰ گذاشته‌اید. این یک نمونه ساده از اصول کدنویسی تمیز در سطح جزئیات است.

priority، ترتیب اجرای سرنوشت پروژه شماست. بی‌دلیل عدد عوض نکنید، و اگر عوض کردید، دلیلش را در کامنت بنویسید.

حذف هوک‌ها به روش درست

حذف هوک، در دو سناریو لازم می‌شود: لغو یک قابلیت افزونه دیگر، یا تغییر رفتار قالب والد در چایلد تم. الگوی پایه:

remove_action( $hook_name, $callback, $priority );
remove_filter( $hook_name, $callback, $priority );

remove_action( 'wp_footer', 'myplugin_add_footer_signature', 10 );

سه نکته مهم:

  • priority باید مطابق باشد: اگر هوک با priority ۱۰ ثبت شده باشد و شما با priority پیش‌فرض ۱۰ حذف کنید، حذف می‌شود. اما اگر priority اصلی ۲۰ بوده و شما ۱۰ بدهید، حذف نمی‌شود. اشتباه رایج: فراموش کردن priority در remove_action.
  • ترتیب زمانی حذف: حذف باید بعد از ثبت انجام شود. اگر می‌خواهید هوک افزونه دیگری را حذف کنید، باید کد حذف شما بعد از bootstrap آن افزونه اجرا شود. این کار معمولاً با priority بالاتر روی هوک init یا wp_loaded انجام می‌شود.
  • عدم امکان حذف closure: اگر هوک با تابع ناشناس (closure) ثبت شده، قابل حذف نیست. این محدودیت، دلیل مهمی است که در افزونه‌های حرفه‌ای callbackها را به‌صورت متد کلاس یا تابع نام‌دار تعریف می‌کنیم.

در چایلد تم، حذف هوک‌های والد یکی از رایج‌ترین کارهاست. اگر با این مفهوم آشنایی ندارید، قالب چایلد چیست و توسعه با چایلد تم را مرور کنید. چایلد تم دقیقاً برای همین نوع سفارشی‌سازی ساخته شده که والد را دست‌نخورده نگه می‌دارد.

ساخت هوک سفارشی

در پروژه‌های بزرگ، گاهی نیاز داریم که خودمان هم هوک بسازیم تا کد ما قابل توسعه باشد. الگوی پایه:

// ساخت اکشن سفارشی
do_action( 'myplugin_after_order_created', $order_id, $user_id );

// استفاده از اکشن سفارشی
add_action( 'myplugin_after_order_created', 'myplugin_send_notification', 10, 2 );

// ساخت فیلتر سفارشی
$final_price = apply_filters( 'myplugin_final_price', $base_price, $user_id );

// استفاده از فیلتر سفارشی
add_filter( 'myplugin_final_price', 'myplugin_apply_discount', 10, 2 );

سه نکته در ساخت هوک سفارشی:

  • پیشوند یکتا در نام هوک: نام هوک باید پیشوند اختصاصی داشته باشد. مثال: myplugin_after_order_created نه after_order_created.
  • مستندسازی هوک: در بالای هر do_action یا apply_filters، با PHPDoc توضیح دهید که این هوک چه کاری انجام می‌دهد، چه پارامترهایی دارد و چه چیزی برمی‌گرداند. الگوی استاندارد این مستندسازی، در ساختار استاندارد کدنویسی وردپرس آمده است.
  • قابلیت توسعه توسط افزونه‌های دیگر: ساخت هوک سفارشی، افزونه شما را به یک «پلتفرم» تبدیل می‌کند که افزونه‌های دیگر می‌توانند به آن وصل شوند. این الگو در ووکامرس، لرن‌دش و سایر افزونه‌های بزرگ استاندارد است.

هوک در قالب یا افزونه؟

یکی از پرتکرارترین سوال‌ها در پروژه‌های وردپرسی: هوک را در قالب بنویسم یا افزونه؟ سه سناریو:

  • هوک‌های ظاهری (front-end): اگر هوک فقط روی نمایش اثر دارد — مثلاً افزودن یک بخش به فوتر یا تغییر عنوان — می‌تواند در چایلد تم باشد. مثال: wp_footer، the_content، excerpt_length.
  • هوک‌های منطقی (business logic): اگر هوک منطق کسب‌وکار دارد — مثلاً ثبت نوع‌نوشته، ذخیره داده، ارسال ایمیل — باید در افزونه باشد. اگر این هوک‌ها در قالب باشند، روز تغییر قالب، منطق از دست می‌رود. تفصیل این تفکیک را در مراحل ساخت افزونه اختصاصی و مراحل ساخت قالب اختصاصی آورده‌ام.
  • هوک‌های ساختاری (theme setup): هوک‌هایی که ساختار قالب را تعریف می‌کنند — after_setup_theme، widgets_init — در چایلد تم قرار می‌گیرند. جایگاه دقیق این هوک‌ها را در ساختار فایل‌های قالب استاندارد آورده‌ام.

یک قاعده عملی در پروژه‌های خودم: ۸۰٪ هوک‌ها در افزونه، ۲۰٪ در چایلد تم. اگر در پروژه‌ای نسبت برعکس شد، احتمالاً منطق کسب‌وکار در قالب نشسته که در روز تغییر قالب به بحران تبدیل می‌شود. برای درک عمیق‌تر این تفکیک، توسعه با چایلد تم را مرور کنید.

دیباگ هوک‌ها

وقتی هوکی کار نمی‌کند یا تعارض ایجاد می‌کند، سه ابزار در پروژه‌های خودم استفاده می‌کنم:

  • افزونه Query Monitor: تمام هوک‌های اجرا شده در هر صفحه را نشان می‌دهد، با ترتیب، priority و callbackها. این ابزار، اولین قدم در تشخیص تعارض بین دو افزونه روی یک هوک است.
  • error_log و WP_DEBUG: در هوک مشکوک، error_log( 'reached' ) بگذارید تا ببینید کد اجرا می‌شود یا نه. مسیر کامل در تست و دیباگ پروژه‌های وردپرس.
  • حذف دسته‌ای هوک‌ها: اگر تعارض بین دو افزونه است، یکی را غیرفعال کنید و ببینید مشکل حل می‌شود یا نه. مسیر کامل در شناسایی افزونه مشکل‌ساز و بررسی سازگاری قالب و افزونه آمده است.

یک نکته تخصصی: در پروژه‌های بزرگ که چند افزونه روی یک هوک کار می‌کنند، ترتیب priority در دیباگ حیاتی است. اگرچه Query Monitor این ترتیب را نشان می‌دهد، اما درک چرایی آن ترتیب، تفاوت بین دیباگ سریع و دیباگ کند است. اگر می‌خواهید درک عمیق‌تری از این بحث داشته باشید، اصول کدنویسی تمیز در وردپرس را مرور کنید.

جدول چک‌لیست استفاده از هوک

جمع‌بندی چک‌لیست استفاده صحیح از هوک‌ها:

موضوعقاعده کلیدینشانه اشتباه
انتخاب اکشن یا فیلترانجام کار = اکشن، تغییر داده = فیلتراستفاده از فیلتر برای ارسال ایمیل
callbackتابع نام‌دار با پیشوند یکتاclosure که قابل حذف نیست
priorityفقط با دلیل عوض کنید و مستندسازی کنیدpriority بدون کامنت
removepriority مطابق با ثبتفراموش کردن priority در remove
محل قرارگیریمنطق در افزونه، ظاهر در چایلد تمثبت نوع‌نوشته در قالب
هوک سفارشیپیشوند یکتا، PHPDoc کاملنام هوک عمومی بدون prefix
دیباگQuery Monitor + error_logحدس زدن ترتیب اجرا

اشتباهات رایج در استفاده از هوک‌ها

در اشتباهات رایج توسعه وردپرس فهرست کامل را نوشته‌ام؛ اما شش مورد که در استفاده از هوک‌ها بیشتر می‌بینم:

  • استفاده از هوک اشتباه: ثبت نوع‌نوشته روی after_setup_theme به‌جای init. این اشتباه، در نیمی از پروژه‌هایی که بررسی کرده‌ام، وجود داشت.
  • نبود return در فیلتر: callback فیلتر با echo به‌جای return. تغییر شما اعمال نمی‌شود یا در جای اشتباه ظاهر می‌شود.
  • priority بدون دلیل: عوض کردن priority بدون درک دلیل. اگر نمی‌دانید چرا ۲۰ گذاشته‌اید، احتمالاً نیازی نیست.
  • حذف هوک با priority اشتباه: remove_action با priority متفاوت از add_action، حذف نمی‌کند و شما فکر می‌کنید حذف شده. ساعتها دیباگ در این مرحله تلف می‌شود.
  • استفاده از closure در هوک‌هایی که بعداً باید حذف شوند: closure قابل حذف نیست. اگر افزونه دیگری بخواهد کد شما را غیرفعال کند، نمی‌تواند.
  • نبود مستندسازی هوک سفارشی: سه ماه بعد، کسی نمی‌داند هوک شما چه پارامترهایی می‌گیرد و چه چیزی برمی‌گرداند. الگوی مستندسازی در ساختار استاندارد کدنویسی وردپرس آمده است.

یک اشتباه کم‌تکرار اما گران‌قیمت: فراموش کردن فراخوانی do_action یا apply_filters در پروژه‌های تیمی. اگر دو توسعه‌دهنده روی یک بخش کار می‌کنند و یکی از آن‌ها هوک سفارشی ثبت می‌کند اما دیگری نمی‌داند، کد دوم اجرا نمی‌شود. راه‌حل: هوک‌های سفارشی را در مستندات پروژه و در فایل readme ثبت کنید.

دید مهندسی

از منظر مهندسی، هوک‌ها یک پیاده‌سازی از الگوی Publish-Subscribe در سطح یک CMS هستند. سه لایه را در پروژه‌های حرفه‌ای همیشه مرور می‌کنم. لایه اول، معماری مبتنی بر رویداد: استفاده از هوک‌ها فقط برای «وصل کردن کد» نیست؛ برای «طراحی معماری رویدادمحور» است. اگر افزونه شما یک قابلیت قابل توسعه می‌سازد — مثلاً یک سیستم امتیازدهی — باید برای هر نقطه کلیدی (ایجاد امتیاز، تغییر امتیاز، حذف امتیاز) هوک سفارشی بگذارید تا افزونه‌های دیگر بدون دست‌کاری کد شما بتوانند وصل شوند. این الگو، در ووکامرس و لرن‌دش استاندارد است. تفصیل این نوع معماری را در توسعه افزونه از صفر آورده‌ام. لایه دوم، مدیریت priority به‌عنوان قرارداد: در پروژه‌های بزرگ، priority باید به‌عنوان یک «قرارداد بین‌افزونه‌ای» مدیریت شود. اگر افزونه شما روی فیلتر the_content با priority ۱۰ نشسته، افزونه دیگری که priority ۲۰ دارد، می‌تواند تغییر شما را ببیند. این ترتیب، باید در مستندات پروژه ثبت شود. الگوی مستندسازی در ساختاربندی پروژه وردپرس آمده است. لایه سوم، تست‌پذیری هوک‌ها: هوک‌ها، تست‌پذیر نیستند اگر منطق مستقیماً در callback باشد. الگوی حرفه‌ای، جداسازی logic از callback است: callback فقط داده را می‌گیرد و به یک تابع یا متد منطقی پاس می‌دهد. این الگو، تست با PHPUnit را ساده می‌کند. مسیر کامل تست را در تست و دیباگ پروژه‌های وردپرس آورده‌ام.

یک نکته تکمیلی برای تیم‌های فنی: در پروژه‌های بزرگ، ثبت هوک‌ها را در یک نقطه واحد (Registry Pattern) انجام دهید، نه پراکنده در فایل‌های مختلف. این الگو، مدیریت ترتیب اجرا را ساده می‌کند و در روز دیباگ، تفاوت بین ساعت‌ها کار و دقیقه‌ها کار است. تفصیل این الگو در اصول کدنویسی تمیز و ساختار استاندارد کدنویسی آمده است. یک تجربه مستقیم از خودم در این زمینه: در یکی از پروژه‌های فروشگاهی، هفت افزونه اختصاصی روی یک سایت نصب بود و هرکدام هوک‌هایشان را در فایل اصلی ثبت می‌کردند. هنگام debug، پیدا کردن اینکه کدام افزونه روی init با priority چه عددی نشسته، یک روز کامل وقت گرفت. بعد از بازآرایی همه روی Registry Pattern، همان دیباگ در ۱۵ دقیقه انجام می‌شد — تفاوتی که در یک سال، به دهها ساعت کار ذخیره شده تبدیل شد.

جمع‌بندی

استفاده صحیح از هوک‌های وردپرس، در چهار تصمیم کلیدی خلاصه می‌شود: انتخاب اکشن یا فیلتر بر اساس ماهیت کار، استفاده از callback نام‌دار با پیشوند یکتا، مدیریت priority با دلیل و مستندسازی، و قرار دادن هوک در لایه درست (افزونه برای منطق، چایلد تم برای ظاهر). سه اصل در پایان تاکید می‌کنم: اول، هوک را به‌عنوان پایه معماری ببینید، نه قابلیت جانبی. دوم، priority را با دلیل عوض کنید و دلیلش را در کامنت بنویسید. سوم، از closure در هوک‌هایی که ممکن است بعداً حذف شوند، پرهیز کنید.

اگر همین امروز در حال کار روی یک افزونه یا قالب هستید، پیشنهاد عملی من سه گام است: ابتدا کد فعلی خود را بازبینی کنید که آیا هوک‌ها در لایه درست (افزونه یا چایلد تم) قرار دارند؛ سپس هر priority غیرپیش‌فرض را مستندسازی کنید؛ و در پایان، افزونه Query Monitor را نصب کنید و ببینید که آیا ترتیب اجرای هوک‌ها با انتظار شما مطابقت دارد یا نه. اگر در هر مرحله‌ای گیر کردید یا تجربه‌ای از یک تعارض هوک دارید، در دیدگاه‌ها بنویسید. تجربه شما از استفاده درست یا نادرست از هوک‌ها، برای توسعه‌دهنده بعدی که در همین نقطه ایستاده، ارزشمندترین راهنماست. 🔗