معالجة الأخطاء وتصحيح الأخطاء

بعد 32 درسًا في كتابة الأكواد البرمجية، لا شك أنك واجهت نصيبك من العقبات — صفحات بيضاء فارغة، ورسائل خطأ غامضة، وعدم معرفة كيفية استخدام try/catch. يربط هذا الدرس بين جميع مهارات تصحيح الأخطاء، ويرفع مستواك من «العثور على الأخطاء بالصدفة» إلى «استكشاف الأخطاء وإصلاحها بشكل منهجي».

1. مستويات الخطأ

تتضمن لغة PHP ثلاثة أنواع من الأخطاء:

TEXT
Notice      ← "You may have overlooked something" (undefined variable, offset)
Warning     ← "The code has a problem but can continue" (include file not found)
Fatal Error ← "The code has a serious error and must stop" (calling a non-existent function)
PHP
<?php
// Notice: undefined variable (PHP continues execution)
echo $undefinedVar;  // Notice: Undefined variable $undefinedVar

// Warning: file not found (PHP continues execution)
include 'nonexistent.php';  // Warning: Failed opening...

// Fatal Error: calling a non-existent function (PHP stops)
thisFunctionDoesNotExist();  // Fatal Error: Call to undefined function
?>

2. التحكم في الإبلاغ عن الأخطاء

PHP
<?php
// Development: show all errors
error_reporting(E_ALL);
ini_set('display_errors', '1');

// Production: log but don't display
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/php-errors.log');
?>
🔥 خطأ شائع: لا تقم أبدًا بتعيين display_errors = On على خادم الإنتاج — فقد تكشف رسائل الخطأ عن مسارات الملفات وكلمات مرور قواعد البيانات ومعلومات حساسة أخرى. يجب أن يرى المستخدمون صفحة خطأ 500 عامة؛ أما تفاصيل الخطأ فتُسجل في السجل.

▶ مثال: التكوين المراعي للبيئة

PHP
<?php
$isDev = ($_SERVER['SERVER_NAME'] ?? '') === 'localhost';

if ($isDev) {
    error_reporting(E_ALL);
    ini_set('display_errors', '1');
} else {
    error_reporting(E_ALL);
    ini_set('display_errors', '0');
    ini_set('log_errors', '1');
    ini_set('error_log', '/var/log/php/errors.log');
}
?>
▶ جرّب الكود

3. معالجة الاستثناءات: try/catch/finally

PHP
<?php
function divide(float $a, float $b): float {
    if ($b === 0.0) {
        throw new Exception("Division by zero is not allowed!");
    }
    return $a / $b;
}

try {
    echo divide(10, 2) . "<br>";  // 5
    echo divide(10, 0) . "<br>";  // Throws an exception
} catch (Exception $e) {
    echo "[Caught Exception] {$e->getMessage()}<br>";
    echo "File: {$e->getFile()}<br>";
    echo "Line: {$e->getLine()}<br>";
} finally {
    echo "Whether success or failure, finally always runs (e.g., close files, release resources)<br>";
}
?>
كتلة وقت التشغيل
try يبدأ التنفيذ من هنا — تتم مراقبة الاستثناءات
catch يتم تنفيذها في حالة حدوث استثناء في كتلة try
finally يعمل دائمًا بغض النظر عن الظروف (يُستخدم عادةً لأغراض التنظيف)

4. الاستثناءات المخصصة

PHP
<?php
class ValidationException extends Exception {
    private array $errors;
    
    public function __construct(array $errors, string $message = "Validation failed") {
        parent::__construct($message);
        $this->errors = $errors;
    }
    
    public function getErrors(): array {
        return $this->errors;
    }
}

class UserNotFoundException extends Exception {}

// Using custom exceptions
function findUser(int $id): array {
    $users = [1 => ['name' => 'John'], 2 => ['name' => 'Jane']];
    
    if (!isset($users[$id])) {
        throw new UserNotFoundException("User ID {$id} does not exist");
    }
    return $users[$id];
}

function validateUsername(string $name): void {
    $errors = [];
    if ($name === '') $errors[] = 'Username cannot be empty';
    if (strlen($name) > 50) $errors[] = 'Username is too long';
    if (!empty($errors)) throw new ValidationException($errors);
}

// Unified exception handling
try {
    validateUsername('');
    $user = findUser(99);
} catch (ValidationException $e) {
    echo "[Validation Failed]<br>" . implode("<br>", $e->getErrors());
} catch (UserNotFoundException $e) {
    echo "[User Not Found] {$e->getMessage()}";
} catch (Exception $e) {
    echo "[Unknown Error] {$e->getMessage()}";
}
?>

5. التسجيل

PHP
<?php
class AppLogger {
    public function __construct(
        private string $logDir
    ) {
        if (!is_dir($logDir)) {
            mkdir($logDir, 0755, true);
        }
    }
    
    public function info(string $message, array $context = []): void {
        $this->log('INFO', $message, $context);
    }
    
    public function error(string $message, array $context = []): void {
        $this->log('ERROR', $message, $context);
    }
    
    private function log(string $level, string $message, array $context): void {
        $date = date('Y-m-d');
        $time = date('H:i:s');
        
        $line = "[{$time}] [{$level}] {$message}";
        if (!empty($context)) {
            $line .= ' ' . json_encode($context, JSON_UNESCAPED_UNICODE);
        }
        $line .= "\n";
        
        $file = "{$this->logDir}/app-{$date}.log";
        file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
    }
}

$log = new AppLogger(__DIR__ . '/logs');
$log->info('User logged in', ['username' => 'John', 'ip' => $_SERVER['REMOTE_ADDR'] ?? '']);
$log->error('Payment failed', ['order_id' => 12345, 'reason' => 'Insufficient balance']);
?>

6. تقنيات تصحيح الأخطاء

▶ مثال: وظائف أدوات تصحيح الأخطاء

PHP
<?php
// Most commonly used debug output
$data = ['name' => 'John', 'skills' => ['PHP', 'MySQL']];

var_dump($data);  // Detailed output (type + value)
// array(2) { ["name"]=> string(4) "John" ["skills"]=> ... }

print_r($data);   // Concise output
// Array ( [name] => John [skills] => Array ( [0] => PHP [1] => MySQL ) )

// Formatted var_dump (recommended)
echo '<pre>'; print_r($data); echo '</pre>';

// Debug and terminate
var_dump($data); exit;  // or die(var_dump($data));
?>
▶ جرّب الكود

(1) مرجع سريع لتصحيح الأخطاء

PHP
<?php
// Value + type
var_dump($variable);

// Only show in development
if ($isDev) {
    echo '<pre>'; var_dump($data); echo '</pre>';
}

// Log it
error_log("Debug info: " . print_r($data, true));

// Check if execution reaches a certain line
echo "HERE"; exit;  // or error_log("Reached this line");

// View the call stack
debug_print_backtrace();

// Check if a variable is set
var_dump(isset($var), empty($var));
?>

7. التخزين المؤقت للمخرجات ومعالجات الأخطاء المخصصة

PHP
<?php
// Output buffering: capture echo/print output
ob_start();
echo "This text won't be sent directly to the browser.";
$content = ob_get_clean();  // Get and clear the buffer
// $content is now "This text won't be sent directly to the browser."

// Custom error handler
function myErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool {
    $message = "[{$errno}] {$errstr} in {$errfile}:{$errline}";
    error_log($message);
    
    if (ini_get('display_errors')) {
        echo "<div style='background:#ffebee;padding:10px;margin:10px'>
            <strong>PHP Error:</strong> {$message}
        </div>";
    }
    return true;  // Prevent PHP's default handling
}
set_error_handler('myErrorHandler');

// Test the custom error handler
echo $undefined;  // Now displays in a nice HTML box
?>

❓ أسئلة شائعة

س كيف يمكنني تصحيح خطأ الصفحة البيضاء الفارغة؟
ج السبب الأكثر شيوعًا هو حدوث خطأ فادح في display_errors = Off. خطوات حل المشكلة: (1) أضف error_reporting(E_ALL); ini_set('display_errors', '1');؛ (2) تحقق من سجل أخطاء PHP؛ (3) أدخل echo "1"; exit; على فترات متباعدة لتحديد السطر الذي يتوقف عنده التنفيذ.
س ما هو Xdebug؟ هل أحتاج إليه؟
ج Xdebug هو ملحق احترافي لتصحيح أخطاء PHP — يتيح لك تعيين نقاط التوقف، والتنقل خطوة بخطوة عبر الكود، وفحص المتغيرات. كمبتدئ، يكفي استخدام var_dump. وعندما يتجاوز مشروعك 1000 سطر، فإنه يستحق التثبيت. ويُعد مزيج VS Code + Xdebug مريحًا بقدر أدوات المطور (DevTools) في المتصفح.

📖 ملخص

📝 تمارين

  1. اكتب صفحة آلة حاسبة للقسمة تستخدم آلية try/catch للتعامل مع القسمة على الصفر وعرض رسالة توضيحية.
  2. قم بإنشاء فئة أساسية AppException مع فئتين فرعيتين هما ValidationException وDatabaseException. استخدمها في منطق تسجيل المستخدمين وقم بالتعامل معها بشكل منفصل.
  3. أضف ميزة التسجيل إلى التمرين 2: عند اكتشاف استثناءات، قم بتدوين تفاصيل الخطأ (الطابع الزمني، الملف، رقم السطر) في ملف سجل.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%