صفحهٔ تنظیمات اختصاصی، یکی از آن قابلیت‌هایی است که هر افزونهٔ جدی به آن نیاز پیدا می‌کند. تجربه‌ام این است که بیشتر تازه‌واردها، این کار را با HTML دستی و $_POST انجام می‌دهند و بعداً به دیوار امنیت و نگهداری می‌خورند. پاسخ درست، استفاده از Settings API وردپرس است — مکانیزم رسمی که مدیریت nonce، sanitize، و ذخیره در wp_options را خودکار انجام می‌دهد. این مقاله، گام‌به‌گام ساخت یک صفحهٔ تنظیمات حرفه‌ای را مرور می‌کند. اگر با مفاهیم پایه آشنا نیستید، افزونه وردپرس چیست و توسعهٔ افزونه از صفر را پیش از ادامه ببینید.

Settings API چیست و چرا؟

Settings API، مجموعه‌ای از توابع رسمی وردپرس برای مدیریت تنظیمات است. مزایا: یک — امنیت. nonce، sanitize و validate خودکار انجام می‌شود. دو — استاندارد. ذخیره در wp_options به‌صورت منظم. سه — ادغام با پیشخوان. ظاهر یکپارچه با بقیهٔ وردپرس. چهار — پایداری در آپدیت‌ها. حتی اگر وردپرس تغییرات ظاهری بدهد، API پایدار می‌ماند. تجربه‌ام: افزونه‌هایی که Settings API را رعایت نمی‌کنند، در ۹۰٪ موارد با آپدیت‌های بعدی وردپرس، به مشکل امنیتی یا ظاهری می‌خورند. راهنمای مفاهیم پایه در افزونه وردپرس چیست.

Settings API فقط یک الگوی برنامه‌نویسی نیست؛ مکانیزم رسمی وردپرس برای مدیریت امن تنظیمات است. اگر آن را دور بزنید، امنیت را دور زده‌اید.

گام اول، ثبت منو در پیشخوان:

function my_plugin_add_menu() {
    add_options_page(
        'تنظیمات افزونهٔ من',      // عنوان صفحه
        'افزونهٔ من',                 // عنوان منو
        'manage_options',             // capability لازم
        'my-plugin-settings',         // slug
        'my_plugin_render_settings'   // callback رندر
    );
}
add_action( 'admin_menu', 'my_plugin_add_menu' );

سه نکته: یک — از add_options_page برای زیرمنو در «تنظیمات» استفاده کنید یا از add_menu_page برای منوی اصلی. دو — capability باید manage_options باشد (فقط ادمین دسترسی دارد). سه — slug باید یکتا باشد تا با افزونه‌های دیگر تعارض نکند. الگوی دقیق در ساخت منوی مدیریتی وردپرس.

ثبت تنظیمات

گام دوم، ثبت تنظیمات با register_setting:

function my_plugin_register_settings() {
    register_setting(
        'my_plugin_group',              // گروه تنظیمات
        'my_plugin_settings',           // نام option در wp_options
        array(
            'type'              => 'array',
            'sanitize_callback' => 'my_plugin_sanitize',
            'default'           => array(),
        )
    );
}
add_action( 'admin_init', 'my_plugin_register_settings' );

نکتهٔ کلیدی: پارامتر sanitize_callback الزامی است. بدون آن، وردپرس داده را بدون فیلتر ذخیره می‌کند — خطر امنیتی جدی. الگوی sanitize را در بخش مربوطه می‌بینیم. راهنمای کامل در پاک‌سازی داده‌ها و اعتبارسنجی داده‌ها.

افزودن فیلدها

گام سوم، افزودن بخش‌ها و فیلدها:

function my_plugin_add_fields() {
    // افزودن بخش
    add_settings_section(
        'my_plugin_general',            // id بخش
        'تنظیمات عمومی',                 // عنوان
        'my_plugin_section_callback',   // callback توضیح
        'my-plugin-settings'            // صفحه
    );

    // فیلد متن
    add_settings_field(
        'api_key',
        'کلید API',
        'my_plugin_field_text',
        'my-plugin-settings',
        'my_plugin_general',
        array( 'label_for' => 'api_key' )
    );

    // فیلد چک‌باکس
    add_settings_field(
        'enable_feature',
        'فعال‌سازی قابلیت',
        'my_plugin_field_checkbox',
        'my-plugin-settings',
        'my_plugin_general'
    );
}
add_action( 'admin_init', 'my_plugin_add_fields' );

الگوی callback برای فیلدها:

function my_plugin_field_text( $args ) {
    $options = get_option( 'my_plugin_settings', array() );
    $value = isset( $options[ $args['label_for'] ] ) ? $options[ $args['label_for'] ] : '';
    echo '<input type="text" id="' . esc_attr( $args['label_for'] ) . '" ';
    echo 'name="my_plugin_settings[' . esc_attr( $args['label_for'] ) . ']" ';
    echo 'value="' . esc_attr( $value ) . '" class="regular-text" />';
}

الگوی esc_attr روی مقدار، الزامی است. بدون آن، خطر XSS. راهنما در نوشتن PHP امن در وردپرس.

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

گام چهارم، رندر صفحه با فرم استاندارد:

function my_plugin_render_settings() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    <div class="wrap">
        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
        <form action="options.php" method="post">
            <?php
            settings_fields( 'my_plugin_group' );  // nonce و action
            do_settings_sections( 'my-plugin-settings' );  // بخش‌ها و فیلدها
            submit_button();
            ?>
        </form>
    </div>
    <?php
}

نکته: تابع settings_fields، مکانیزم nonce و action وردپرس را می‌سازد. بدون آن، فرم قابل ارسال نیست. تجربه‌ام: توسعه‌دهندگانی که فرم دستی می‌سازند، غیر از امنیت، با مشکلات کشف‌نشده‌ای مثل ناهماهنگی در redirect و پیام «تنظیمات ذخیره شد» روبرو می‌شوند.

Sanitize و اعتبارسنجی

تابع sanitize، قلب امنیت صفحهٔ تنظیمات است:

function my_plugin_sanitize( $input ) {
    $clean = array();
    if ( isset( $input['api_key'] ) ) {
        $clean['api_key'] = sanitize_text_field( $input['api_key'] );
    }
    if ( isset( $input['enable_feature'] ) ) {
        $clean['enable_feature'] = (bool) $input['enable_feature'];
    }
    if ( isset( $input['email'] ) ) {
        $clean['email'] = sanitize_email( $input['email'] );
    }
    if ( isset( $input['url'] ) ) {
        $clean['url'] = esc_url_raw( $input['url'] );
    }
    return $clean;
}

هر فیلد، sanitize مناسب خودش را دارد: متن: sanitize_text_field؛ ایمیل: sanitize_email؛ URL: esc_url_raw؛ عدد: absint؛ HTML مجاز: wp_kses_post. راهنمای کامل در پاک‌سازی داده‌ها.

خواندن مقادیر ذخیره‌شده

خواندن مقادیر در کد افزونه:

$options = get_option( 'my_plugin_settings', array() );
$api_key = isset( $options['api_key'] ) ? $options['api_key'] : '';
$enable = ! empty( $options['enable_feature'] );

نکته: همیشه مقدار پیش‌فرض را در get_option بدهید. بدون آن، در اولین اجرا undefined index می‌گیرید. الگوی خواندن در front-end و پیشخوان یکسان است؛ فقط در front-end باید به guard دسترسی‌ها توجه کنید.

چند تب و بخش

برای پروژه‌های بزرگ، صفحهٔ تنظیمات با تب‌ها UX بهتری می‌دهد. الگوی ساده: از پارامتر tab در URL استفاده کنید و در هر تب، بخش‌های مربوطه را با add_settings_section جدا کنید. الگوی دقیق در ساخت منوی مدیریتی. تجربه‌ام: صفحهٔ تنظیمات با تب‌ها، در افزونه‌های با بیش از ۱۰ گزینه، استفادهٔ روزمره را ساده‌تر می‌کند.

الگوهای پیشرفته

برای توسعه‌دهنده‌های سطح بالا، سه الگوی پیشرفته: یک — کلاس‌محور. به‌جای توابع سراسری، یک کلاس Settings_Page بسازید که تمام منطق ثبت، رندر و sanitize را در خود نگه دارد. دو — Customizer API. برای تنظیمات ظاهری قالب، به‌جای صفحهٔ تنظیمات، از Customizer استفاده کنید که پیش‌نمایش زنده دارد. سه — REST API برای تنظیمات. در پروژه‌های Headless، تنظیمات را از طریق REST endpoint مدیریت کنید. تجربه‌ام: در پروژه‌های بزرگ، ترکیب این سه الگو، نگهداری را به‌شدت ساده می‌کند. الگوی REST در استفاده از REST API و ساخت API اختصاصی برای وردپرس. نکته: صفحات تنظیمات را در یک متاباکس یا تب جدا نگه دارید و از ارسال داده‌های نامرتبط در همان فرم خودداری کنید — این جداسازی، در بازبینی‌های امنیتی کمک مهمی است.

اشتباهات رایج

  • نبود sanitize_callback: خطر امنیتی جدی.
  • نبود escape در خروجی: خطر XSS — PHP امن.
  • نبود check_user_can: دسترسی غیرمجاز.
  • نبود nonce: در فرم دستی، خطر CSRF.
  • ذخیرهٔ هر فیلد در option جدا: بجای یک آرایه، حجم wp_options را بالا می‌برد.
  • نبود مقدار پیش‌فرض در get_option: خطای undefined index.
  • sanitize ناقص: فقط بعضی فیلدها sanitize، بقیه بدون فیلتر. باید هر فیلد، sanitize مناسب خودش را داشته باشد.
  • نادیده‌گرفتن capability خاص: در بعضی پروژه‌ها، تنظیمات باید به نقش خاصی محدود شود، نه فقط ادمین.

جمع‌بندی

ساخت صفحهٔ تنظیمات در وردپرس، پنج گام دارد: ثبت منو، ثبت تنظیمات، افزودن فیلدها، رندر فرم و sanitize. اگر امروز فقط یک کار می‌کنید: در افزونه یا قالب فعلی خودتان، اگر صفحهٔ تنظیمات دستی دارید، آن را به Settings API منتقل کنید. همین انتقال، امنیت و پایداری را چند برابر می‌کند. تجربهٔ خودتان از ساخت صفحهٔ تنظیمات، در دیدگاه‌ها ارزشمند است. ⚙️