معالجة الأخطاء وتصحيح الأخطاء
بعد 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) في المتصفح.📖 ملخص
- إشعار < تحذير < خطأ فادح — ثلاثة مستويات للخطأ
- التطوير:
display_errors On؛ الإنتاج:display_errors Off+log_errors On try { } catch (ExceptionType $e) { } finally { }— نمط معالجة الاستثناءات- فئات الاستثناءات المخصصة (
class XxxException extends Exception) error_log()للتسجيل،var_dump() + exitللتصحيح السريع- استكشاف أخطاء الصفحة الفارغة: قم أولاً بتمكين
display_errors، ثم تحقق من سجل الأخطاء - التخزين المؤقت للإخراج
ob_start()/ob_get_clean()يتحكم في الإخراج
📝 تمارين
- اكتب صفحة آلة حاسبة للقسمة تستخدم آلية try/catch للتعامل مع القسمة على الصفر وعرض رسالة توضيحية.
- قم بإنشاء فئة أساسية
AppExceptionمع فئتين فرعيتين هماValidationExceptionوDatabaseException. استخدمها في منطق تسجيل المستخدمين وقم بالتعامل معها بشكل منفصل. - أضف ميزة التسجيل إلى التمرين 2: عند اكتشاف استثناءات، قم بتدوين تفاصيل الخطأ (الطابع الزمني، الملف، رقم السطر) في ملف سجل.



