تابع register_setting() در وردپرس ابزار رسمی ثبت یک تنظیم (setting) در سیستم Settings API است و پیش‌نیاز ذخیره امن مقادیر پنل مدیریت افزونه‌ها محسوب می‌شود. بدون ثبت صحیح setting، فرم‌های تنظیمات افزونه در برابر حمله‌های رایج مثل Injection و CSRF آسیب‌پذیر می‌شوند.

تابع register_setting وردپرس یکی از پرکاربردترین توابع Settings API برای ثبت تنظیمات افزونه‌ها و قالب‌هاست. این تابع امکان تعریف option_group، option_name و sanitize_callback را فراهم می‌کند و پایه ذخیره امن مقادیر پنل مدیریت محسوب می‌شود. در این راهنما ساختار کامل، پارامترها، نمونه‌های واقعی، اشتباهات رایج و نکات امنیتی این تابع بررسی می‌شود. همچنین تفاوت آن با add_option و update_option توضیح داده می‌شود. در پایان پرسش‌های پرتکرار و نگاه فنی عمیق به این تابع مرور خواهد شد.

در پروژه‌هایی که پنل تنظیمات اختصاصی داشتند، این تابع همیشه یکی از نقاط حساس کد بوده است. یک تنظیم بدون sanitize_callback، به‌سادگی به یک نقطه نشت داده یا ورود کد مخرب تبدیل می‌شود. این تابع به‌تنهایی امنیت را تضمین نمی‌کند؛ امنیت از ترکیب درست register_setting با sanitize و capability حاصل می‌شود.

چرا register_setting اهمیت دارد

وردپرس برای ذخیره مقادیر پنل مدیریت از جدول wp_options استفاده می‌کند. هر افزونه‌ای که یک فرم تنظیمات دارد، باید مقادیر ورودی کاربر را در همین جدول ذخیره کند. اما ذخیره مستقیم با update_option بدون یک لایه اعتبارسنجی و بدون هماهنگی با فرم‌های استاندارد وردپرس، امنیت و انسجام پروژه را تهدید می‌کند.

تابع register_setting() به‌عنوان بخشی از Settings API این خلأ را پر می‌کند. این تابع به وردپرس اعلام می‌کند که یک تنظیم مشخص وجود دارد، چه نامی دارد، از چه گروهی است و هنگام ذخیره چه تابعی باید روی مقدار آن اجرا شود. پس از این ثبت، فرم‌های استاندارد مثل settings_fields() و do_settings_sections() به‌طور خودکار با آن کار می‌کنند.

برای درک کامل جایگاه این تابع در اکوسیستم Settings API، مطلب تابع add_settings_section و تابع add_settings_field را مطالعه کنید. این دو تابع در کنار register_setting، سه‌گانه اصلی ساخت پنل تنظیمات را تشکیل می‌دهند.

ساختار و امضای تابع register_setting

امضای این تابع در نسخه‌های جدید وردپرس به شکل زیر است:

register_setting( string $option_group, string $option_name, array $args = array() ): void

پارامتر اول، نام گروه تنظیمات است که باید با مقدار option_group در settings_fields() یکسان باشد. پارامتر دوم، نام کلید تنظیم در جدول wp_options است. پارامتر سوم از نسخه 4.7 اضافه شده و می‌تواند شامل type، description، sanitize_callback، show_in_rest و default باشد.

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

پارامترهای کلیدی و کاربرد هرکدام

پارامتر option_group

مشخص می‌کند که این setting به کدام گروه از تنظیمات تعلق دارد. این گروه در تابع settings_fields() استفاده می‌شود تا وردپرس بداند کدام فیلدها باید پردازش شوند:

register_setting(
    'myplugin_settings',
    'myplugin_api_key'
);

در فرم مربوطه، حتماً باید settings_fields( 'myplugin_settings' ) فراخوانی شود تا فیلدهای این گروه پردازش شوند.

پارامتر option_name

نام کلید تنظیم در جدول wp_options. این نام در تابع get_option() و update_option() استفاده می‌شود:

$api_key = get_option( 'myplugin_api_key' );

برای مطالعه الگوی دریافت و ذخیره تنظیمات، مطلب تابع get_option و تابع update_option را ببینید.

پارامتر type

نوع داده‌ای که ذخیره می‌شود. مقادیر مجاز: string، integer، number، boolean، array، object. این پارامتر در REST API و در اعتبارسنجی خودکار استفاده می‌شود:

register_setting( 'myplugin_settings', 'myplugin_count', array(
    'type' => 'integer',
) );

پارامتر description

توضیح کوتاهی که در REST API نمایش داده می‌شود. برای مستندسازی داخلی مفید است:

register_setting( 'myplugin_settings', 'myplugin_mode', array(
    'description' => 'حالت فعال افزونه',
) );

پارامتر show_in_rest

اگر true باشد، این تنظیم در REST API قابل دسترسی می‌شود. برای پنل‌های مبتنی بر بلاک گوتنبرگ ضروری است:

register_setting( 'myplugin_settings', 'myplugin_mode', array(
    'show_in_rest' => true,
    'type'         => 'string',
) );

پارامتر default

مقدار پیش‌فرض که در صورت نبود مقدار در دیتابیس، در REST API استفاده می‌شود. توجه کنید که این مقدار به‌طور خودکار در دیتابیس ذخیره نمی‌شود؛ فقط در API رفتار پیش‌فرض را تعیین می‌کند.

پارامتر sanitize_callback

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

register_setting(
    'myplugin_settings',
    'myplugin_url',
    array(
        'sanitize_callback' => 'esc_url_raw',
    )
);

در بخش بعدی، انواع callbackهای متداول را بررسی خواهیم کرد.

نقش حیاتی sanitize_callback در امنیت

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

  • sanitize_text_field: برای متن تک‌خطی
  • sanitize_textarea_field: برای متن چندخطی
  • sanitize_email: برای ایمیل
  • esc_url_raw: برای URL
  • absint: برای اعداد صحیح مثبت
  • wp_kses_post: برای HTML محدود
  • sanitize_key: برای شناسه‌های لاتین

برای آشنایی با الگوهای جامع sanitize و escape، مطلب راهنمای Sanitization در وردپرس منبع اصلی است.

callback سفارشی برای داده پیچیده

در برخی سناریوها، sanitize_callback باید منطق اختصاصی داشته باشد. مثلاً وقتی یک آرایه از تنظیمات را ذخیره می‌کنید:

register_setting(
    'myplugin_settings',
    'myplugin_options',
    array(
        'type'              => 'array',
        'sanitize_callback' => function ( $input ) {
            $clean = array();
            if ( isset( $input['mode'] ) ) {
                $clean['mode'] = sanitize_key( $input['mode'] );
            }
            if ( isset( $input['email'] ) ) {
                $clean['email'] = sanitize_email( $input['email'] );
            }
            return $clean;
        },
    )
);

این الگو در افزونه‌های حرفه‌ای بسیار رایج است. مطلب Input Validation در وردپرس اصول این کار را پوشش می‌دهد.

callback برای داده‌های حساس

اگر تنظیم شما حاوی کلید API یا رمز است، باید علاوه بر sanitize، امکان ذخیره امن و پاک کردن کش را نیز فراهم کنید. برای این نوع داده‌ها از sanitize_text_field استفاده کنید و مطمئن شوید که مقدار در لاگ‌ها ثبت نمی‌شود.

نمونه‌های عملی در پروژه واقعی

ساخت پنل تنظیمات پایه

add_action( 'admin_init', function () {
    register_setting(
        'myplugin_settings',
        'myplugin_api_key',
        array(
            'type'              => 'string',
            'sanitize_callback' => 'sanitize_text_field',
            'default'           => '',
        )
    );

    add_settings_section(
        'myplugin_main_section',
        'تنظیمات اصلی',
        function () {
            echo '<p>' . esc_html__( 'تنظیمات اصلی افزونه', 'my-plugin' ) . '</p>';
        },
        'myplugin_settings'
    );

    add_settings_field(
        'myplugin_api_key_field',
        'کلید API',
        function () {
            $value = get_option( 'myplugin_api_key' );
            echo '<input type="text" name="myplugin_api_key" value="' . esc_attr( $value ) . '" />';
        },
        'myplugin_settings',
        'myplugin_main_section'
    );
} );

در این نمونه، سه تابع اصلی Settings API با هم ترکیب شده‌اند. برای مطالعه دقیق‌تر بخش‌ها و فیلدها، مطالب add_settings_section و add_settings_field را ببینید.

نمایش فرم در صفحه تنظیمات

function myplugin_settings_page() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    <div class="wrap">
        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
        <form method="post" action="options.php">
            <?php
            settings_fields( 'myplugin_settings' );
            do_settings_sections( 'myplugin_settings' );
            submit_button();
            ?>
        </form>
    </div>
    <?php
}

در این الگو، تابع settings_fields() فیلدهای مخفی nonce و action را به‌طور خودکار تولید می‌کند. برای مطالعه نحوه ساخت این صفحه، مطلب تابع add_options_page را ببینید.

ثبت تنظیم با show_in_rest برای گوتنبرگ

register_setting(
    'myplugin_settings',
    'myplugin_color',
    array(
        'type'              => 'string',
        'show_in_rest'      => true,
        'sanitize_callback' => 'sanitize_hex_color',
        'default'           => '#ffffff',
    )
);

الگوهای REST API در مطلب تابع register_rest_route پوشش داده شده است.

پاک‌سازی تنظیم در زمان غیرفعال‌سازی

اگر افزونه غیرفعال شد، ممکن است بخواهید تنظیمات پاک شوند. از تابع register_deactivation_hook استفاده کنید و در آن با تابع delete_option مقادیر را حذف کنید. برای فعال‌سازی، مطلب تابع register_activation_hook راهنماست.

ثبت تنظیمات یک Custom Post Type

گاهی تنظیمات به یک post type خاص مرتبط است. برای ثبت این نوع داده، مطلب تابع register_post_type را ببینید. برای تنظیمات سراسری، همان register_setting کافی است.

اشتباهات رایج در استفاده از register_setting

نبود sanitize_callback

شایع‌ترین اشتباه. بدون sanitize، هر ورودی از فرم به‌طور مستقیم در دیتابیس ذخیره می‌شود. این یعنی یک مهاجم می‌تواند کد HTML یا JavaScript را در تنظیمات ذخیره کند و در ادامه در همه صفحات سایت اجرا شود.

نبود capability check در فرم

تابع register_setting به‌خودی‌خود سطح دسترسی را بررسی نمی‌کند. فرمی که این تنظیمات را نمایش می‌دهد، باید با current_user_can( 'manage_options' ) محافظت شود. برای مطالعه کامل نقش‌ها، مطلب Capability و نقش‌های کاربری سفارشی را ببینید.

نبود nonce در فرم

تابع settings_fields() به‌طور خودکار nonce اضافه می‌کند، اما اگر فرم را دستی ساختید، باید خودتان این کار را انجام دهید. برای مطالعه جامع، مطلب Nonce در وردپرس را ببینید.

نادرستی نام option_group

اگر option_group در register_setting با مقدار settings_fields() یکسان نباشد، تنظیمات ذخیره نمی‌شوند. این اشتباه رایج در پروژه‌های با چند setting است.

ثبت تکراری یک setting

اگر یک setting را چند بار در افزونه‌های مختلف ثبت کنید، رفتار غیرمنتظره رخ می‌دهد. همیشه از نام‌گذاری پیشوندی مثل myplugin_ استفاده کنید تا تداخل رخ ندهد.

نبود بررسی خطا در ذخیره‌سازی

پس از ارسال فرم، اگر sanitize_callback مقداری را رد کند، کاربر متوجه نمی‌شود. بهتر است پیام موفقیت یا هشدار به‌صورت واضح نمایش داده شود. مطلب هوک admin_enqueue_scripts برای نمایش notice مفید است.

نبود تست روی سناریوهای مرزی

تست‌هایی مثل «ورودی خالی»، «ورودی نامعتبر»، «کاربر بدون دسترسی» و «همزمانی دو فرم» را حتماً بنویسید.

امنیت و عملکرد در register_setting

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

  • sanitize_callback مناسب با نوع داده
  • capability check در نمایش فرم
  • nonce در فرم (خودکار با settings_fields)
  • escape در نمایش مقادیر در HTML

برای مطالعه کامل اصول امنیتی، مطلب SQL Injection Prevention در وردپرس و راهنمای Sanitization مرجع هستند.

از نظر عملکرد، register_setting فقط یک بار در زمان اجرا ثبت می‌کند و به‌طور مستقیم کوئری اضافه ایجاد نمی‌کند. هزینه اصلی زمانی است که خود تنظیمات ذخیره یا خوانده می‌شوند:

  1. هر get_option در سایت باعث یک کوئری می‌شود مگر از cache استفاده کند
  2. اگر تنظیمات سنگین و پربازدید باشند، بهتر است در گروه autoload قرار بگیرند
  3. تنظیمات حساس نباید در لاگ‌ها ثبت شوند

برای مطالعه الگوهای بهینه‌سازی تنظیمات، مطلب بهینه‌سازی wp_options توصیه می‌شود.

پرسش‌های پرتکرار درباره register_setting

تفاوت register_setting با add_option چیست؟

register_setting() یک تنظیم را در Settings API ثبت می‌کند تا فرم‌های استاندارد بتوانند از آن استفاده کنند، درحالی‌که add_option() مستقیماً یک مقدار در دیتابیس درج می‌کند بدون منطق اعتبارسنجی.

آیا حتماً باید از register_setting استفاده کرد؟

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

آیا می‌توان یک setting را در چند گروه ثبت کرد؟

خیر، هر setting فقط به یک گروه تعلق دارد. اگر نیاز به گروه متفاوت دارید، باید setting جدید با نام متفاوت بسازید.

تفاوت type و sanitize_callback چیست؟

type در REST API و مستندسازی استفاده می‌شود، در حالی که sanitize_callback مسئول اعتبارسنجی و پاک‌سازی مقادیر ورودی است.

آیا در صورت نبود sanitize_callback چه اتفاقی می‌افتد؟

وردپرس برای typeهای ساده، یک sanitize پیش‌فرض اعمال می‌کند، اما این کافی نیست. توصیه قوی این است که همیشه sanitize_callback صریح تعریف کنید.

آیا می‌توان show_in_rest را در نسخه‌های قدیمی وردپرس استفاده کرد؟

خیر، این پارامتر از نسخه 4.7 اضافه شده است. اگر پروژه روی نسخه قدیمی اجرا می‌شود، این پارامتر نادیده گرفته می‌شود.

آیا register_setting روی قالب‌ها هم کاربرد دارد؟

بله، قالب‌ها هم می‌توانند از این تابع برای تنظیمات قالب استفاده کنند. مطلب Theme Customizer پیشرفته الگوی این کار را نشان می‌دهد.

آیا register_setting با Multisite سازگار است؟

در Multisite، این تابع به‌طور پیش‌فرض روی تنظیمات همان سایت اعمال می‌شود. برای تنظیمات سطح شبکه، از register_setting با گروه network یا توابع اختصاصی شبکه استفاده کنید. مطلب مدیریت Multisite وردپرس مفید است.

نگاه فنی عمیق به register_setting

در سطح معماری، register_setting() یک رکورد را در آرایه سراسری $wp_registered_settings ثبت می‌کند که در متغیر wp_registered_settings قابل دسترسی است. در نسخه‌های جدید وردپرس، این ساختار به یک کلاس WP_Settings منتقل شده اما API سطح بالا همان register_setting باقی مانده است.

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

نکته دوم، اثر show_in_rest است. اگر این پارامتر فعال باشد و type و sanitize_callback به‌درستی تعریف شده باشند، REST API به‌طور خودکار endpointهای استاندارد برای خواندن و نوشتن این تنظیم می‌سازد. اما اگر فقط یکی از این پارامترها ناقص باشد، endpoint با خطای 500 پاسخ می‌دهد.

مسئله سوم، مدیریت cache در ذخیره تنظیمات است. هر بار که یک option از طریق options.php ذخیره می‌شود، وردپرس به‌طور خودکار cache آن را پاک می‌کند. اما اگر مقدار از طریق update_option مستقیم ذخیره شود، این پاک‌سازی خودکار رخ نمی‌دهد و ممکن است مقدار قدیمی در cache باقی بماند.

در نهایت، در پروژه‌های Enterprise توصیه می‌شود یک کلاس Settings_Manager بسازید که مسئول ثبت، اعتبارسنجی و ذخیره تنظیمات باشد. این کار از پخش شدن منطق در فایل‌های مختلف جلوگیری می‌کند و تست‌پذیری را بالا می‌برد. برای مطالعه الگوهای ساختاری، مطلب توابع وردپرس برای گزینه‌های سایت و تابع register_meta مفید است. برای مطالعه بیشتر در مورد خود وردپرس، WordPress در ویکی‌پدیا نقطه شروع خوبی است.

اگر در پروژه‌ای با مشکل تداخل تنظیمات یا رفتار غیرمنتظره sanitize مواجه شده‌اید، برای ما جالب است بدانید کدام راهکار عملاً به حل مسئله کمک کرده است. تجربه خود را در دیدگاه‌ها بنویسید تا برای سایر توسعه‌دهندگان هم مفید باشد.