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

۱. ویرایش فایل هسته یا قالب والد

این بزرگ‌ترین و رایج‌ترین اشتباه است. یک خط تغییر در functions.php قالب والد، در اولین آپدیت، ناپدید می‌شود. تغییر در هستهٔ وردپرس، در هر به‌روزرسانی امنیتی، پاک می‌شود و ممکن است سایت را ناپایدار کند. نشانه: در فایل قالب والد یا wp-includes کدی اضافه شده که در نسخهٔ رسمی نیست. ریشه: کوتاه‌ترین راهِ رسیدن به نتیجه، بدون توجه به پیامد. راه‌حل: تمام سفارشی‌سازی ظاهری در چایلد تم، و تمام منطق در افزونهٔ اختصاصی. راهنمای کامل در افزودن کد بدون ویرایش هسته و توسعه با چایلد تم.

۲. نبود پیشوند در نام توابع

در PHP، فضای نام سراسری است. تابعی با نام get_data() یا process_user()، به‌سرعت با افزونه یا قالب دیگری تعارض می‌کند. نشانهٔ کلاسیک: پس از نصب افزونه‌ای جدید، سایت با خطای Cannot redeclare function روبرو می‌شود. نشانه: خطاهای Fatal error با ذکر «redeclare» یا رفتار غیرعادی در افزونه‌های مختلف. ریشه: عدم استفاده از پیشوند اختصاصی برای توابع و کلاس‌ها. راه‌حل: پیشوند سه تا پنج کاراکتری از نام پروژه در همهٔ توابع، کلاس‌ها و متغیرهای سراسری. مثال‌ها در استانداردهای کدنویسی وردپرس و پیاده‌سازی استانداردها. در پروژه‌ای که افزونهٔ اختصاصی‌اش تابع format_price() تعریف کرده بود، پس از نصب یک افزونهٔ فروشگاهی، سایت با خطای Fatal از دسترس خارج شد. بازنویسی با پیشوند، در نیم‌ساعت انجام شد ولی سایت دو ساعت آفلاین بود.

پیشوند در نام توابع، مثل شناسنامه است؛ تا روزی که تعارض پیش نیاید، کسی اهمیتش را نمی‌فهمد — و روزی که پیش بیاید، ساعت‌ها وقت می‌گیرد.

۳. نبود sanitize و escape

هر داده‌ای که از کاربر می‌آید، قبل از ذخیره باید پاک‌سازی شود؛ هر داده‌ای که نمایش داده می‌شود، باید escape شود. حذف این دو مرحله، خطر XSS و SQLi جدی می‌سازد. نشانه: نبود sanitize_text_field در پردازش فرم، یا نبود esc_html در نمایش خروجی. در لاگ خطا این مشکل دیده نمی‌شود؛ فقط در پرونده‌های امنیتی. ریشه: فرض «داده‌ای که من می‌سازم، امن است». راه‌حل: قاعدهٔ ثابت در تمام کد: هر ورودی با sanitize_*، هر خروجی با esc_*. راهنمای کامل در PHP امن در وردپرس، پاک‌سازی داده‌ها، و اعتبارسنجی داده‌ها.

۴. کوئری خام بدون prepare

کوئری خام با $wpdb->query( $sql ) و مقادیر ورودی، خطر SQL Injection جدی دارد. نشانه: کوئری‌هایی که متغیرهای ورودی را مستقیم در رشتهٔ SQL جای می‌دهند. ریشه: آشنایی با SQL، بدون آگاهی از لایهٔ امنیتی وردپرس. راه‌حل: همیشه $wpdb->prepare:

// نادرست
$sql = "SELECT * FROM wp_posts WHERE post_author = $author_id";

// درست
$sql = $wpdb->prepare(
    "SELECT * FROM {$wpdb->posts} WHERE post_author = %d",
    $author_id
);
$results = $wpdb->get_results( $sql );

راهنمای کامل در توابع کوئری سفارشی و بهینه‌سازی کوئری‌های MySQL.

۵. نبود nonce و check_user_can

هر فرم و درخواست AJAX، نیاز به nonce دارد تا از CSRF جلوگیری شود. هر عملیات حساس، نیاز به current_user_can دارد. نشانه: فرمی که فقط با بررسی $_POST پردازش می‌شود، بدون wp_verify_nonce و بدون current_user_can. ریشه: فرض اینکه «فقط من به این فرم دسترسی دارم». راه‌حل: الگوی استاندارد در تمام فرم‌ها:

// در فرم
wp_nonce_field( 'my_action', 'my_nonce' );

// در پردازش
if ( ! isset( $_POST['my_nonce'] ) ) return;
if ( ! wp_verify_nonce( $_POST['my_nonce'], 'my_action' ) ) return;
if ( ! current_user_can( 'manage_options' ) ) return;

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

۶. کد در فایل اصلی افزونه

فایل اصلی افزونه، برای bootstrap است؛ نه برای منطق. وقتی my-plugin.php بالای ۵۰۰ خط می‌رسد، نگهداری و تست سخت می‌شود. نشانه: فایل اصلی افزونه با انبوهی از توابع و کلاس‌ها، بدون ساختار پوشه‌ای. ریشه: شروع با فایل ساده، بدون بازنگری ساختار در طول رشد. راه‌حل: انتقال منطق به پوشهٔ includes/، جدا کردن admin از public، و استفاده از autoloader. الگو در ساختار فایل‌های افزونهٔ استاندارد و کدنویسی اختصاصی افزونه. در پروژه‌ای با فایل اصلی ۱۵۰۰ خطی، بازنویسی به ساختار استاندارد، زمان دیباگ را از چند ساعت به چند دقیقه کاهش داد.

۷. لود asset در همهٔ صفحات

لود CSS و JS در همهٔ صفحات، حتی صفحه‌هایی که آن‌ها را لازم ندارند، سرعت سایت را محسوس کاهش می‌دهد. نشانه: در DevTools، فایل‌های CSS/JS قالب یا افزونه در صفحه‌هایی که به آن‌ها نیاز ندارند. ریشه: استفاده از wp_enqueue_scripts بدون شرط. راه‌حل: بارگذاری انتخابی:

function my_plugin_assets() {
    if ( ! is_singular( 'portfolio' ) ) return;
    wp_enqueue_style( 'my-portfolio', ... );
    wp_enqueue_script( 'my-portfolio', ... );
}
add_action( 'wp_enqueue_scripts', 'my_plugin_assets' );

راهنمای کامل در تأثیر افزونه‌ها بر سرعت سایت و بهینه‌سازی کد وردپرس.

۸. کوئری درون حلقه (N+1)

کوئری اضافه برای هر آیتم حلقه، شایع‌ترین الگوی کندی در وردپرس است. نشانه: تعداد کوئری که با تعداد آیتم حلقه رابطهٔ خطی دارد. ریشه: فراخوانی توابعی مثل get_post_meta یا get_userdata در حلقه، بدون آگاهی از cache یا aggregation. راه‌حل: جمع‌آوری IDها قبل از حلقه و یک کوئری واحد:

$query = new WP_Query( array( 'post_type' => 'project', 'posts_per_page' => 20 ) );

$author_ids = array_unique( wp_list_pluck( $query->posts, 'post_author' ) );
$authors = array();
foreach ( $author_ids as $author_id ) {
    $authors[ $author_id ] = get_userdata( $author_id );
}

while ( $query->have_posts() ) :
    $query->the_post();
    $author = $authors[ get_the_author_meta( 'ID' ) ];
endwhile;

راهنمای کامل در بهینه‌سازی کوئری‌ها و کدنویسی کوئری سفارشی.

۹. نبود کش در محاسبات سنگین

هر محاسبهٔ سنگین که در هر بازدید تکرار می‌شود، باید کش شود. نشانه: کوئری یا محاسبه‌ای که در هر صفحه اجرا می‌شود، ولی نتیجه در بازه‌های کوتاه تغییر نمی‌کند. ریشه: ترس از پیچیدگی cache یا فراموشی آن. راه‌حل: استفاده از Transients:

function my_plugin_get_stats() {
    $data = get_transient( 'my_stats' );
    if ( $data !== false ) return $data;

    $data = /* محاسبه سنگین */;
    set_transient( 'my_stats', $data, HOUR_IN_SECONDS );
    return $data;
}

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

۱۰. منطق در قالب به‌جای افزونه

وقتی منطق کسب‌وکار در قالب نوشته شود، روز تغییر قالب همه از دست می‌رود. نشانه: توابع پیچیده در functions.php چایلد یا والد، شامل کوئری، ذخیره‌سازی، یا اتصال به سرویس بیرونی. ریشه: راحتی دسترسی به فایل قالب. راه‌حل: قاعدهٔ سه‌بخشی: ظاهر در چایلد تم، منطق در افزونهٔ اختصاصی، تنظیمات در Options API. راهنمای تفکیک در افزودن قابلیت به وردپرس، توسعه با چایلد تم، و کدنویسی اختصاصی افزونه.

کدی که در قالب زندگی می‌کند، عمری به عمر قالب دارد؛ کدی که در افزونه زندگی می‌کند، عمری به عمر کسب‌وکار دارد.

۱۱. نبود Git و تست

پروژه‌ای که Git و تست ندارد، در بحران آسیب‌پذیر است. نشانه: تاریخچهٔ تغییرات نامعلوم، بازگشت به نسخهٔ قبلی با کپی دستی، نبود تست خودکار برای توابع حیاتی. ریشه: تمرکز روی فیچر، بدون سرمایه‌گذاری در زیرساخت توسعه. راه‌حل: Git از روز اول و تست خودکار از اولین فیچر جدی. راهنمای Git در گیت در وردپرس و آموزش Git از صفر. راهنمای تست در تست و دیباگ پروژه‌های وردپرس و CI/CD در وردپرس. در پروژه‌ای که از روز اول Git و تست داشت، در پنج آپدیت بزرگ، حتی یک باگ Production نداشتیم.

۱۲. نبود مستندسازی

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

چک‌لیست پیش از انتشار

دستهبررسیپرچم قرمز
ساختارهیچ فایل والد یا هسته ویرایش شده؟هر خط تغییر در هسته یا والد
نام‌گذاریهمهٔ توابع و کلاس‌ها پیشوند یکتا دارند؟تابع با نام عمومی
امنیتsanitize در همهٔ ورودی‌ها، escape در همهٔ خروجی‌ها؟هر مسیر بدون sanitize
کوئریکوئری خام با prepare؟متغیر مستقیم در SQL
نانسهر فرم و AJAX دارای nonce و check_user_can؟فرم بدون nonce
ساختار افزونهفایل اصلی زیر ۳۰۰ خط؟فایل اصلی با کد منطق
assetبارگذاری انتخابی CSS/JS؟لود در همهٔ صفحات
کوئری در حلقههیچ کوئری اضافه در حلقه؟الگوی N+1
کشمحاسبات سنگین کش شده؟محاسبه در هر بازدید
تفکیکمنطق در افزونه، ظاهر در چایلد؟منطق در قالب
Gitتمام تغییرات در Git ثبت شده؟تغییرات خارج از Git
مستندسازیREADME و PHPDoc کامل؟کد بدون توضیح

دوازده اشتباهی که مرور کردیم، همه از یک ریشه می‌آیند: تصمیم‌های ظاهراً راحت که در بلندمدت هزینه دارند. اگر امروز یک کار در این مسیر انجام می‌دهید: فهرست بالا را برای پروژهٔ فعلی خود چک کنید و ببینید کدام‌یک از این دوازده مورد را دارید. هر یک که دارید، برنامهٔ اصلاح در هفتهٔ بعد داشته باشید. اگر تجربه‌ای از یک اشتباه گران‌قیمت در پروژهٔ خود دارید — به‌ویژه در مراحل رشد پروژه — در دیدگاه‌ها بنویسید؛ همان گزارش‌های واقعی، این فهرست را دقیق‌تر می‌کند. ⚠️