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

چرا دانستن تعداد اجرای اکشن حیاتی است؟

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

تابع did_action چیست؟

تابع did_action() یک تابع هسته وردپرس است که در فایل wp-includes/plugin.php تعریف شده است. این تابع تعداد دفعات اجرای یک اکشن مشخص را در طول درخواست جاری برمی‌گرداند. خروجی این تابع یک عدد صحیح است که می‌تواند صفر (اکشن هنوز اجرا نشده) یا بیشتر باشد. برخلاف توابع دیگر که بولی برمی‌گردانند، این تابع مقدار شمارنده واقعی را برمی‌گرداند. نکته مهم این است که این شمارنده تنها در طول یک درخواست HTTP معتبر است و در درخواست بعدی به صفر بازنشانی می‌شود.

امضای تابع و پارامترها

امضای این تابع به‌شکل زیر است:
function did_action( $hook_name ) {
    global $wp_actions;
    if ( ! isset( $wp_actions[ $hook_name ] ) ) {
        return 0;
    }
    return $wp_actions[ $hook_name ];
}
پارامتر ورودی یک رشته است که نام اکشن را مشخص می‌کند. این پارامتر اجباری است و اگر خالی باشد، وردپرس خطای Warning می‌دهد. خروجی یک عدد صحیح است. برای بررسی اینکه اکشن اجرا شده یا نه، مقایسه با صفر کافی است. برای بررسی اجرای چندباره، از عملگرهای مقایسه‌ای مانند > یا >= استفاده کنید.

سازوکار داخلی تابع

تابع did_action() از متغیر جهانی $wp_actions استفاده می‌کند. این متغیر یک آرایه انجمنی است که در آن، کلیدها نام اکشن‌ها و مقادیر، تعداد اجرا هستند. وقتی تابع do_action() فراخوانی می‌شود، پیش از اجرای callbackها، مقدار شمارنده اکشن مربوطه را یک واحد افزایش می‌دهد. بنابراین، در داخل تابع callback، مقدار did_action() برای آن اکشن حداقل یک است. نکته مهم: شمارنده پس از پایان درخواست HTTP به‌طور خودکار صفر می‌شود. بنابراین این تابع تنها برای کنترل در طول یک درخواست مناسب است.

تفاوت با doing_action و current_action

سه تابع مرتبط با اکشن‌ها وجود دارند که معمولاً با هم اشتباه گرفته می‌شوند: - did_action( $hook ): تعداد اجرای یک اکشن مشخص را برمی‌گرداند - doing_action( $hook ): بررسی می‌کند که آیا اکشن مشخص در همین لحظه در حال اجرا است - current_action(): نام اکشن در حال اجرا را برمی‌گرداند تفاوت کلیدی: did_action گذشته‌نگر است (چند بار اجرا شده)، doing_action حال‌نگر است (آیا الان در حال اجرا است) و current_action توصیفی است (کدام اکشن الان اجرا می‌شود). راهنمای این توابع در صفحه doing_action و صفحه current_action آمده است.

کاربردهای عملی در افزونه‌نویسی

یکی از رایج‌ترین کاربردها، جلوگیری از اجرای یک عملیات سنگین بیش از یک بار در یک درخواست است:
add_action( 'save_post', 'myplugin_send_notification', 10, 3 );
function myplugin_send_notification( $post_id, $post, $update ) {
    if ( did_action( 'save_post' ) > 1 ) {
        return; // این فراخوانی دوم است، نادیده بگیر
    }

    // ارسال اعلان تنها یک بار
    wp_mail( 'admin@example.com', 'نوشته جدید', $post->post_title );
}
این الگو در سناریوهایی که یک اکشن به‌صورت غیرمستقیم چند بار فراخوانی می‌شود، بسیار کاربردی است. کاربرد دیگر، اجرای متفاوت بر پایه تعداد اجرا:
add_action( 'myplugin_process_item', 'myplugin_handle_item' );
function myplugin_handle_item( $item_id ) {
    $execution_count = did_action( 'myplugin_process_item' );

    if ( 1 === $execution_count ) {
        // اولین آیتم، راه‌اندازی اولیه
        myplugin_init_batch();
    }

    myplugin_process_single_item( $item_id );

    if ( $execution_count === myplugin_get_total_items() ) {
        // آخرین آیتم، بستن دسته
        myplugin_finalize_batch();
    }
}

جلوگیری از اجرای تکراری در حلقه‌ها

در حلقه‌های وردپرس، گاهی یک اکشن در هر آیتم فراخوانی می‌شود. اگر می‌خواهید عملیاتی تنها یک بار انجام شود، از این تابع استفاده کنید:
add_action( 'myplugin_after_render_item', 'myplugin_track_first_item' );
function myplugin_track_first_item( $item ) {
    if ( 1 !== did_action( 'myplugin_after_render_item' ) ) {
        return;
    }

    // ثبت تنها برای اولین آیتم
    myplugin_log_first_rendered_item( $item );
}
این الگو در گزارش‌گیری، شمارش و مانیتورینگ بسیار مفید است.

کاربرد در بهینه‌سازی کارایی

در افزونه‌هایی که callback آن‌ها به چند اکشن متصل است، استفاده از این تابع می‌تواند از اجرای تکراری کوئری‌های سنگین جلوگیری کند:
function myplugin_shared_callback() {
    static $cache = null;

    if ( null === $cache ) {
        $cache = myplugin_get_expensive_data();
    }

    if ( did_action( 'myplugin_shared_action' ) > 1 ) {
        return; // داده از قبل استفاده شده
    }

    myplugin_use_data( $cache );
}
add_action( 'myplugin_shared_action', 'myplugin_shared_callback' );
نکته مهم: ترکیب این تابع با متغیرهای static می‌تواند در برخی سناریوها مؤثرتر باشد، چرا که هزینه محاسبه مجدد را حذف می‌کند.

نکات امنیتی و اشتباهات رایج

اشتباه اول، نبود شرط است. اگر مقدار بازگشتی را بدون بررسی استفاده کنید، ممکن است خطای Fatal Error رخ دهد. اشتباه دوم، نبود شمارنده در حلقه‌ها است. اگر کد شما در حلقه اجرا می‌شود و به شمارنده اعتماد می‌کنید، باید مطمئن شوید که با آیتم‌های متعدد به‌درستی کار می‌کند. اشتباه سوم، استفاده در سناریوهای کش‌شده است. اگر خروجی صفحه کش شود، شمارنده در درخواست‌های بعدی صفر می‌شود. بنابراین نمی‌توان به آن برای کنترل بین درخواست‌ها اعتماد کرد. اشتباه چهارم، نبود تست است. باید کد خود را در سناریوهای مختلف (اجرای یک بار، چند بار، در حلقه) بررسی کنید. اشتباه پنجم، اتکای انحصاری به این تابع برای جلوگیری از اجرای تکراری است. در برخی موارد، بهتر است از پارامترهای static یا فلگ‌های داخلی استفاده کنید.

تحلیل فنی پیشرفته

در نگاه مهندسی، تابع did_action() یک نقطه دسترسی در لایه Hook System است که بر چند جنبه از سیستم اثر می‌گذارد. لایه اول لایه شمارش است. وردپرس یک آرایه سراسری $wp_actions دارد که هر اکشن فراخوانی‌شده را شمارش می‌کند. این ساختار سبک و کارآمد است و هزینه ناچیزی دارد. لایه دوم لایه Idempotency است. این تابع امکان پیاده‌سازی عملیات Idempotent را فراهم می‌کند، یعنی عملیاتی که حتی اگر چند بار فراخوانی شود، اثر یکسان دارد. لایه سوم لایه Batch Processing است. در پردازش دسته‌ای، این تابع امکان تشخیص شروع و پایان دسته را فراهم می‌کند. لایه چهارم لایه Performance است. جلوگیری از اجرای تکراری کوئری‌های سنگین می‌تواند سرعت را چند برابر کند. لایه پنجم لایه Multisite است. در شبکه‌های Multisite، شمارنده در هر سایت مستقل است. لایه ششم لایه REST API است. در درخواست‌های REST API، شمارنده تنها در طول آن درخواست معتبر است. لایه هفتم لایه Caching است. اگر از Object Cache استفاده می‌کنید، باید توجه داشته باشید که $wp_actions در حافظه محلی است و با Object Cache ذخیره نمی‌شود. لایه هشتم لایه تست است. تست‌های واحد باید همه سناریوها را پوشش دهند: اجرای یک بار، چند بار، در حلقه و در Contextهای مختلف. مفاهیم پایه‌ای Idempotency در Idempotence در ویکی‌پدیا توضیح داده شده است. برای مطالعه بیشتر روی توابع مرتبط، می‌توانید به راهنمای doing_action، راهنمای current_action، راهنمای current_filter، راهنمای do_action، راهنمای add_action و راهنمای هوک save_post مراجعه کنید.

پرسش‌های پرتکرار

تابع did_action چه چیزی برمی‌گرداند؟ تعداد دفعات اجرای یک اکشن مشخص در طول درخواست جاری. آیا این تابع در درخواست‌های مختلف مقدار یکسانی دارد؟ خیر، شمارنده در هر درخواست به صفر بازنشانی می‌شود. تفاوت did_action و doing_action چیست؟ اولی گذشته‌نگر است و دومی حال‌نگر. آیا این تابع برای جلوگیری از اجرای تکراری در حلقه مناسب است؟ بله، یکی از رایج‌ترین کاربردهای آن است. آیا این تابع با Object Cache کار می‌کند؟ شمارنده در حافظه محلی است و با Object Cache ذخیره نمی‌شود.

نتیجه و مسیر ادامه

تابع did_action() یک ابزار کارآمد برای شمارش تعداد اجرای اکشن‌ها و جلوگیری از اجرای تکراری است. استفاده درست از آن یعنی درک دقیق متغیر $wp_actions، توجه به محدودیت درون‌درخواستی، ترکیب با doing_action و current_action و تست در سناریوهای مختلف. این تابع در افزونه‌های پیچیده و پردازش‌های دسته‌ای ارزش بالایی دارد. اگر این تابع را در پروژه‌ای واقعی به کار برده‌اید و رفتار غیرمنتظره‌ای دیده‌اید — به‌خصوص در حلقه‌های تودرتو یا در ترکیب با افزونه‌های دیگر — تجربه‌تان می‌تواند راهگشای دیگران باشد. کدام بخش بیشترین زمان را از شما گرفت؟ دیدگاه خود را بنویسید.