تابع doing_action یکی از توابع کلیدی وردپرس برای تشخیص در حال اجرا بودن یک اکشن مشخص در لحظه است. این تابع در جلوگیری از حلقه‌های بی‌نهایت، کنترل بازگشت‌های تودرتو و شرط‌گذاری دقیق در callbackها نقش حیاتی دارد. تشخیص درست اینکه کد شما در داخل یک اکشن در حال اجراست، می‌تواند از بروز باگ‌های پیچیده و کرش سرور جلوگیری کند. اشتباهات رایجی مانند نبود شرط، نبود بررسی و نبود تست می‌تواند به رفتار غیرمنتظره و افت عملکرد منجر شود. تسلط بر این تابع برای کدنویسی امن و افزونه‌نویسی حرفه‌ای ضروری است و در هوک‌های پیچیده کاربرد جدی دارد.

چرا تشخیص اکشن در حال اجرا حیاتی است؟

در توسعه افزونه وردپرس، گاهی یک 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 و تست در سناریوهای مختلف. این تابع در افزونه‌هایی که به چند اکشن متصل هستند و در پردازش‌های پیچیده، ارزش بالایی دارد. اگر این تابع را در پروژه‌ای واقعی به کار برده‌اید و رفتار غیرمنتظره‌ای دیده‌اید — به‌خصوص در حلقه‌های تودرتو یا در ترکیب با افزونه‌های دیگر — تجربه‌تان می‌تواند راهگشای دیگران باشد. کدام بخش بیشترین زمان را از شما گرفت؟ دیدگاه خود را بنویسید.