سه خط کدی که افزونه را به یک پلتفرم تبدیل کرد

سال ۱۳۹۹، یک افزونه اختصاصی برای یک شرکت بیمه نوشتم که پرونده‌های خسارت را مدیریت می‌کرد. سه ماه بعد، مدیر فنی شرکت زنگ زد که «می‌خواهیم هر بار پرونده‌ای ثبت شد، یک پیامک هم به مشتری بفرستیم؛ ولی نمی‌خواهیم کد افزونه شما را دست بزنیم». من به‌جای ویرایش کد، سه خط اضافه کردم: یک do_action در نقطه دقیقاً پس از ثبت پرونده. سپس افزونه پیامک را در چند خط، به همان اکشن قلاب زدم. آن روز فهمیدم که ساخت اکشن سفارشی، مرز بین یک افزونه معمولی و یک پلتفرم قابل توسعه است. افزونه‌ای که اکشن سفارشی دارد، به دیگران اجازه می‌دهد بدون دست‌زدن به کد آن، رفتارش را گسترش دهند. این مقاله، همان تجربه و پروتکل ساخت اکشن سفارشی است که در پروژه‌های واقعی به‌کار می‌برم. اگر با مفاهیم پایه آشنا نیستید، پیش از ادامه هوک‌های وردپرس چیست، تفاوت اکشن و فیلتر در وردپرس و نحوه استفاده از add_action را بخوانید. مکمل این مقاله مهم‌ترین اکشن هوک‌های وردپرس، راهنمای حرفه‌ای کار با هوک‌ها و ساخت قابلیت اختصاصی با هوک‌ها است.

اکشن سفارشی دقیقاً چیست؟

اکشن سفارشی، یک «نقطه اعلام» است که خودِ شما در کد افزونه یا قالب تعریف می‌کنید. هسته وردپرس ده‌ها اکشن رسمی دارد (مثل init، save_post، wp_footer) که همه افزونه‌ها به آن‌ها وصل می‌شوند. اکشن سفارشی، همان مفهوم است، ولی برای رخدادهای اختصاصی افزونه شما. مثال‌های طبیعی از دنیای واقعی:

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

سه تفاوت بنیادین با اکشن‌های هسته:

  1. مالکیت: اکشن هسته، بخشی از وردپرس است؛ اکشن سفارشی، بخشی از افزونه شما. مسئولیت مستندسازی و پایداری آن با شماست.
  2. پیشوند: اکشن‌های هسته نام‌های شناخته‌شده دارند؛ اکشن سفارشی شما باید پیشوند یکتا داشته باشد تا با دیگران تعارض نکند.
  3. پارامترها: شما تعیین می‌کنید چه داده‌ای به callbackها پاس دهید و ترتیب پارامترها چگونه باشد.
اکشن سفارشی، مثل پریز برق است: شما پریز را در دیوار اتاق خودتان نصب می‌کنید؛ هرکسی می‌تواند دوشاخه بزند، ولی استاندارد پریز، کار شماست.

سه گام ساخت اکشن سفارشی

ساخت اکشن سفارشی، سه گام ساده دارد:

  1. گام اول — تعریف نقطه اعلام: در جایی از کد افزونه که رخداد موردنظر اتفاق می‌افتد، از do_action() استفاده کنید.
  2. گام دوم — پاس دادن پارامترها: داده‌های موردنیاز را به do_action پاس دهید تا callbackها به آن‌ها دسترسی داشته باشند.
  3. گام سوم — مستندسازی: در بالای خط do_action، با PHPDoc توضیح دهید که چه پارامترهایی پاس داده می‌شود و چه کاربردی دارد.

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

ساده‌ترین اکشن سفارشی

حداقل یک اکشن سفارشی، یک خط است:

// در افزونه شما، پس از تکمیل یک فرآیند
do_action( 'myplugin_after_process_completed' );

و مصرف آن در افزونه دیگر:

add_action( 'myplugin_after_process_completed', 'other_plugin_send_notification' );

function other_plugin_send_notification() {
    // ارسال نوتیفیکیشن
}

این دو خط، دقیقاً همان چیزی است که در ابتدای مقاله گفتم: افزونه شما را به یک نقطه اتصال تبدیل می‌کند. راهنمای نحوه قلاب‌زدن در نحوه استفاده از add_action، استفاده درست از هوک‌ها، مهم‌ترین اکشن هوک‌ها و تفاوت اکشن و فیلتر آمده است.

پاس دادن پارامترها به اکشن سفارشی

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

function myplugin_save_claim( $claim_data ) {
    // ... ذخیره در دیتابیس
    $claim_id = wp_insert_post( array(
        'post_type'   => 'claim',
        'post_title'  => $claim_data['title'],
        'post_status' => 'publish',
    ) );

    // اعلام رخداد با سه پارامتر
    do_action( 'myplugin_after_claim_created', $claim_id, $claim_data, get_current_user_id() );
}

و callback در افزونه دیگر:

add_action( 'myplugin_after_claim_created', 'other_plugin_send_sms', 10, 3 );

function other_plugin_send_sms( $claim_id, $claim_data, $user_id ) {
    $phone = get_user_meta( $user_id, '_phone', true );
    if ( $phone ) {
        myplugin_send_sms( $phone, 'پرونده شما ثبت شد' );
    }
}

سه نکته در پاس دادن پارامتر: یک — ترتیب پارامترها را در مستندسازی صریح بنویسید. callbackها نمی‌توانند حدس بزنند. دو — پارامترها را به‌صورت داده خام پاس دهید، نه فرمت‌شده. اگر callback به داده خام نیاز دارد، داده خام را پاس دهید؛ فرمت‌دهی را به callback بسپارید. سه — تعداد پارامترها را محدود نگه دارید. اکشن با ده پارامتر، برای callbackها گیج‌کننده است؛ اگر نیاز دارید، از یک آرایه معنادار استفاده کنید. راهنمای کامل در پارامترهای هوک وردپرس، Priority در هوک‌ها، کنترل ترتیب اجرای هوک‌ها، استفاده درست از هوک‌ها و راهنمای حرفه‌ای هوک‌ها آمده است.

اکشن بدون پارامتر، زنگی است که فقط صدا می‌دهد؛ اکشن با پارامتر، زنگی است که می‌گوید چه خبر است.

مستندسازی اکشن سفارشی با PHPDoc

الگوی استاندارد مستندسازی اکشن سفارشی در وردپرس:

/**
 * پس از ثبت موفق یک پرونده خسارت اجرا می‌شود.
 *
 * @since 1.0.0
 *
 * @param int   $claim_id  شناسه پرونده ثبت‌شده.
 * @param array $claim_data داده‌های پرونده شامل title، amount و description.
 * @param int   $user_id   شناسه کاربری که پرونده را ثبت کرده.
 */
do_action( 'myplugin_after_claim_created', $claim_id, $claim_data, $user_id );

سه نکته در مستندسازی: یک — @since: نسخه‌ای که این اکشن معرفی شده. اگر بعداً پارامتر اضافه کردید، در نسخه بعدی با @since دیگر توضیح دهید. دو — @param: نوع و توضیح هر پارامتر. سه — توضیح یک‌خطی: در ابتدای بلوک، در یک جمله بگویید اکشن چه زمانی اجرا می‌شود. الگوهای مشابه در ساختار استاندارد کدنویسی، استانداردهای کدنویسی وردپرس، اصول کدنویسی تمیز، ساختار فایل‌های افزونه استاندارد، استفاده از استانداردها در پروژه‌ها و ساختاربندی پروژه وردپرس آمده است.

نام‌گذاری اکشن سفارشی: قواعد و اشتباهات

نام اکشن سفارشی، سه قاعده دارد:

  1. پیشوند یکتا: نام اکشن باید با پیشوند افزونه شما شروع شود. مثال: myplugin_after_claim_created نه after_claim_created. دلیلش ساده است: وردپرس ده‌ها اکشن دارد و افزونه‌های دیگر هم هرکدام. بدون پیشوند، تعارض حتمی است.
  2. حروف کوچک و زیرخط: سبک استاندارد وردپرس. myplugin_after_claim_created نه myPlugin_AfterClaimCreated.
  3. نام معنادار: myplugin_after_claim_created بهتر از myplugin_hook_1.

یک الگوی حرفه‌ای دیگر: اکشن‌ها را بر اساس زمان رخداد نام‌گذاری کنید. سه پیشوند زمانی که در پروژه‌های خودم استفاده می‌کنم: before_، after_، on_. مثال‌ها: myplugin_before_claim_save، myplugin_after_claim_saved، myplugin_on_claim_status_changed. راهنمای کامل در استانداردهای کدنویسی وردپرس، اصول کدنویسی تمیز، ساختار استاندارد کدنویسی، اشتباهات رایج توسعه وردپرس و کدنویسی اختصاصی افزونه آمده است.

کجا do_action را قرار دهیم؟

انتخاب نقطه درست برای do_action، تفاوت بین اکشن مفید و اکشن بی‌فایده است. سه قاعده عملی:

قاعده اول — بعد از تکمیل یک منطق، نه در وسط: اکشن را جایی قرار دهید که وضعیت به یک نقطه پایدار رسیده است. اگر اکشن را قبل از ذخیره داده صدا بزنید، callbackها داده ناقص می‌بینند.

قاعده دوم — در چند نقطه کلیدی، نه همه جا: برای هر عملیات کوچک، اکشن نسازید. سه یا چهار نقطه کلیدی در هر فرآیند، کافی است: آغاز، میانه، پایان موفق، پایان ناموفق.

قاعده سوم — پس از پاک‌سازی، نه قبل: اکشن را پس از پاک‌سازی داده قرار دهید تا callbackها داده تمیز ببینند:

// نامناسب - قبل از پاک‌سازی
do_action( 'myplugin_after_claim_submitted', $_POST );

// مناسب - پس از پاک‌سازی
$claim_data = array(
    'title'       => sanitize_text_field( wp_unslash( $_POST['title'] ?? '' ) ),
    'amount'      => absint( $_POST['amount'] ?? 0 ),
    'description' => wp_kses_post( wp_unslash( $_POST['description'] ?? '' ) ),
);
do_action( 'myplugin_after_claim_submitted', $claim_data, get_current_user_id() );

راهنمای تکمیلی در PHP امن در وردپرس، پاک‌سازی داده‌ها، اعتبارسنجی داده‌ها، نانس وردپرس، توابع امنیت و پاک‌سازی و امنیت پروژه وردپرس آمده است.

ساختار کلاس‌محور برای اکشن سفارشی

در افزونه‌های جدی، اکشن‌های سفارشی را در یک کلاس نگه دارید:

class My_Plugin_Actions {
    /**
     * پس از ثبت موفق یک پرونده خسارت اجرا می‌شود.
     *
     * @since 1.0.0
     *
     * @param int   $claim_id  شناسه پرونده.
     * @param array $claim_data داده‌های پرونده.
     * @param int   $user_id   شناسه کاربر.
     */
    public static function claim_created( $claim_id, $claim_data, $user_id ) {
        do_action( 'myplugin_after_claim_created', $claim_id, $claim_data, $user_id );
    }

    public static function claim_status_changed( $claim_id, $old_status, $new_status ) {
        do_action( 'myplugin_on_claim_status_changed', $claim_id, $old_status, $new_status );
    }

    public static function claim_deleted( $claim_id ) {
        do_action( 'myplugin_before_claim_deleted', $claim_id );
    }
}

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

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

سه اشتباه رایج در ساخت اکشن سفارشی

فهرست کوتاه اما گران‌قیمت از اشتباهاتی که در کدهای بازبینی‌شده دیده‌ام:

اکشن سفارشی، مثل قرارداد است؛ اگر نام و پارامترها شفاف نباشند، هر کس تفسیر خودش را می‌کند و نتیجه، آشفتگی است.

الگوهای حرفه‌ای: سه سطح عمق در ساخت اکشن

در پروژه‌های خودم، سه سطح عمق برای اکشن سفارشی می‌بینم:

سطح اول — اکشن اعلان‌دهنده: فقط اعلام یک رخداد بدون پارامتر. مناسب برای سناریوهایی که callback فقط می‌خواهد بداند رخداد اتفاق افتاد:

do_action( 'myplugin_after_install_completed' );

سطح دوم — اکشن با داده: اعلان رخداد با پارامترهای معنادار. پرکاربردترین سطح. مثال:

do_action( 'myplugin_after_order_created', $order_id, $order_data, $user_id );

سطح سوم — اکشن زنجیره‌ای: چند اکشن مرتبط در یک فرآیند پیچیده. مناسب برای فرآیندهای چندمرحله‌ای:

do_action( 'myplugin_order_process_started', $order_id );
// ... پردازش
do_action( 'myplugin_order_process_completed', $order_id, $result );
// یا در صورت خطا
do_action( 'myplugin_order_process_failed', $order_id, $error_message );

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

ساخت اکشن سفارشی برای افزونه‌های فروشگاهی

در فروشگاه‌های ووکامرسی، اکشن سفارشی برای منطق کسب‌وکار اختصاصی حیاتی است:

class My_Shop_Actions {
    public static function init() {
        add_action( 'woocommerce_thankyou', array( __CLASS__, 'after_purchase' ) );
    }

    public static function after_purchase( $order_id ) {
        $order = wc_get_order( $order_id );
        if ( ! $order ) {
            return;
        }

        $order_data = array(
            'total'    => $order->get_total(),
            'items'    => $order->get_items(),
            'customer' => $order->get_customer_id(),
        );

        do_action( 'myshop_after_purchase_completed', $order_id, $order_data );
    }
}

مصرف این اکشن در افزونه دیگر:

add_action( 'myshop_after_purchase_completed', 'other_plugin_loyalty_points', 10, 2 );

function other_plugin_loyalty_points( $order_id, $order_data ) {
    // افزودن امتیاز وفاداری
}

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

اکشن سفارشی در قالب: از چایلد به والد و برعکس

در قالب‌ها، اکشن سفارشی معمولاً در دو سناریو به‌کار می‌آید:

سناریو اول — افزودن نقطه اتصال در فایل‌های template:

// در header.php قالب
do_action( 'mytheme_after_header' );

// در چایلد تم
add_action( 'mytheme_after_header', 'my_child_add_top_bar' );

function my_child_add_top_bar() {
    echo '<div class="top-bar">اطلاعیه</div>';
}

سناریو دوم — جایگزینی بخش‌هایی از قالب والد: به‌جای override فایل‌های template، قالب والد اکشن‌های سفارشی در نقطه‌های کلیدی می‌گذارد و چایلد تم به آن‌ها وصل می‌شود.

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

دیباگ اکشن سفارشی: ابزارها

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

  • Query Monitor: لیست تمام اکشن‌ها با ترتیب و callback. راهنمای کامل در توابع دیباگ وردپرس.
  • error_log در callback: بررسی اینکه کد اجرا می‌شود یا نه. راهنما در دیباگ کد سفارشی وردپرس.
  • global $wp_filter: برای دیدن تمام callbackهای ثبت‌شده روی اکشن. راهنما در دیباگ اکشن و فیلتر.
  • تست با priority بالا: اگر اکشن در انتهای زنجیره اجرا می‌شود، از priority بالا استفاده کنید.

یک تجربه میدانی: در پروژه‌ای، اکشن سفارشی به‌نظر ساده کار نمی‌کرد. با global $wp_filter، کشف شد که افزونه‌ای دیگر یک متغیر سراسری را در همان نقطه پاک می‌کند. تنظیم priority، مشکل را حل کرد. راهنما در تست و دیباگ پروژه‌های وردپرس، دیباگ کد سفارشی و خطای Deprecated در PHP.

امنیت در اکشن سفارشی: پنج قاعده طلایی

هر اکشن سفارشی، یک نقطه ورود بالقوه است. پنج قاعده امنیتی الزامی:

  1. پاک‌سازی ورودی قبل از اعلام رخداد: do_action را پس از sanitize_* قرار دهید تا callbackها داده آلوده نگیرند. راهنما در پاک‌سازی داده‌ها.
  2. اعتبارسنجی داده قبل از اعلام: absint، sanitize_email، wp_kses_post بسته به نوع. راهنما در اعتبارسنجی داده‌ها.
  3. بررسی دسترسی قبل از هر عملیات حساس: current_user_can. راهنما در نقش و دسترسی.
  4. نانس در فرم‌ها و AJAX: wp_verify_nonce و check_ajax_referer. راهنما در نانس وردپرس و پیاده‌سازی نانس در فرم‌ها.
  5. مستندسازی پارامترها با PHPDoc: تا کسی به‌اشتباه داده حساس پاس ندهد. راهنما در ساختار استاندارد کدنویسی.

مباحث امنیتی تکمیلی در هوک‌های وردپرس و امنیت کد، امنیت پروژه وردپرس، امنیت وردپرس برای مبتدیان، افزونه‌های امنیتی وردپرس، PHP امن در وردپرس، امن‌سازی ورود ادمین و توابع امنیت و پاک‌سازی آمده است.

الگوی Registry Pattern برای اکشن‌های سفارشی

در پروژه‌های بزرگ، تمام اکشن‌های سفارشی را در یک نقطه ثبت کنید:

class My_Plugin_Custom_Actions {
    public static function init() {
        add_action( 'init', array( __CLASS__, 'register_hooks' ) );
    }

    public static function register_hooks() {
        add_action( 'save_post', array( __CLASS__, 'on_post_saved' ), 10, 3 );
        add_action( 'user_register', array( __CLASS__, 'on_user_registered' ) );
    }

    public static function on_post_saved( $post_id, $post, $update ) {
        do_action( 'myplugin_after_post_saved', $post_id, $post, $update );
    }

    public static function on_user_registered( $user_id ) {
        do_action( 'myplugin_after_user_registered', $user_id );
    }
}

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

ساخت اکشن سفارشی برای ووکامرس: مثال‌های واقعی

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

یک — پس از افزودن محصول به سبد:

add_action( 'woocommerce_add_to_cart', 'myshop_on_add_to_cart', 10, 6 );

function myshop_on_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) {
    do_action( 'myshop_after_add_to_cart', $product_id, $quantity, $variation_id );
}

دو — پس از تغییر وضعیت سفارش:

add_action( 'woocommerce_order_status_changed', 'myshop_on_status_changed', 10, 4 );

function myshop_on_status_changed( $order_id, $old_status, $new_status, $order ) {
    do_action( 'myshop_after_order_status_changed', $order_id, $old_status, $new_status );
}

سه — پس از تکمیل یک دوره آموزشی:

add_action( 'learndash_course_completed', 'myshop_on_course_completed', 10, 1 );

function myshop_on_course_completed( $data ) {
    do_action( 'myshop_after_course_completed', $data['user']->ID, $data['course']->ID );
}

راهنمای کامل در هوک‌های ووکامرس، مدیریت سفارش‌های ووکامرس، سفارشی‌سازی سبد و تسویه‌حساب، سفارشی‌سازی صفحه محصول، افزونه‌های سایت آموزشی، راهنمای کار با ووکامرس و ووکامرس چیست آمده است.

ساخت اکشن سفارشی در افزونه‌های عضویتی و LMS

در سایت‌های عضویتی و آموزشی، اکشن سفارشی برای منطق کسب‌وکار ضروری است:

یک — پس از ارتقای سطح عضویت:

do_action( 'myplugin_after_membership_upgraded', $user_id, $old_tier, $new_tier );

دو — پس از اتمام یک درس:

do_action( 'myplugin_after_lesson_completed', $user_id, $lesson_id, $course_id );

سه — پس از صدور گواهی:

do_action( 'myplugin_after_certificate_issued', $user_id, $course_id, $certificate_id );

راهنمای کامل در هوک‌های مدیریت کاربران، هوک‌های ورود و ثبت‌نام، کار با User Meta، توابع نقش و دسترسی، افزونه‌های سایت آموزشی، قالب آموزشی و ساخت قابلیت اختصاصی با هوک‌ها آمده است.

در پروژه‌های بزرگ، اکشن سفارشی، زبان مشترک بین تیم‌ها و افزونه‌هاست؛ اگر این زبان مشترک نباشد، هر افزونه یک جزیره جدا می‌شود.

جمع‌بندی

ساخت اکشن سفارشی در وردپرس، سه گام ساده دارد: تعریف نقطه اعلام با do_action، پاس دادن پارامترها و مستندسازی با PHPDoc. سه اصل را در پایان تاکید می‌کنم: اول، اکشن سفارشی را به‌عنوان پلتفرم ببینید، نه قابلیت جانبی. دوم، پیشوند یکتا و نام معنادار را جدی بگیرید تا با دیگران تعارض نکند. سوم، پارامترها را پس از پاک‌سازی پاس دهید تا callbackها داده تمیز ببینند.

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