چرا حلقه بینهایت در وردپرس شما را زمین میزند؟ راهنمای doing_action
تابع doing_action برای تشخیص در حال اجرا بودن یک اکشن در وردپرس؛ بررسی پارامترها، جلوگیری از حلقه بینهایت و اشتباهات رایج در افزونهنویسی.
چرا تشخیص اکشن در حال اجرا حیاتی است؟
در توسعه افزونه وردپرس، گاهی یک callback که به یک اکشن متصل است، در داخل خود اکشن دیگری را فراخوانی میکند که آن اکشن هم به callbackی متصل است که دوباره اکشن اول را فراخوانی میکند. این ساختار بازگشتی میتواند به حلقه بینهایت منجر شود که سرور را از پا درمیآورد. تابعdoing_action() دقیقاً برای جلوگیری از همین فاجعه ساخته شده است. این تابع به شما میگوید که آیا یک اکشن مشخص در همین لحظه در حال اجرا است یا نه. با این اطلاعات میتوانید جلوی بازگشت بینهایت را بگیرید.
تابع doing_action چیست؟
تابعdoing_action() یک تابع هسته وردپرس است که در فایل wp-includes/plugin.php تعریف شده است. این تابع بررسی میکند که آیا یک اکشن مشخص در همین لحظه در حال اجرا است یا نه.
خروجی این تابع یک مقدار بولی است: true اگر اکشن مشخص در حال اجرا باشد و false در غیر این صورت.
نکته مهم این است که این تابع در داخل تابع callback آن اکشن هم مقدار true برمیگرداند. بنابراین میتوانید تشخیص دهید که آیا در حال اجرای بازگشتی هستید یا نه.
امضای تابع و پارامترها
امضای این تابع بهشکل زیر است:function doing_action( $hook_name = '' ) {
return doing_filter( $hook_name );
}
function doing_filter( $hook_name = '' ) {
global $wp_current_filter;
if ( empty( $hook_name ) ) {
return ! empty( $wp_current_filter );
}
return in_array( $hook_name, $wp_current_filter, true );
}
پارامتر ورودی، نام اکشن مورد بررسی است. اگر خالی باشد، بررسی میکند که آیا هر اکشنی در حال اجرا است یا نه.
نکته مهم: این تابع روی پشته $wp_current_filter کار میکند. اگر اکشن مشخص در این پشته وجود داشته باشد، مقدار true برمیگرداند.
سازوکار داخلی تابع
تابعdoing_action() در واقع doing_filter() را فراخوانی میکند. خود doing_filter() از متغیر جهانی $wp_current_filter استفاده میکند که یک پشته از نام هوکهای در حال اجرا است.
وقتی یک اکشن فراخوانی میشود، نام آن به پشته اضافه میشود. پس از پایان اجرا، از پشته حذف میشود. اگر در داخل یک اکشن، اکشن دیگری فراخوانی شود، نام اکشن دوم هم به پشته اضافه میشود و در پشته، هر دو نام وجود دارند.
این ساختار امکان تشخیص بازگشتهای تودرتو را فراهم میکند.
تفاوت با did_action و current_action
سه تابع مرتبط با اکشنها وجود دارند که معمولاً با هم اشتباه گرفته میشوند: -doing_action( $hook ): بررسی میکند که آیا اکشن مشخص در همین لحظه در حال اجرا است
- did_action( $hook ): تعداد اجرای یک اکشن مشخص را برمیگرداند
- current_action(): نام اکشن در حال اجرا را برمیگرداند
تفاوت کلیدی: doing_action حالنگر است و بولی برمیگرداند، did_action گذشتهنگر است و عدد برمیگرداند، current_action توصیفی است و نام برمیگرداند.
راهنمای این توابع در صفحه did_action و صفحه current_action آمده است.
کاربردهای عملی: جلوگیری از حلقه
یکی از رایجترین کاربردها، جلوگیری از حلقه بینهایت است:add_action( 'save_post', 'myplugin_update_related' );
function myplugin_update_related( $post_id ) {
if ( doing_action( 'save_post' ) ) {
return; // ما در حال پردازش save_post هستیم، بازگشت بینهایت را متوقف کن
}
// بهروزرسانی نوشتههای مرتبط که خودشان save_post را فراخوانی میکنند
$related_posts = myplugin_get_related_posts( $post_id );
foreach ( $related_posts as $related_id ) {
wp_update_post( array(
'ID' => $related_id,
'post_excerpt' => myplugin_generate_excerpt( $related_id ),
) );
}
}
نکته مهم: بدون بررسی doing_action، فراخوانی wp_update_post دوباره save_post را اجرا میکند و این چرخه بینهایت میشود.
کنترل بازگشتهای تودرتو
در سناریوهای پیچیده، ممکن است یک اکشن بهصورت غیرمستقیم چند بار فراخوانی شود:add_action( 'woocommerce_order_status_changed', 'myplugin_sync_order', 10, 4 );
function myplugin_sync_order( $order_id, $from, $to, $order ) {
static $syncing = false;
if ( $syncing ) {
return; // از قبل در حال همگامسازی هستیم
}
if ( doing_action( 'woocommerce_order_status_changed' ) && did_action( 'woocommerce_order_status_changed' ) > 1 ) {
return; // این فراخوانی دوم است
}
$syncing = true;
// همگامسازی که ممکن است وضعیت سفارش را تغییر دهد
myplugin_sync_to_external_system( $order );
$syncing = false;
}
این الگو ترکیبی از static و doing_action و did_action است و در افزونههای حرفهای بسیار رایج است.
ترکیب با سایر توابع
ترکیبdoing_action با did_action امکان تشخیص دقیق وضعیت را فراهم میکند:
function myplugin_context_aware_handler() {
$current = current_action();
if ( doing_action( 'save_post' ) ) {
// اجرا در بستر save_post
} elseif ( doing_action( 'wp_insert_post' ) ) {
// اجرا در بستر wp_insert_post
} else {
// اجرا در بستر دیگر
}
}
add_action( 'save_post', 'myplugin_context_aware_handler' );
add_action( 'wp_insert_post', 'myplugin_context_aware_handler' );
این الگو در افزونههایی که یک callback را به چند اکشن متصل میکنند، بسیار کاربردی است.
نکات امنیتی و اشتباهات رایج
اشتباه اول، نبود شرط است. اگر مقدار بازگشتی را بدون بررسی استفاده کنید، ممکن است رفتار غیرمنتظره رخ دهد. اشتباه دوم، نبود شمارنده است. در برخی سناریوها،doing_action بهتنهایی کافی نیست و باید با did_action ترکیب شود.
اشتباه سوم، نبود تست است. باید کد خود را در سناریوهای مختلف (فراخوانی اول، فراخوانی تودرتو، حلقه) بررسی کنید.
اشتباه چهارم، اتکای انحصاری به این تابع برای جلوگیری از حلقه است. گاهی بهتر است از متغیر static یا فلگ داخلی استفاده کنید.
اشتباه پنجم، نبود escape در خروجی است. اگر اطلاعاتی درباره وضعیت اکشن در HTML چاپ میکنید، از توابع escape استفاده کنید.
تحلیل فنی پیشرفته
در نگاه مهندسی، تابعdoing_action() یک نقطه دسترسی در لایه Hook System است که بر چند جنبه از سیستم اثر میگذارد. لایه اول لایه Stack Management است. وردپرس یک پشته از هوکهای در حال اجرا دارد که در متغیر $wp_current_filter ذخیره میشود.
لایه دوم لایه Recursion Detection است. این تابع امکان تشخیص فراخوانی بازگشتی را فراهم میکند و از حلقه بینهایت جلوگیری میکند.
لایه سوم لایه Context Awareness است. در پروژههایی که یک callback به چند اکشن متصل است، این تابع امکان تنظیم رفتار بر پایه بستر اجرا را فراهم میکند.
لایه چهارم لایه Performance است. فراخوانی مکرر این تابع هزینه ناچیزی دارد چرا که تنها یک جستجوی ساده در آرایه انجام میدهد.
لایه پنجم لایه Security است. جلوگیری از حلقه بینهایت میتواند از حملات DoS (Denial of Service) جلوگیری کند.
لایه ششم لایه Integration است. ترکیب با did_action و current_action امکان تشخیص دقیق وضعیت را فراهم میکند.
لایه هفتم لایه Multisite است. در شبکههای Multisite، پشته هوک در هر سایت مستقل است.
لایه هشتم لایه تست است. تستهای واحد باید همه سناریوها را پوشش دهند: فراخوانی ساده، فراخوانی تودرتو، حلقه.
مفاهیم پایهای Recursion در Recursion در ویکیپدیا توضیح داده شده است.
برای مطالعه بیشتر روی توابع مرتبط، میتوانید به راهنمای did_action، راهنمای current_action، راهنمای current_filter، راهنمای do_action، راهنمای add_action و راهنمای هوک save_post مراجعه کنید.
پرسشهای پرتکرار
تابعdoing_action چه چیزی برمیگرداند؟ مقدار بولی که نشان میدهد آیا اکشن مشخص در حال اجرا است یا نه.
آیا این تابع در داخل خود تابع callback هم مقدار true برمیگرداند؟ بله، یکی از کاربردهای اصلی آن همین است.
تفاوت doing_action و did_action چیست؟ اولی حالنگر است و دومی گذشتهنگر.
آیا این تابع برای جلوگیری از حلقه بینهایت کافی است؟ بله، اما در سناریوهای پیچیده بهتر است با متغیر static ترکیب شود.
آیا این تابع با Object Cache کار میکند؟ پشته در حافظه محلی است و با Object Cache ذخیره نمیشود.
نتیجه و مسیر ادامه
تابعdoing_action() یک ابزار حیاتی برای جلوگیری از حلقه بینهایت و کنترل بازگشتهای تودرتو در وردپرس است. استفاده درست از آن یعنی درک دقیق پشته هوک، ترکیب با did_action و current_action و تست در سناریوهای مختلف. این تابع در افزونههایی که به چند اکشن متصل هستند و در پردازشهای پیچیده، ارزش بالایی دارد.
اگر این تابع را در پروژهای واقعی به کار بردهاید و رفتار غیرمنتظرهای دیدهاید — بهخصوص در حلقههای تودرتو یا در ترکیب با افزونههای دیگر — تجربهتان میتواند راهگشای دیگران باشد. کدام بخش بیشترین زمان را از شما گرفت؟ دیدگاه خود را بنویسید.