معالجة الأخطاء في PHP
إدارة الأخطاء بشكل صحيح يجعل تطبيقك أكثر استقراراً:
- ✅ Try-Catch للاستثناءات
- ✅ معالج الأخطاء المخصص
- ✅ فئة التسجيل (Logging)
- ✅ Assertions للاختبارات
- ✅ تصنيف الأخطاء والاستثناءات
معالجة الاستثناءات
<?php
class ValidationException extends Exception {
private $errors;
public function __construct($message, $errors = [], $code = 0, Throwable $previous = null) {
$this->errors = $errors;
parent::__construct($message, $code, $previous);
}
public function getErrors() {
return $this->errors;
}
}
class UserService {
public function register($userData) {
$errors = [];
if (empty($userData['name'])) {
$errors['name'] = "الاسم مطلوب";
}
if (!filter_var($userData['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = "البريد الإلكتروني غير صحيح";
}
if (!empty($errors)) {
throw new ValidationException("فشل في التحقق من البيانات", $errors);
}
try {
return $this->createUser($userData);
} catch (PDOException $e) {
throw new Exception("فشل في إنشاء المستخدم: " . $e->getMessage());
}
}
}
$userService = new UserService();
try {
$user = $userService->register([
'name' => 'أحمد',
'email' => 'ahmed@example.com'
]);
echo "تم إنشاء المستخدم بنجاح!";
} catch (ValidationException $e) {
$errors = $e->getErrors();
echo "أخطاء التحقق: ";
print_r($errors);
} catch (Exception $e) {
echo "حدث خطأ: " . $e->getMessage();
} finally {
echo "تم الانتهاء من العملية";
}
معالج الأخطاء المخصص
<?php
class ErrorHandler {
public static function register() {
set_error_handler([self::class, 'handleError']);
set_exception_handler([self::class, 'handleException']);
register_shutdown_function([self::class, 'handleShutdown']);
}
public static function handleError($level, $message, $file = '', $line = 0) {
throw new ErrorException($message, 0, $level, $file, $line);
}
public static function handleException(Throwable $exception) {
self::logError(
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
);
if (ini_get('display_errors')) {
self::displayError($exception);
} else {
self::displayFriendlyError();
}
exit(1);
}
public static function handleShutdown() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
self::logError(
$error['message'],
$error['file'],
$error['line']
);
self::displayFriendlyError();
}
}
private static function logError($message, $file, $line, $trace = '') {
$logMessage = sprintf(
"[%s] Error: %s in %s on line %d\nTrace: %s\n\n",
date('Y-m-d H:i:s'),
$message,
$file,
$line,
$trace
);
error_log($logMessage, 3, 'errors.log');
}
private static function displayError(Throwable $exception) {
echo "<div style='border: 1px solid #f00; padding: 10px; margin: 10px;'>";
echo "<h3>خطأ: </h3>";
echo "<p><strong>الرسالة:</strong> {$exception->getMessage()}</p>";
echo "<p><strong>الملف:</strong> {$exception->getFile()}</p>";
echo "<p><strong>السطر:</strong> {$exception->getLine()}</p>";
echo "<pre>{$exception->getTraceAsString()}</pre>";
echo "</div>";
}
private static function displayFriendlyError() {
http_response_code(500);
echo "<h1>عذراً، حدث خطأ</h1>";
echo "<p>نواجه بعض المشاكل التقنية. الرجاء المحاولة لاحقاً.</p>";
}
}
ErrorHandler::register();
فئة التسجيل (Logging)
<?php
class Logger {
const DEBUG = 100;
const INFO = 200;
const WARNING = 300;
const ERROR = 400;
const CRITICAL = 500;
private $logFile;
private $minLogLevel;
public function __construct($logFile = 'app.log', $minLogLevel = self::DEBUG) {
$this->logFile = $logFile;
$this->minLogLevel = $minLogLevel;
}
public function debug($message, $context = []) {
$this->log(self::DEBUG, $message, $context);
}
public function info($message, $context = []) {
$this->log(self::INFO, $message, $context);
}
public function warning($message, $context = []) {
$this->log(self::WARNING, $message, $context);
}
public function error($message, $context = []) {
$this->log(self::ERROR, $message, $context);
}
public function critical($message, $context = []) {
$this->log(self::CRITICAL, $message, $context);
}
private function log($level, $message, $context = []) {
if ($level < $this->minLogLevel) {
return;
}
$levelName = $this->getLevelName($level);
$timestamp = date('Y-m-d H:i:s');
$logMessage = sprintf(
"[%s] %s: %s",
$timestamp,
$levelName,
$this->interpolate($message, $context)
);
file_put_contents($this->logFile, $logMessage . PHP_EOL, FILE_APPEND | LOCK_EX);
}
private function getLevelName($level) {
$levels = [
self::DEBUG => 'DEBUG',
self::INFO => 'INFO',
self::WARNING => 'WARNING',
self::ERROR => 'ERROR',
self::CRITICAL => 'CRITICAL',
];
return $levels[$level] ?? 'UNKNOWN';
}
private function interpolate($message, $context = []) {
$replace = [];
foreach ($context as $key => $val) {
$replace['{' . $key . '}'] = is_scalar($val) ? $val : json_encode($val);
}
return strtr($message, $replace);
}
}
$logger = new Logger('application.log', Logger::INFO);
$logger->info("User {username} logged in", ['username' => 'ahmed']);
$logger->error("Database connection failed", ['error' => $e->getMessage()]);