Options API یکی از پایه‌ای‌ترین مکانیزم‌های وردپرس برای ذخیره‌سازی داده است. هر افزونه یا قالب جدی، حداقل یک ردیف در جدول wp_options دارد. آنچه این مکانیزم را از یک فایل متنی ساده جدا می‌کند، ساختار استاندارد، کش داخلی، و قابلیت autoload است. در سال‌ها کار با وردپرس، دیده‌ام پروژه‌هایی که از Options API به‌درستی استفاده نمی‌کنند، در بلندمدت با کندی دیتابیس، حجم زیاد جدول options، و مشکلات امنیتی دست‌و‌پنجه نرم می‌کنند. در مقابل، پروژه‌هایی که این API را درست به‌کار می‌گیرند، سال‌ها بدون درد نگه‌داری می‌شوند. این مقاله، کار با Options API را از پایه تا الگوهای حرفه‌ای مرور می‌کند. برای درک پیش‌نیازها، افزونه وردپرس چیست، هوک‌های وردپرس، و ساخت صفحهٔ تنظیمات را پیش از ادامه ببینید.

Options API چیست و چه زمانی به‌کار می‌آید؟

Options API مکانیزم استاندارد وردپرس برای ذخیره‌سازی داده‌های ساختاری نامنظم (key-value) در جدول wp_options است. سه ستون اصلی این جدول: option_name (کلید یکتا)، option_value (مقدار، سریالایز‌شده در صورت لزوم)، و autoload (تعیین‌کننده بارگذاری خودکار). چهار سناریوی اصلی برای Options API: یک — تنظیمات افزونه. مثال: کلید API، سطح فعال‌سازی، حالت‌های پیش‌فرض. دو — تنظیمات قالب. مثال: رنگ‌بندی، فونت، چیدمان پیش‌فرض. سه — داده‌های سراسری سایت. مثال: آمار، لاگ، کش ساده. چهار — پرچم‌های وضعیت. مثال: نسخهٔ دیتابیس، تنظیمات نصب. تفاوت Options API با ترنزینت‌ها را در ترنزینت‌ها در وردپرس، با متادیتای پست در کار با متاباکس‌ها، و با User Meta در کار با User Meta آورده‌ام.

Options API مکانیزم ذخیره‌سازی دادهٔ سراسری در وردپرس است؛ نه جای دادهٔ کاربر، نه جای متادیتای پست. تفکیک این سه، اولین گام معماری دادهٔ درست است.

چهار عمل اصلی: افزودن، خواندن، به‌روزرسانی، حذف

چهار تابع پایه در Options API:

// افزودن
add_option( 'my_plugin_api_key', 'abc123', '', 'yes' );

// خواندن
$api_key = get_option( 'my_plugin_api_key', 'پیش‌فرض' );

// به‌روزرسانی
update_option( 'my_plugin_api_key', 'xyz789' );

// حذف
delete_option( 'my_plugin_api_key' );

نکته‌ها: یک — get_option با مقدار پیش‌فرض: همیشه پارامتر دوم را بگذارید. بدون آن، اگر option وجود نداشت، false برمی‌گرداند و نمی‌توان تفاوت «نبود» با «مقدار false» را تشخیص داد. دو — add_option در برابر update_option: add_option فقط اگر option وجود ندارد اضافه می‌کند؛ update_option اگر وجود دارد به‌روزرسانی و اگر ندارد اضافه می‌کند. برای امنیت بیشتر، در فعال‌سازی افزونه از add_option استفاده کنید و در عملیات روزمره از update_option. سه — پیشوند یکتا: تمام کلیدهای option باید با پیشوند افزونه شروع شوند تا تعارض با افزونه‌های دیگر رخ ندهد. چهار — delete_option هنگام غیرفعال‌سازی: نه همیشه. اگر کاربر افزونه را موقتاً غیرفعال می‌کند، احتمالاً نمی‌خواهید تنظیمات از دست برود. حذف فقط در uninstall.php. راهنمای کامل توابع در توابع وردپرس برای گزینه‌های سایت و توابع متادیتا.

سه تابع مکمل که گاهی به‌کار می‌آیند: یک — update_site_option: در مالتی‌سایت، برای ذخیرهٔ در سطح شبکه. دو — get_site_option: متناظر خواندن. سه — delete_site_option: حذف در سطح شبکه. تفاوت این‌ها با نسخه‌های معمول در بخش مالتی‌سایت همین مقاله می‌آید.

autoload و تأثیر آن بر عملکرد

ستون autoload در جدول wp_options، یکی از کم‌توجه‌شده‌ترین و در عین حال مهم‌ترین فیلدهای وردپرس است. سه مقدار ممکن: یک — yes: option به‌طور خودکار در هر بازدید بارگذاری می‌شود. مناسب optionهایی که تقریباً همیشه به‌کار می‌آیند (تنظیمات پایهٔ سایت، تنظیمات افزونه‌های فعال). دو — no: option فقط وقتی لود می‌شود که صریحاً get_option فراخوانی شود. مناسب optionهای سنگین یا کم‌مصرف. سه — auto-on یا auto-off: در نسخه‌های اخیر وردپرس، رفتار پویا.

// با autoload = yes
add_option( 'my_plugin_settings', $data, '', 'yes' );

// با autoload = no (پیشنهاد برای داده‌های سنگین)
add_option( 'my_plugin_large_cache', $data, '', 'no' );

نکتهٔ کلیدی: هر option با autoload = yes، در هر بازدید لود می‌شود — حتی اگر در آن درخواست به‌کار نرود. اگر ۵۰ option با autoload=yes در سایت وجود داشته باشد و هرکدام ۵ کیلوبایت باشد، ۲۵۰ کیلوبایت به هر ریکوئست اضافه می‌شود. تجربهٔ میدانی من در سایت‌های قدیمی: گاهی ده‌ها مگابایت option با autoload=yes دیده‌ام که نتیجه‌اش کندی مزمن سایت و فشار روی دیتابیس بوده. روش بررسی: با یک کوئری ساده در phpMyAdmin:

SELECT SUM( LENGTH( option_value ) ) AS total_size
FROM wp_options
WHERE autoload = 'yes';

اگر عدد بالای ۱ مگابایت است، زمان بازبینی رسیده. راهنمای تکمیلی در بهینه‌سازی کوئری‌های MySQL، پاک‌سازی دیتابیس وردپرس، و بهینه‌سازی کد وردپرس.

یک نکتهٔ عملی دربارهٔ تغییر autoload روی option موجود:

// روش استاندارد - حذف و افزودن مجدد
$value = get_option( 'my_plugin_large_data' );
delete_option( 'my_plugin_large_data' );
add_option( 'my_plugin_large_data', $value, '', 'no' );

این الگو در نسخه‌های قدیمی وردپرس، تنها راه امن است. در نسخه‌های جدید، می‌توانید با WP-CLI هم این کار را انجام دهید: wp option update my_plugin_large_data --autoload=no.

ذخیرهٔ داده‌های ساختاریافته

Options API به‌طور خودکار آرایه و آبجکت را سریالایز می‌کند. سه الگوی متداول: یک — آرایهٔ تنظیمات:

$settings = array(
    'api_key'    => 'abc123',
    'debug'      => true,
    'retry_max'  => 3,
    'cache_ttl'  => 3600,
);
update_option( 'my_plugin_settings', $settings );

// خواندن
$settings = get_option( 'my_plugin_settings', array() );
$api_key  = $settings['api_key'] ?? '';
$debug    = ! empty( $settings['debug'] );

مزیت این الگو: تمام تنظیمات در یک ردیف از جدول. بدون آن، هر تنظیم یک ردیف جداگانه می‌شد که جدول را شلوغ می‌کرد. دو — آبجکت: مشابه آرایه، ولی توصیه نمی‌شود چون در خواندن و به‌روزرسانی، تبدیل‌های اضافه لازم دارد. سه — دادهٔ عددی بزرگ: برای آمار یا لاگ، از نوع دادهٔ مناسب استفاده کنید:

$stats = array(
    'total_visits'    => 125000,
    'unique_visitors' => 45000,
    'last_reset'      => time(),
);
update_option( 'my_plugin_stats', $stats );

نکته: داده‌های حجیم (بیش از چند صد کیلوبایت) را در Options API نگه ندارید. برای این موارد، یا از کش اختصاصی (Redis/Memcached) استفاده کنید، یا از جداول اختصاصی دیتابیس. راهنمای انتخاب در تأثیر دیتابیس بر سرعت سایت. یک قاعدهٔ عملی: اگر داده‌ای قرار است بیشتر از ۱۰۰ کیلوبایت باشد، بهتر است از Options API خارج شود. دلیل: هر بار که option با autoload=yes لود می‌شود، تمام محتوایش در حافظه قرار می‌گیرد، حتی اگر آن درخواست به بخشی از آن نیاز نداشته باشد.

رابطه با Settings API

Settings API، لایه‌ای بالاتر از Options API است که مدیریت فرم، nonce، sanitize و ذخیره را خودکار انجام می‌دهد. تفاوت بنیادین: Options API — ذخیره‌سازی سطح پایین. Settings API — رابط مدیریت فرم که در پشت صحنه از Options API استفاده می‌کند. الگوی ترکیبی:

function my_plugin_register_settings() {
    register_setting(
        'my_plugin_group',
        'my_plugin_options',
        array(
            'type'              => 'array',
            'sanitize_callback' => 'my_plugin_sanitize',
            'default'           => array(),
            'autoload'          => 'yes',
        )
    );
}
add_action( 'admin_init', 'my_plugin_register_settings' );

پارامتر autoload در register_setting از وردپرس ۴.۲ به بعد پشتیبانی می‌شود. مزیت این الگو: فرم مدیریت، nonce و sanitize خودکار. در پس‌زمینه، ذخیره در wp_options انجام می‌شود. راهنمای کامل در ساخت صفحهٔ تنظیمات اختصاصی و پاک‌سازی داده‌ها. یک نکتهٔ مهم: برای تنظیمات پیچیده با چند فیلد، همیشه Settings API را انتخاب کنید. Options API تنها برای ذخیره‌سازی داخلی و بدون فرم مدیریتی، انتخاب درست است. تجربه‌های میدانی من در این مورد: در پروژه‌ای که فرم تنظیمات به‌صورت دستی ساخته شده بود، سرعت باگ‌گیری در nonce و sanitize چند روز طول کشید؛ بازنویسی با Settings API در چند ساعت انجام شد.

Options API در مالتی‌سایت

در وردپرس مالتی‌سایت، دو سطح ذخیره‌سازی وجود دارد: یک — سطح سایت (blog): هر سایت، جدول wp_options خودش را دارد. توابع get_option، add_option، update_option، delete_option در این سطح کار می‌کنند. دو — سطح شبکه: تمام شبکه، جدول wp_sitemeta دارد. برای این سطح، از توابع get_site_option، add_site_option، update_site_option، delete_site_option استفاده می‌شود.

// ذخیرهٔ تنظیمات در سطح شبکه
update_site_option( 'my_plugin_network_settings', array(
    'enabled' => true,
    'version' => '1.0.0',
) );

// خواندن
$settings = get_site_option( 'my_plugin_network_settings', array() );

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

کش و بازیابی سریع

Options API دو لایهٔ کش داخلی دارد: یک — کش حافظه‌ای درخواست (request-level cache): در هر درخواست، اولین get_option کوئری می‌زند، ولی فراخوانی‌های بعدی همان option، از حافظه خوانده می‌شوند. دو — کش سراسری با object cache: اگر Redis یا Memcached فعال باشد، optionها در کش سراسری ذخیره می‌شوند و بین درخواست‌ها به اشتراک گذاشته می‌شوند.

// در wp-config.php
// برای فعال‌سازی object cache با Redis
// نیاز به نصب Redis و افزونهٔ Redis Object Cache

نکته: با فعال‌سازی object cache، تعداد کوئری‌های wp_options به‌طور چشمگیری کاهش می‌یابد. تجربه‌های میدانی من در پروژه‌های پرترافیک: با Redis، تعداد کوئری‌های دیتابیس از چند صد به زیر بیست در هر ریکوئست رسیده. راهنمای کامل کش در افزونه‌های کش وردپرس و بهینه‌سازی کد وردپرس. یک الگوی تکمیلی: در کد افزونه، پس از update_option، در صورت وجود کش اختصاصی، آن را هم باطل کنید. اگر از wp_cache_delete استفاده می‌کنید، حتماً در متد save نیز فراخوانی کنید.

پاک‌سازی و مدیریت حجم

با گذشت زمان، جدول wp_options ممکن است با ردیف‌های بی‌استفاده پر شود. سه منبع رایج: یک — افزونه‌های حذف‌شده که uninstall.php نداشته‌اند. دو — optionهای موقت که پاک نشده‌اند. سه — ترنزینت‌های منقضی. دو ابزار پاک‌سازی: یک — پاک‌سازی دستی با phpMyAdmin:

-- یافتن optionهای بزرگ
SELECT option_name, LENGTH(option_value) AS size
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC
LIMIT 30;

دو — پاک‌سازی با افزونه: افزونه‌هایی مثل WP-Optimize یا Advanced Database Cleaner امکان پاک‌سازی هدفمند optionها، ترنزینت‌ها و ردیف‌های بی‌استفاده را می‌دهند. راهنمای کامل در پاک‌سازی دیتابیس وردپرس، افزونه‌های بهینه‌سازی دیتابیس، و پاک‌سازی ترنزینت‌ها. یک نکتهٔ مهم: پیش از هر پاک‌سازی، بکاپ کامل از دیتابیس بگیرید. راهنمای بکاپ در بکاپ دیتابیس و افزونه‌های بکاپ. در حذف option، مطمئن شوید که آن option متعلق به افزونهٔ فعال است؛ حذف اشتباهی option یک افزونهٔ فعال می‌تواند سایت را بشکند.

ساختار کلاس‌محور

در افزونه‌های جدی، منطق Options API در یک کلاس اختصاصی نگه داشته می‌شود:

class My_Plugin_Settings {
    const OPTION_KEY = 'my_plugin_settings';
    const DEFAULTS   = array(
        'api_key'   => '',
        'debug'     => false,
        'cache_ttl' => 3600,
    );

    public static function init() {
        add_action( 'admin_init', array( __CLASS__, 'register' ) );
    }

    public static function get( $key = null ) {
        $settings = get_option( self::OPTION_KEY, self::DEFAULTS );
        if ( ! is_array( $settings ) ) {
            $settings = self::DEFAULTS;
        }
        $settings = wp_parse_args( $settings, self::DEFAULTS );

        return $key ? ( $settings[ $key ] ?? null ) : $settings;
    }

    public static function update( $key, $value ) {
        $settings = self::get();
        $settings[ $key ] = $value;
        return update_option( self::OPTION_KEY, $settings );
    }

    public static function delete_all() {
        return delete_option( self::OPTION_KEY );
    }

    public static function register() {
        register_setting(
            'my_plugin_group',
            self::OPTION_KEY,
            array(
                'type'              => 'array',
                'sanitize_callback' => array( __CLASS__, 'sanitize' ),
                'default'           => self::DEFAULTS,
            )
        );
    }

    public static function sanitize( $input ) {
        $clean = self::DEFAULTS;
        if ( isset( $input['api_key'] ) ) {
            $clean['api_key'] = sanitize_text_field( $input['api_key'] );
        }
        if ( isset( $input['debug'] ) ) {
            $clean['debug'] = (bool) $input['debug'];
        }
        if ( isset( $input['cache_ttl'] ) ) {
            $clean['cache_ttl'] = absint( $input['cache_ttl'] );
        }
        return $clean;
    }
}
My_Plugin_Settings::init();

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

امنیت در Options API

چهار قاعدهٔ الزامی: یک — پاک‌سازی پیش از ذخیره. هر مقدار بسته به نوع خود. دو — escape در نمایش. esc_html، esc_attr، esc_url. سه — نانس در فرم‌های مدیریت. اگر از Settings API استفاده می‌کنید، خودکار. اگر فرم دستی دارید، نانس الزامی است. چهار — محدودسازی دسترسی. توابعی که option را به‌روزرسانی می‌کنند، باید در فرم‌های مدیریت و با capability مناسب محافظت شوند. نمونهٔ نانس دستی:

// در فرم
<?php wp_nonce_field( 'my_plugin_save', 'my_plugin_nonce' ); ?>

// در پردازش
if ( ! isset( $_POST['my_plugin_nonce'] ) ) {
    return;
}
if ( ! wp_verify_nonce( $_POST['my_plugin_nonce'], 'my_plugin_save' ) ) {
    return;
}
if ( ! current_user_can( 'manage_options' ) ) {
    return;
}

راهنمای کامل در PHP امن در وردپرس، نانس وردپرس، پاک‌سازی داده‌ها، و اعتبارسنجی داده‌ها. یک آسیب‌پذیری شایع که در پرونده‌های امنیتی دیده‌ام: endpointهای AJAX که optionهای حساس را بدون check_ajax_referer به‌روزرسانی می‌کردند. نتیجه: هر کاربری می‌توانست با یک درخواست ساختگی، تنظیمات افزونه را تغییر دهد. هیچ endpoint AJAX در Options API نباید از این بررسی معاف باشد. راهنمای امنیت پروژه در امنیت پروژه وردپرس و امنیت وردپرس برای مبتدیان.

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

سه الگوی پیشرفته در کار با Options API: یک — Version Tracking. ذخیرهٔ نسخهٔ دیتابیس و اجرای مهاجرت‌ها در فعال‌سازی:

function my_plugin_maybe_upgrade() {
    $current_version = get_option( 'my_plugin_db_version', '0.0.0' );
    $target_version  = '1.2.0';

    if ( version_compare( $current_version, $target_version, '>=' ) ) {
        return;
    }

    // مهاجرت از 0.0.0 به 1.2.0
    if ( version_compare( $current_version, '1.0.0', '<' ) ) {
        // مهاجرت به 1.0.0
    }
    if ( version_compare( $current_version, '1.2.0', '<' ) ) {
        // مهاجرت به 1.2.0
    }

    update_option( 'my_plugin_db_version', $target_version );
}
add_action( 'plugins_loaded', 'my_plugin_maybe_upgrade' );

دو — Options as Feature Flags. استفاده از option برای فعال/غیرفعال‌سازی قابلیت‌ها بدون انتشار نسخهٔ جدید:

if ( get_option( 'my_plugin_enable_new_editor', false ) ) {
    // نسخهٔ جدید
} else {
    // نسخهٔ قدیم
}

سه — Options Backup and Restore. در پروژه‌های مشتری‌محور، امکان Export/Import تنظیمات:

// Export
$settings = get_option( 'my_plugin_settings' );
header( 'Content-Type: application/json' );
header( 'Content-Disposition: attachment; filename="my-plugin-settings.json"' );
echo wp_json_encode( $settings );
exit;

// Import
$json = file_get_contents( $_FILES['settings_file']['tmp_name'] );
$data = json_decode( $json, true );
if ( is_array( $data ) ) {
    update_option( 'my_plugin_settings', $data );
}

راهنمای الگوهای مشابه در اتصال وردپرس به سرویس‌های خارجی و بهینه‌سازی کد وردپرس. یک الگوی تکمیلی برای پروژه‌های پرترافیک: ذخیرهٔ optionهای حساس را با wp_cache_set کش کنید و در update_option، هم کش و هم option را به‌روزرسانی کنید. این دوگانه‌سازی، در تجربهٔ من، بار دیتابیس را در سایت‌های پرمصرف تا ۴۰٪ کاهش داده است.

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

Options API، یکی از پایه‌ای‌ترین و در دسترس‌ترین مکانیزم‌های ذخیره‌سازی در وردپرس است. مسیر حرفه‌ای کار با آن، هفت گام دارد: چهار تابع اصلی، توجه به autoload، انتخاب صحیح نوع داده، ترکیب هوشمندانه با Settings API، مدیریت مالتی‌سایت، کش اختصاصی، و ساختار کلاس‌محور. اگر امروز یک کار در این مسیر انجام می‌دهید: به جدول wp_options نگاه کنید و ببینید کدام optionهای autoload=yes بیش از ۱۰۰ کیلوبایت حجم دارند؛ انتقال آن‌ها به autoload=no یا کش اختصاصی، ممکن است به‌تنهایی سرعت سایت را محسوس کند. اگر تجربه‌ای از یک option سنگین یا مشکل autoload در پروژهٔ خود دارید، در دیدگاه‌ها بنویسید؛ همان گزارش‌های واقعی، این راهنما را دقیق‌تر می‌کند. ⚙️