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

تفاوت ذهنی افزونهٔ آماتور و حرفه‌ای

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

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

ساختار پوشه‌ها و فایل‌ها

ساختاری که در پروژه‌های خودم استاندارد کرده‌ام:

my-plugin/
├── my-plugin.php        (فایل اصلی با هدر)
├── uninstall.php        (عملیات حذف امن)
├── readme.txt           (توضیحات و changelog)
├── includes/
│   ├── class-plugin.php
│   ├── class-admin.php
│   └── class-frontend.php
├── admin/
│   └── views/
├── assets/
│   ├── css/
│   ├── js/
│   └── images/
├── languages/
└── vendor/

سه نکته: اول، فایل اصلی فقط نقش bootstrap دارد و کار اصلی به کلاس‌ها منتقل می‌شود. دوم، پوشهٔ includes/ کلاس‌های اصلی را نگه می‌دارد. سوم، پوشهٔ assets/ برای فایل‌های استاتیک است — همان استانداردی که در نحوه استفاده صحیح از هوک‌های وردپرس هم توصیه کرده‌ام.

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

/*
Plugin Name: My Custom Plugin
Plugin URI: https://example.com/my-plugin
Description: توضیح مختصر از کارکرد افزونه
Version: 1.0.0
Requires at least: 6.0
Requires PHP: 8.0
Author: Your Name
License: GPL-2.0-or-later
Text Domain: my-plugin
Domain Path: /languages
*/

سه فیلد حیاتی: Version که در آپدیت‌ها تغییر می‌کند، Requires PHP که سازگاری را مشخص می‌کند، و Text Domain که برای ترجمه ضروری است.

معماری کلاس‌محور در برابر تابع‌محور

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

class My_Plugin {
    private static $instance = null;
    
    public static function get_instance() {
        if ( self::$instance === null ) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        $this->load_dependencies();
        $this->register_hooks();
    }
    
    private function register_hooks() {
        add_action( 'init', array( $this, 'init' ) );
    }
}

الگوی Singleton (تک‌نمونه) در پروژه‌های افزونه‌نویسی زیاد استفاده می‌شود، ولی باید بدانید که معایب هم دارد. برای مطالعهٔ عمیق‌تر، توابع وردپرس چیست و چگونه از آن‌ها استفاده کنیم؟ و نحوه استفاده از توابع وردپرس در پروژه‌ها.

هوک‌ها: پایهٔ ارتباط با وردپرس

افزونهٔ حرفه‌ای، از هوک‌ها استفاده می‌کند، نه از دستکاری هسته. سه الگوی اصلی: Action برای افزودن رفتار، Filter برای تغییر مقدار، و هوک‌های سفارشی برای اینکه دیگران بتوانند افزونهٔ شما را توسعه دهند. اگر با تفاوت این دو آشنا نیستید، هوک‌های وردپرس چیستند و چگونه کار می‌کنند؟ و تفاوت Action و Filter در وردپرس چیست و نحوه استفاده از add_action در وردپرس.

یک اصل مهم: در افزونهٔ حرفه‌ای، حداقل یک هوک عمومی برای رخدادهای اصلی افزونه تعریف کنید. مثال: do_action( 'my_plugin_after_save', $order_id ). این کار، افزونهٔ شما را در اکوسیستم قابل‌توسعه می‌کند.

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

صفحهٔ تنظیمات، یکی از نقطه‌های حساس امنیتی است. مسیر کامل در ساخت صفحه تنظیمات اختصاصی در وردپرس. سه اصل: اول، از Settings API رسمی وردپرس استفاده کنید، نه فرم HTML خام. دوم، همهٔ ورودی‌ها را با sanitize_callback پاک‌سازی کنید. سوم، خروجی‌ها را با esc_html، esc_attr و esc_url امن کنید. مسیرهای تکمیلی در پاک‌سازی داده‌ها در کدنویسی وردپرس و اعتبارسنجی داده‌ها در کدنویسی وردپرس و نوشتن کد PHP امن برای وردپرس.

امنیت: شش اصل غیرقابل مذاکره

  1. Nonce برای همهٔ فرم‌ها: هر فرم باید با wp_nonce_field و wp_verify_nonce محافظت شود — مسیر کامل در نانس وردپرس و نقش آن در امنیت فرم‌ها.
  2. Sanitization ورودی‌ها: هر ورودی، قبل از ذخیره پاک‌سازی شود.
  3. Escaping خروجی‌ها: هر خروجی، قبل از نمایش امن شود.
  4. بررسی دسترسی کاربران: با current_user_can، قبل از هر عملیات حساس.
  5. اجتناب از توابع خطرناک: eval، exec، system — هرگز.
  6. پایش به‌روزرسانی کتابخانه‌های وابسته: اگر از Composer استفاده می‌کنید، کتابخانه‌های وابسته را منظم چک کنید.

آماده‌سازی برای ترجمه

افزونهٔ حرفه‌ای، برای ترجمه آماده است. تمام رشته‌های متنی باید با توابع ترجمه نوشته شوند:

__( 'متن ترجمه‌پذیر', 'my-plugin' )
_e( 'متن چاپی', 'my-plugin' )
esc_html__( 'متن امن', 'my-plugin' )

و در فایل اصلی، بارگذاری دامنهٔ ترجمه:

load_plugin_textdomain(
    'my-plugin',
    false,
    dirname( plugin_basename( __FILE__ ) ) . '/languages'
);

مدیریت CSS و JS

دو قانون طلایی: اول، فایل‌ها را فقط در صفحاتی که نیاز دارند بارگذاری کنید. هرگز CSS و JS افزونه را در همهٔ صفحات پیشخوان یا فرانت‌اند بار نکنید. برای این کار، شرط‌های صفحه را قبل از enqueue چک کنید. دوم، از wp_enqueue_style و wp_enqueue_script استفاده کنید، نه تگ‌های مستقیم. مسیر دقیق در افزونه‌های وردپرس چطور روی سرعت سایت اثر می‌گذارند؟.

حذف افزونه: پروتکل نهایی

افزونهٔ حرفه‌ای، هنگام حذف، همهٔ آثار خود را پاک می‌کند. فایل uninstall.php برای این کار است:

if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    exit;
}

delete_option( 'my_plugin_settings' );
// و پاک‌سازی جدول‌های اختصاصی در صورت وجود

یک نکتهٔ ظریف: بعضی از افزونه‌ها به کاربر اجازه می‌دهند انتخاب کند که داده‌ها هنگام حذف پاک شوند یا نه. این گزینه، در افزونه‌های حرفه‌ای رعایت می‌شود چون داده‌های کاربر ممکن است برای بازگشت نیاز باشد.

جدول چک‌لیست انتشار افزونه

دستهاقدامضروری؟
ساختارپوشه‌بندی استانداردبله
ساختارهدر افزونه با تمام فیلدهابله
معماریکلاس‌محور یا حداقل پیشوند یکتاتوصیه
امنیتNonce در همهٔ فرم‌هابله
امنیتSanitize و Escapeبله
ترجمهText Domain و توابع i18nتوصیه
سرعتبارگذاری مشروط CSS/JSبله
پاک‌سازیuninstall.phpبله
مستنداتreadme.txt با changelogبله

نگاه معمارانه به افزونه به‌عنوان یک محصول

برای توسعه‌دهندهٔ ارشد، افزونه فقط یک تکه کد نیست؛ یک محصول است که چرخهٔ عمر دارد. سه اصل که در پروژه‌های حرفه‌ای رعایت می‌کنم:

اصل اول — نسخه‌بندی معنادار. هر تغییر، با نسخهٔ جدید و توضیح در readme.txt. نسخه‌بندی Semantic (Major.Minor.Patch) به کاربران می‌گوید آپدیت چقدر مهم است.

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

اصل سوم — برنامهٔ خروج. اگر کاربری بخواهد افزونهٔ شما را ترک کند، نباید داده‌هایش گروگان بماند. حتی اگر افزونهٔ شما جدول اختصاصی دارد، مسیر Export داده‌ها را فراهم کنید.

یک نکتهٔ عملی که در چند پروژهٔ افزونه‌نویسی به آن رسیده‌ام: اگر می‌خواهید افزونه‌تان به مخزن رسمی وردپرس برود، قبل از ارسال، تمام استانداردهای کدنویسی را رعایت کنید. ابزارهای بررسی خودکار مثل PHPCS با استاندارد WordPress، این کار را ساده می‌کنند. مسیر کامل در استانداردهای کدنویسی وردپرس چیست و استفاده از WordPress Coding Standards در پروژه‌ها. در نهایت، توسعهٔ افزونهٔ حرفه‌ای، ترکیبی است از دانش فنی، توجه به جزئیات و نگاه بلندمدت به محصول. اگر این سه با هم باشند، افزونه‌ای می‌سازید که سال‌ها بدون دردسر کار می‌کند — چیزی که در پروژه‌های تجاری، از هر ویژگی جدید، ارزشمندتر است.

اگر افزونه‌ای برای وردپرس نوشته‌اید — چه منتشر شده و چه داخلی — سناریو را در دیدگاه بنویسید. چالش‌های فنی که در مسیر ساخت افزونه تجربه کرده‌اید، برای توسعه‌دهنده‌های بعدی ارزشمندتر از هر راهنمای عمومی است. 🔌