هوک save_post یکی از پرکاربردترین هوک‌های وردپرس برای ذخیره متادیتا هنگام ایجاد یا ویرایش نوشته است. این هوک نقطه استاندارد ذخیره داده‌های سفارشی محسوب می‌شود و در همه افزونه‌های حرفه‌ای استفاده می‌شود. طراحی امن این هوک با بررسی nonce، autosave، revision و capability، پایه حفاظت از داده و جلوگیری از حفره‌های امنیتی است. اشتباهات رایجی مانند نبود nonce، نبود بررسی autosave، نبود بررسی capability و نبود تست می‌تواند به از دست رفتن داده یا به‌روزرسانی ناخواسته منجر شود. تسلط بر این هوک برای افزونه‌نویسی حرفه‌ای ضروری است و در ذخیره داده کاربرد جدی دارد.

چرا ذخیره امن داده حیاتی است؟

در پنل ویرایش وردپرس، هر بار که کاربر دکمه ذخیره یا به‌روزرسانی را می‌زند، وردپرس یک چرخه پیچیده را طی می‌کند: ذخیره عنوان، محتوا، دسته‌بندی، برچسب و متادیتا. این چرخه با هوک save_post به توسعه‌دهنده اعلام می‌شود. اگر این هوک به‌درستی استفاده نشود، سه خطر جدی وجود دارد: از دست رفتن داده، به‌روزرسانی ناخواسته در Autosave، و حفره‌های امنیتی. آگاهی از این خطرات و رعایت اصول امنیتی، پایه پیاده‌سازی حرفه‌ای محسوب می‌شود.

هوک save_post چیست؟

هوک save_post یک اکشن هوک در وردپرس است که پس از ذخیره یک نوشته در پایگاه داده اجرا می‌شود. این هوک در فایل wp-includes/post.php در تابع wp_insert_post فراخوانی می‌شود. پس از اجرای این هوک، وردپرس کوئری به‌روزرسانی را در جدول wp_posts ارسال کرده و متادیتای پست را ذخیره کرده است. اگر می‌خواهید متادیتای سفارشی خود را ذخیره کنید، این نقطه مناسب است. نکته مهم این است که این هوک هم در ایجاد پست جدید و هم در به‌روزرسانی پست موجود اجرا می‌شود. برای تفکیک این دو حالت، از پارامتر $update استفاده کنید که در بخش بعدی بررسی می‌شود.

پارامترها و مقادیر ورودی

تابع callback این هوک سه پارامتر دریافت می‌کند: - $post_id: شناسه نوشته ذخیره‌شده - $post: شیء WP_Post نوشته - $update: اگر true باشد، به‌روزرسانی است و اگر false باشد، ایجاد جدید نمونه استفاده:
add_action( 'save_post', 'myplugin_save_metadata', 10, 3 );
function myplugin_save_metadata( $post_id, $post, $update ) {
    // بررسی‌های امنیتی
    // ذخیره متادیتا
}
نکته مهم: تعداد آرگومان‌ها (پارامتر چهارم add_action) باید دقیقاً 3 باشد تا هر سه پارامتر به تابع پاس داده شوند.

priority و ترتیب اجرا

مقدار پیش‌فرض priority برای این هوک، 10 است. اگر افزونه‌های دیگر نیز به این هوک متصل هستند، priority ترتیب اجرا را تعیین می‌کند. کاربردهای رایج priority: - 1 تا 5: عملیات اولیه مانند بررسی‌های امنیتی - 10: ترتیب استاندارد - 20: پس از ذخیره سایر افزونه‌ها - 99: عملیات پایانی مانند پاک‌سازی کش

بررسی autosave و revision

یکی از پرتکرارترین اشتباهات، نبود بررسی Autosave است. وردپرس به‌صورت خودکار هر چند ثانیه یک نسخه موقت از نوشته ذخیره می‌کند. اگر در این زمان متادیتا ذخیره شود، ممکن است داده ناخواسته ذخیره شود. بررسی صحیح:
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
    return;
}
همچنین باید بررسی کنید که آیا این ذخیره مربوط به یک Revision است یا نه:
if ( wp_is_post_revision( $post_id ) ) {
    return;
}
نکته مهم: این دو بررسی باید در ابتدای تابع callback انجام شوند، پیش از هر عملیات دیگری.

بررسی nonce

بررسی nonce یکی از اصول پایه امنیتی است. هر فرم در پنل ویرایش باید nonce داشته باشد و در زمان ذخیره، این nonce بررسی شود. الگوی صحیح:
if ( ! isset( $_POST['myplugin_nonce'] ) ) {
    return;
}

$nonce = sanitize_text_field( wp_unslash( $_POST['myplugin_nonce'] ) );

if ( ! wp_verify_nonce( $nonce, 'myplugin_save_action' ) ) {
    return;
}
نکته مهم: استفاده از sanitize_text_field و wp_unslash پیش از بررسی. راهنمای تابع wp_verify_nonce در صفحه wp_verify_nonce آمده است.

بررسی capability

پیش از ذخیره متادیتا، باید بررسی شود که کاربر جاری مجاز به ویرایش این پست است:
if ( ! current_user_can( 'edit_post', $post_id ) ) {
    return;
}
نکته مهم: برای متادیتاهای خاص، ممکن است نیاز به بررسی capability متفاوتی باشد. برای نمونه، اگر ذخیره تنظیمات سئو انجام می‌شود، بررسی manage_options ممکن است مناسب‌تر باشد. راهنمای این تابع در صفحه current_user_can آمده است.

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

الگوی کامل ذخیره متادیتا:
add_action( 'save_post', 'myplugin_save_complete_metadata', 10, 3 );
function myplugin_save_complete_metadata( $post_id, $post, $update ) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }

    if ( wp_is_post_revision( $post_id ) ) {
        return;
    }

    if ( ! isset( $_POST['myplugin_nonce'] ) ) {
        return;
    }

    $nonce = sanitize_text_field( wp_unslash( $_POST['myplugin_nonce'] ) );
    if ( ! wp_verify_nonce( $nonce, 'myplugin_save_metadata' ) ) {
        return;
    }

    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    if ( isset( $_POST['myplugin_price'] ) ) {
        update_post_meta(
            $post_id,
            '_myplugin_price',
            (float) $_POST['myplugin_price']
        );
    }

    if ( isset( $_POST['myplugin_sku'] ) ) {
        update_post_meta(
            $post_id,
            '_myplugin_sku',
            sanitize_text_field( wp_unslash( $_POST['myplugin_sku'] ) )
        );
    }

    if ( isset( $_POST['myplugin_priority'] ) ) {
        $allowed = array( 'low', 'normal', 'high' );
        $priority = sanitize_key( wp_unslash( $_POST['myplugin_priority'] ) );

        if ( in_array( $priority, $allowed, true ) ) {
            update_post_meta( $post_id, '_myplugin_priority', $priority );
        }
    }
}
ذخیره متادیتا در یک پست تایپ خاص:
add_action( 'save_post_product', 'myplugin_save_product_metadata', 10, 3 );
function myplugin_save_product_metadata( $post_id, $post, $update ) {
    // بررسی‌های امنیتی مشترک
    // ذخیره متادیتای مخصوص محصول
}
استفاده از هوک اختصاصی save_post_{post_type} باعث می‌شود که تابع شما تنها در ذخیره آن پست تایپ خاص اجرا شود. این رویکرد کارایی را بهبود می‌بخشد. راهنمای این تابع در صفحه register_post_type آمده است.

هوک‌های مشابه و تفاوت‌ها

چند هوک مشابه وجود دارند که معمولاً با هم اشتباه گرفته می‌شوند: - save_post: پس از ذخیره هر نوع پستی - save_post_{post_type}: پس از ذخیره پست تایپ خاص - wp_insert_post: در هر بار درج یا به‌روزرسانی - edit_post: پس از ویرایش پست موجود - publish_post: هنگام انتشار پست برای ذخیره متادیتا، save_post یا save_post_{post_type} مناسب‌ترین گزینه است.

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

اشتباه اول، نبود nonce است. بدون nonce، حمله CSRF ممکن است. اشتباه دوم، نبود بررسی Autosave است. در Autosave ممکن است متادیتا ناخواسته ذخیره شود. اشتباه سوم، نبود بررسی wp_is_post_revision است. در ویرایش نسخه‌های قدیمی، ممکن است متادیتا به‌روزرسانی شود. اشتباه چهارم، نبود بررسی current_user_can است. ممکن است کاربر غیرمجاز متادیتا را تغییر دهد. اشتباه پنجم، نبود sanitize در مقادیر است. هر ورودی کاربر باید پاک‌سازی شود. اشتباه ششم، استفاده از نام تکراری برای هوک است. همیشه از prefix اختصاصی استفاده کنید. اشتباه هفتم، اجرای عملیات سنگین در همین هوک است. اگر عملیات ارسال ایمیل یا درخواست API دارید، بهتر است آن را به Cron یا پس‌زمینه منتقل کنید. اشتباه هشتم، نبود تست است. باید در سناریوهای ایجاد جدید، به‌روزرسانی، Autosave، Revision و کاربر غیرمجاز تست کنید.

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

در نگاه مهندسی، هوک save_post یک نقطه معماری در لایه Persistence است که بر چند لایه سیستم اثر می‌گذارد. لایه اول لایه Lifecycle است. این هوک پس از ذخیره پست اصلی اجرا می‌شود و امکان ذخیره داده‌های وابسته را فراهم می‌کند. لایه دوم لایه Security Boundary است. بررسی nonce، capability و sanitize در این هوک، مرز امنیتی بین کاربر و داده را تعریف می‌کند. لایه سوم لایه Hook Priority است. اگر چند افزونه به این هوک متصل باشند، priority ترتیب اجرا را تعیین می‌کند. لایه چهارم لایه Autosave Handling است. وردپرس در Autosave نیز این هوک را فراخوانی می‌کند، بنابراین بررسی DOING_AUTOSAVE ضروری است. لایه پنجم لایه Re-entrancy است. اگر در تابع callback خودتان دوباره wp_insert_post یا wp_update_post فراخوانی کنید، این هوک ممکن است به‌صورت بازگشتی اجرا شود. برای جلوگیری از حلقه بی‌نهایت، از remove_action و add_action موقت استفاده کنید. راهنمای این توابع در صفحه remove_action و صفحه has_action آمده است. لایه ششم لایه Performance است. تعداد زیاد عملیات در این هوک می‌تواند زمان ذخیره‌سازی پست را افزایش دهد. لایه هفتم لایه Multisite است. در شبکه‌های Multisite، این هوک در هر سایت مستقل اجرا می‌شود. لایه هشتم لایه Testing است. تست‌های End-to-End باید همه سناریوها را پوشش دهند. مفاهیم پایه‌ای Lifecycle Events در Lifecycle hooks در ویکی‌پدیا توضیح داده شده است. برای مطالعه بیشتر روی توابع مرتبط، می‌توانید به راهنمای update_post_meta، راهنمای get_post_meta، راهنمای delete_post_meta، راهنمای add_meta_box، راهنمای wp_verify_nonce، راهنمای current_user_can، راهنمای register_post_type و راهنمای esc_html مراجعه کنید.

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

تفاوت save_post و save_post_{post_type} چیست؟ اولی برای همه پست تایپ‌ها و دومی برای یک پست تایپ خاص اجرا می‌شود. آیا این هوک در Autosave اجرا می‌شود؟ بله، بنابراین بررسی DOING_AUTOSAVE ضروری است. چطور از حلقه بی‌نهایت جلوگیری کنیم؟ با remove_action موقت پیش از فراخوانی wp_update_post. آیا این هوک در پست تایپ سفارشی کار می‌کند؟ بله، اگر save_post یا save_post_{post_type} استفاده شود. آیا می‌توان در این هوک ایمیل ارسال کرد؟ بهتر است این کار به پس‌زمینه منتقل شود.

ادامه مسیر

هوک save_post نقطه استاندارد وردپرس برای ذخیره متادیتای سفارشی است. استفاده درست از آن یعنی بررسی Autosave، بررسی Revision، بررسی nonce، بررسی capability، sanitize داده‌ها و انتقال عملیات سنگین به پس‌زمینه. اشتباه‌های کوچک در این هوک اغلب به از دست رفتن داده یا حفره‌های امنیتی منجر می‌شوند. اگر این هوک را در پروژه‌ای واقعی به کار برده‌اید و رفتار غیرمنتظره‌ای دیده‌اید — به‌خصوص در Autosave، در ویرایش سریع یا در Multisite — تجربه‌تان می‌تواند راهگشای دیگران باشد. کدام بخش بیشترین زمان را از شما گرفت؟ دیدگاه خود را بنویسید.