دیباگ، مهارتی که هیچ‌کس یاد نمی‌دهد

در سال‌های کار با وردپرس، یک الگوی عجیب دیده‌ام: توسعه‌دهندگانی که در نوشتن کد مهارت بالایی دارند، گاهی در دیباگ، ساعت‌ها وقت تلف می‌کنند — نه به‌خاطر بی‌سوادی، به‌خاطر نبود روش. خودم سال‌ها این‌طور بودم: به‌جای فعال‌کردن WP_DEBUG، با var_dump و die() در کد می‌گشتم تا بفهمم چرا صفحه سفید شده. یک بار، دو روز کامل صرف کردم تا بفهمم چرا یک صفحه سفید شده؛ روز سوم، یک همکار باتجربه‌تر گفت «فایل debug.log را باز کردی؟» جواب منفی بود. لاگ، دقیقاً همان خطا را نشان می‌داد که من دو روز دنبالش بودم. از آن روز، یک عادت در من شکل گرفت: پیش از هر دیباگ، ابزارها را راه‌اندازی کن، بعد سراغ کد برو. این مقاله، حاصل همان عادت است — چارچوبی از توابع و ابزارهای دیباگ وردپرس که در پروژه‌های واقعی به‌کار می‌برم.

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

چرا دیباگ، مهارت پنهان توسعه‌دهنده وردپرس است؟

سه دلیل که دیباگ را از یک «فعالیت جانبی» به «مهارت اصلی» تبدیل می‌کند:

  1. درصد زمان توسعه: در تجربه من، توسعه‌دهندگان حرفه‌ای بین ۴۰ تا ۶۰ درصد زمان خود را صرف دیباگ و عیب‌یابی می‌کنند، نه نوشتن کد جدید. اگر این ۵۰ درصد بهینه شود، بهره‌وری کلی دو برابر می‌شود.
  2. هزینه باگ در Production: باگی که در محیط توسعه در پنج دقیقه پیدا می‌شود، همان باگ در Production می‌تواند ساعت‌ها وقت پشتیبانی و اعتبار برند بگیرد. دیباگ خوب در توسعه، بیمه‌نامه Production است.
  3. اثر روی کیفیت کد: توسعه‌دهنده‌ای که ابزارهای دیباگ را می‌شناسد، در نوشتن کد نیز محتاط‌تر است؛ چون می‌داند چه چیزی را چطور خواهد سنجید. این تفکر، در اصول کدنویسی تمیز و استانداردهای کدنویسی وردپرس هم به‌عنوان اصل آمده است.
دیباگ، هنر پرسیدن سوال درست در زمان درست است. ابزارها فقط به شما اجازه می‌دهند سوال را سریع‌تر بپرسید، ولی تصمیم‌گیری، کار شماست.

WP_DEBUG و ثابت‌های مرتبط

اولین ابزار دیباگ در وردپرس، خود WP_DEBUG است که در wp-config.php فعال می‌شود. پنج ثابت مرتبط:

// فعال‌سازی حالت دیباگ
define( 'WP_DEBUG', true );

// ذخیره خطاها در فایل debug.log
define( 'WP_DEBUG_LOG', true );

// عدم نمایش خطاها در صفحه
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

// نمایش نسخه غیرفشرده اسکریپت‌ها
define( 'SCRIPT_DEBUG', true );

// ذخیره کوئری‌ها برای دیباگ کوئری‌ها
define( 'SAVEQUERIES', true );

پنج نکته حیاتی در استفاده از این ثابت‌ها:

  • WP_DEBUG = true در محیط توسعه: خطاهای PHP در سطح Warning و Notice نمایش داده می‌شوند. در محیط Production هرگز WP_DEBUG_DISPLAY = true نگذارید — چون خطاها به کاربر نمایش داده می‌شوند و مسیر فایل‌ها و ساختار سرور افشا می‌شود. راهنمای امنیت در امنیت وردپرس برای مبتدیان.
  • WP_DEBUG_LOG = true در Production: این ثابت را می‌توانید در Production هم فعال کنید تا خطاها در فایل debug.log ذخیره شوند، ولی نمایش داده نشوند. توصیه من در پروژه‌های واقعی: در Production فعال، در پوشه‌ای خارج از دسترس عمومی. راهنمای تفصیلی در امن‌سازی wp-config.
  • WP_DEBUG_DISPLAY = false + @ini_set: ترکیب این دو، خطاها را از صفحه خارج و به لاگ هدایت می‌کند. بدون @ini_set، بعضی هاست‌ها خطاها را با تنظیمات PHP نمایش می‌دهند.
  • SCRIPT_DEBUG = true: وردپرس نسخه‌های غیرفشرده .js و .css را لود می‌کند. برای دیباگ front-end مفید است ولی روی سرعت اثر می‌گذارد — در Production خاموش.
  • SAVEQUERIES = true: تمام کوئری‌ها در متغیر $wpdb->queries ذخیره می‌شوند. بار قابل‌توجهی روی سرور دارد؛ فقط در محیط توسعه یا دیباگ‌های کوتاه‌مدت فعال کنید. راهنمای بهینه‌سازی کوئری‌ها در بهینه‌سازی کوئری‌ها.

یک تجربه میدانی: در پروژه‌ای، تیم تولید چند هفته با باگ‌های گاه‌به‌گاه مواجه بود که در محیط توسعه بازتولید نمی‌شد. راه‌حل: فعال‌کردن WP_DEBUG_LOG روی Production به‌مدت دو هفته، با چرخش خودکار لاگ. بعد از دو هفته، لاگ نشان داد یک افزونه جانبی، هر چند ساعت یک خطای Deprecated تولید می‌کرد که در نسخه بعدی PHP، به خطای Fatal تبدیل می‌شد. رفع سه‌خطی، مشکل چند هفته‌ای را بست.

error_log و نوشتن لاگ سفارشی

خارج از WP_DEBUG_LOG، می‌توانید خودتان در کد لاگ بنویسید:

// لاگ ساده
error_log( 'پیام دیباگ' );

// لاگ با داده
$data = array( 'user_id' => 5, 'action' => 'purchase' );
error_log( print_r( $data, true ) );

// لاگ ساختاریافته
error_log( sprintf( '[%s] %s: %s', current_time( 'mysql' ), 'my_plugin', 'محاسبه انجام شد' ) );

سه نکته مهم در استفاده از error_log: یک — پیش از چاپ داده پیچیده، از print_r با پارامتر دوم true استفاده کنید تا به‌جای چاپ، رشته برگردانده شود. دو — در Production هرگز داده حساس (رمز، کلید API، اطلاعات کاربر) را لاگ نکنید. سه — لاگ‌ها را با یک پیشوند معنادار بنویسید تا در فایل بزرگ، پیدا کردنشان راحت باشد. راهنمای تفصیلی در دیباگ کد سفارشی وردپرس.

برای پروژه‌های جدی، توصیه می‌کنم یک کلاس لاگ اختصاصی بسازید:

class My_Plugin_Logger {
    const LOG_FILE = 'my-plugin';

    public static function log( $message, $context = array(), $level = 'info' ) {
        if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
            return;
        }

        $entry = sprintf(
            '[%s] [%s] [%s] %s %s',
            current_time( 'mysql' ),
            self::LOG_FILE,
            strtoupper( $level ),
            is_string( $message ) ? $message : wp_json_encode( $message ),
            $context ? wp_json_encode( $context ) : ''
        );

        error_log( $entry );
    }

    public static function info( $message, $context = array() ) {
        self::log( $message, $context, 'info' );
    }

    public static function warning( $message, $context = array() ) {
        self::log( $message, $context, 'warning' );
    }

    public static function error( $message, $context = array() ) {
        self::log( $message, $context, 'error' );
    }
}

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

Query Monitor: چشمان دیباگ

Query Monitor، محبوب‌ترین افزونه دیباگ در وردپرس است و در پروژه‌های واقعی، اولین چیزی است که نصب می‌کنم. پنج قابلیت اصلی:

  • کوئری‌های دیتابیس: تعداد، زمان اجرا، منبع (افزونه/قالب/هسته)، و کوئری خام. منوی «Queries by Caller» به‌تنهایی نصف زمان دیباگ را کم می‌کند.
  • هوک‌ها: کدام هوک‌ها در چه ترتیبی با چه اولویتی اجرا می‌شوند. برای دیباگ ترتیب اجرا، بی‌رقیب است.
  • خطاهای PHP: خطاهایی که در صفحه رخ می‌دهند، با مسیر و خط دقیق.
  • HTTP API: درخواست‌های خروجی به سرویس‌های بیرونی، با زمان و پاسخ. برای دیباگ توابع HTTP وردپرس بسیار مفید است.
  • متغیرهای Query: پارامترهای WP_Query، برای دیباگ کوئری‌های سفارشی. راهنما در توابع کوئری سفارشی.

یک نکته حیاتی: Query Monitor را روی Production فعال نگذارید. بار قابل‌توجهی به سرور اضافه می‌کند و ممکن است اطلاعات حساس را نمایش دهد. روی محیط توسعه یا استیجینگ استفاده کنید. راهنمای محیط لوکال در توسعه با محیط لوکال. یک تجربه میدانی: در پروژه‌ای که صفحه اصلی در چهار ثانیه لود می‌شد، Query Monitor نشان داد که یک افزونه کوچک، ۸۰ کوئری اضافه در هر بازدید می‌زند. حذف افزونه، زمان پاسخ را به ۱.۵ ثانیه رساند. راهنمای تکمیلی در چرا قالب‌ها سایت را کند می‌کنند.

دیباگ با Xdebug و step debugging

Xdebug، ابزار حرفه‌ای دیباگ PHP است. سه قابلیت اصلی: یک — Step Debugging: کد را خط‌به‌خط اجرا کنید و متغیرها را در هر نقطه ببینید. دو — Stack Traces: مسیر فراخوانی در لحظه خطا. سه — Profiling: زمان اجرای هر تابع. پیکربندی در php.ini:

xdebug.mode = debug,develop
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9003
xdebug.start_with_request = trigger
xdebug.log_level = 0

پس از پیکربندی، در VS Code یا PHPStorm، افزونه Xdebug Client را نصب کنید و Breakpoint بگذارید. یک تجربه میدانی: در پروژه‌ای، یک متغیر در شرط خاصی null می‌شد و از مسیرهای مختلفی رد می‌شد. با Xdebug، در پنج دقیقه علت پیدا شد — بدون Xdebug، سه روز وقت گرفته بود. توصیه من: Xdebug را روی Production غیرفعال کنید (بار اضافه و کاهش سرعت). فقط در محیط توسعه. راهنمای راه‌اندازی در توسعه با محیط لوکال.

هر ابزار دیباگ، مثل یک لنز است: با لنز درست، جزئیات را می‌بینید؛ با لنز اشتباه، فقط نزدیک‌تر می‌شوید.

دیباگ هوک‌ها و ترتیب اجرا

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

// آیا هوک خاصی ثبت شده است؟
has_action( 'init', 'my_callback' );       // برای action
has_filter( 'the_content', 'my_filter' ); // برای filter

// چند بار اجرا شده است؟
did_action( 'init' );                    // چند بار init اجرا شد

// فیلتر جاری
current_filter();                       // نام هوک در حال اجرا

// لیست تمام هوک‌های ثبت‌شده در یک نقطه
global $wp_filter;
error_log( print_r( $wp_filter['init'], true ) );

نکته مهم: global $wp_filter یکی از پرکاربردترین ابزارها در دیباگ هوک‌ها است. با آن می‌توانید ببینید چه توابعی با چه اولویتی روی یک هوک ثبت شده‌اند. راهنمای کامل در هوک‌های وردپرس، استفاده درست از هوک‌ها، و کنترل ترتیب اجرای هوک‌ها. یک تجربه میدانی: در پروژه‌ای، یک افزونه دیگر، فیلتر the_content را با اولویت ۵ ثبت کرده بود و قبل از افزونه ما اجرا می‌شد. با global $wp_filter، در چند ثانیه پیدا شد و مشکل با تغییر اولویت حل شد.

دیباگ AJAX و REST API

دیباگ endpointهای AJAX و REST API، چالش‌های خاص خودش را دارد چون خطاها در کنسول مرورگر پنهان می‌شوند. سه ابزار:

ابزار اول، لاگ در پاسخ:

function my_ajax_handler() {
    // فعال‌سازی نمایش خطا در پاسخ
    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        ini_set( 'display_errors', 1 );
    }
    
    // پردازش
    $result = array( 'status' => 'ok' );
    wp_send_json_success( $result );
}

ابزار دوم، لاگ در فایل اختصاصی:

function my_ajax_handler() {
    error_log( 'AJAX Request: ' . wp_json_encode( $_POST ) );
    
    // پردازش
    wp_send_json_success( $result );
}

ابزار سوم، استفاده از REST API با بررسی بهتر: در REST API، خطاها به‌طور خودکار در پاسخ JSON برمی‌گردند و در DevTools قابل مشاهده‌اند. راهنمای کامل در ساخت API اختصاصی، REST API وردپرس، و تست و دیباگ پروژه‌های وردپرس. یک تجربه میدانی: در پروژه‌ای، endpoint AJAX پاسخی خالی برمی‌گرداند. علت: یک خطای Notice در PHP، قبل از wp_send_json_success رخ می‌داد و پاسخ را قطع می‌کرد. با فعال‌کردن WP_DEBUG_LOG، خطا در لاگ پیدا شد.

دیباگ کوئری‌های دیتابیس

برای دیباگ کوئری‌ها، دو ابزار اصلی: یک — SAVEQUERIES: تمام کوئری‌ها در $wpdb->queries ذخیره می‌شوند. دو — Query Monitor: نمایش گرافیکی کوئری‌ها با زمان و منبع. الگوی دستی با SAVEQUERIES:

// در wp-config.php
define( 'SAVEQUERIES', true );

// در کد (پشت سد دسترسی ادمین)
add_action( 'wp_footer', function() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    
    global $wpdb;
    $total_time = 0;
    $total_queries = count( $wpdb->queries );
    
    foreach ( $wpdb->queries as $query ) {
        $total_time += $query[1];
    }
    
    printf(
        '',
        $total_queries,
        $total_time
    );
} );

نکته مهم: SAVEQUERIES بار قابل‌توجهی روی سرور دارد و روی Production غیرفعال باشد. یک تجربه میدانی: در پروژه‌ای، صفحه اصلی ۴۵۰ کوئری داشت. با تحلیل لاگ، فهمیدیم ۳۰۰ کوئری از یک ویجت فوتر قدیمی می‌آید. حذف ویجت، تعداد کوئری را به ۱۵۰ کاهش داد. راهنمای بهینه‌سازی در بهینه‌سازی کوئری‌ها و بهینه‌سازی کوئری‌های MySQL.

دیباگ front-end و کنسول مرورگر

در front-end، سه ابزار اصلی: یک — کنسول مرورگر: خطاهای JavaScript، هشدارها، و پیام‌های لاگ. دو — تب Network: درخواست‌های شبکه، زمان لود، حجم فایل. سه — تب Performance: پروفایلینگ front-end، زمان رندر، گلوگاه‌ها. یک تجربه میدانی: در پروژه‌ای، یک فرم با شکست مواجه می‌شد. کنسول مرورگر نشان داد یک فایل JS سوم‌شخص، متغیر جهانی $ را از jQuery گرفته و بازتعریف کرده. حذف آن فایل، فرم را نجات داد. راهنمای کامل در پیدا کردن خطاهای JS در کنسول و ابزارهای تست سرعت سایت.

دیباگ روی Production

دیباگ روی Production، محدودیت‌های خاص خودش را دارد. پنج قاعده الزامی: یک — WP_DEBUG_DISPLAY = false: خطاها به کاربر نمایش داده نشوند. دو — WP_DEBUG_LOG = true: خطاها در فایل ذخیره شوند. سه — پایش دوره‌ای لاگ: روزانه یا هفتگی، فایل debug.log را بررسی کنید. چهار — ابزار monitoring: از سرویس‌هایی مثل New Relic یا Uptime Robot برای پایش خطاها و زمان پاسخ استفاده کنید. پنج — بکاپ فوری: پیش از هر تغییر روی Production، بکاپ کامل. راهنمای بکاپ در بکاپ سایت و افزونه‌های بکاپ.

یک نکته پیشرفته: برای دیباگ روی Production، از یک زیرساخت لاگ‌گیری مرکزی استفاده کنید. سرویس‌هایی مثل Sentry یا Bugsnag، خطاها را در یک داشبورد جداگانه نمایش می‌دهند و از پر شدن debug.log روی هاست جلوگیری می‌کنند. راهنمای ساختاربندی در ساختاربندی پروژه وردپرس و CI/CD در وردپرس. یک تجربه میدانی: در پروژه‌ای با Sentry، خطاهای Production که قبلاً ماه‌ها پنهان می‌ماندند، در چند دقیقه پس از رخ دادن، به تیم گزارش می‌شدند و در همان هفته رفع می‌شدند.

دیباگ روی Production مثل جراحی در اتاق عمل شیشه‌ای است: هر حرکت دیده می‌شود، هر خطا هزینه دارد، و هیچ‌وقت نمی‌توانی «فقط یک تست کوچک» بکنی.

اشتباهات رایج در دیباگ وردپرس

  • نبود WP_DEBUG در محیط توسعه: خطاها پنهان می‌مانند. راهنما در رفع Fatal error PHP.
  • WP_DEBUG_DISPLAY = true روی Production: خطر افشای مسیرها و اطلاعات. راهنما در امنیت وردپرس.
  • نبود بکاپ پیش از تغییر دیباگ: در صورت خطا، بازگشت دشوار. راهنما در بکاپ سایت.
  • استفاده از var_dump در Production: افشای داده و شکست ظاهری. راهنما در دیباگ کد سفارشی.
  • نصب Query Monitor روی Production: بار اضافه و نمایش اطلاعات حساس به ادمین. راهنما در بهینه‌سازی کوئری‌ها.
  • فعال بودن Xdebug روی Production: کندی محسوس سایت. راهنما در محیط لوکال.
  • نادیده‌گرفتن Warningها: تبدیل به Fatal error در آینده. راهنما در رفع Warning PHP.
  • نبود لاگ در کد سفارشی: در بحران، اطلاعات کافی برای دیباگ وجود ندارد. راهنما در بهینه‌سازی کد وردپرس.
  • تغییرات کور برای رفع خطا: یک مشکل را حل، سه مشکل جدید می‌سازد. راهنما در تست و دیباگ.
  • نبود Error Tracking روی Production: خطاها پنهان می‌مانند. راهنما در ساختاربندی پروژه.
  • نبود Breakpoint در محیط توسعه: دیباگ با var_dump کندتر و کم‌دقت‌تر. راهنما در Xdebug و محیط لوکال.
  • نبود مستندسازی خطاهای تکرارشده: هر بار از صفر دیباگ می‌کنید. راهنما در استانداردها در پروژه.

جمع‌بندی

توابع و ابزارهای دیباگ وردپرس، در پنج گروه خلاصه می‌شوند: ثابت‌های WP_DEBUG (WP_DEBUG، WP_DEBUG_LOG، SAVEQUERIES)، لاگ‌گیری (error_log، کلاس لاگ اختصاصی)، ابزارهای افزونه‌ای (Query Monitor، Xdebug)، دیباگ درون‌کدی (has_action، did_action، global $wp_filter)، و پایش Production (Sentry، لاگ مرکزی). سه اصل را در پایان تاکید می‌کنم: اول، پیش از هر دیباگ، ابزارها را راه‌اندازی کنید — لاگ، خطایاب، پروفایلر. دوم، ترتیب دیباگ را از داده‌های واقعی شروع کنید، نه از حدس و گمان. سوم، از دیباگ‌های گذشته درس بگیرید و آن‌ها را مستند کنید.

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