أساسيات PHP
رفع الملفات
رفع الملفات
تسهل PHP التعامل مع رفع الملفات من المستخدمين. هذا ضروري للتطبيقات التي تحتاج إلى صور الملف الشخصي ورفع المستندات وملفات الوسائط والمزيد.
تنبيه أمني: رفع الملفات يمثل خطرًا أمنيًا كبيرًا إذا لم يتم التعامل معه بشكل صحيح. تحقق دائمًا من نوع الملف وحجمه ومحتواه. لا تثق أبدًا في إدخال المستخدم!
المتغير العمومي الفائق $_FILES
عند رفع الملفات، يتم تخزينها في مصفوفة المتغير العمومي الفائق $_FILES بالبنية التالية:
<?php
// بنية $_FILES لإدخال ملف اسمه "photo"
$_FILES['photo']['name'] // اسم الملف الأصلي
$_FILES['photo']['type'] // نوع MIME (مثل، "image/jpeg")
$_FILES['photo']['size'] // حجم الملف بالبايتات
$_FILES['photo']['tmp_name'] // الموقع المؤقت على الخادم
$_FILES['photo']['error'] // رمز الخطأ (0 = لا يوجد خطأ)
?>
نموذج رفع أساسي
النماذج التي ترفع الملفات يجب أن تتضمن سمة enctype:
<!DOCTYPE html>
<html>
<head>
<title>رفع ملف</title>
</head>
<body>
<h1>رفع ملف</h1>
<!-- مهم: enctype="multipart/form-data" مطلوب -->
<form method="post" action="upload.php" enctype="multipart/form-data">
<label for="file">اختر ملف:</label>
<input type="file" id="file" name="photo">
<button type="submit">رفع</button>
</form>
</body>
</html>
حاسم: قم دائمًا بتضمين
enctype="multipart/form-data" في النماذج مع رفع الملفات، وإلا لن يتم إرسال الملفات.
معالج رفع ملف أساسي
<?php
// upload.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// التحقق من رفع الملف
if (isset($_FILES['photo']) && $_FILES['photo']['error'] === 0) {
// تفاصيل الملف
$filename = $_FILES['photo']['name'];
$temp_path = $_FILES['photo']['tmp_name'];
$file_size = $_FILES['photo']['size'];
$file_type = $_FILES['photo']['type'];
// تعيين دليل الرفع
$upload_dir = 'uploads/';
$destination = $upload_dir . $filename;
// نقل الملف من الموقع المؤقت إلى الوجهة
if (move_uploaded_file($temp_path, $destination)) {
echo "تم رفع الملف بنجاح: $filename";
} else {
echo "خطأ في رفع الملف.";
}
} else {
echo "لم يتم رفع ملف أو حدث خطأ.";
}
}
?>
رموز أخطاء رفع الملفات
<?php
// فحص أخطاء الرفع
$error = $_FILES['photo']['error'];
switch ($error) {
case UPLOAD_ERR_OK:
echo "لا يوجد خطأ";
break;
case UPLOAD_ERR_INI_SIZE:
echo "الملف يتجاوز upload_max_filesize في php.ini";
break;
case UPLOAD_ERR_FORM_SIZE:
echo "الملف يتجاوز MAX_FILE_SIZE في نموذج HTML";
break;
case UPLOAD_ERR_PARTIAL:
echo "تم رفع الملف جزئيًا فقط";
break;
case UPLOAD_ERR_NO_FILE:
echo "لم يتم رفع أي ملف";
break;
case UPLOAD_ERR_NO_TMP_DIR:
echo "مجلد مؤقت مفقود";
break;
case UPLOAD_ERR_CANT_WRITE:
echo "فشل في كتابة الملف على القرص";
break;
default:
echo "خطأ غير معروف";
}
?>
رفع ملف آمن مع التحقق
<?php
// secure_upload.php
$errors = [];
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES['photo']) && $_FILES['photo']['error'] === 0) {
$file = $_FILES['photo'];
// الحصول على معلومات الملف
$filename = $file['name'];
$temp_path = $file['tmp_name'];
$file_size = $file['size'];
$file_type = $file['type'];
// الحصول على امتداد الملف
$file_ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
// 1. التحقق من امتداد الملف
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($file_ext, $allowed_extensions)) {
$errors[] = "نوع ملف غير صحيح. المسموح: " . implode('، ', $allowed_extensions);
}
// 2. التحقق من نوع MIME
$allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($file_type, $allowed_mime_types)) {
$errors[] = "نوع MIME غير صحيح";
}
// 3. التحقق من حجم الملف (الحد الأقصى 5 ميجابايت)
$max_size = 5 * 1024 * 1024; // 5 ميجابايت بالبايتات
if ($file_size > $max_size) {
$errors[] = "الملف كبير جدًا. الحد الأقصى: 5 ميجابايت";
}
// 4. التحقق من أنه صورة فعلية
$image_info = getimagesize($temp_path);
if ($image_info === false) {
$errors[] = "الملف ليس صورة صالحة";
}
// إذا لم توجد أخطاء، المتابعة مع الرفع
if (empty($errors)) {
// إنشاء اسم ملف فريد لمنع الكتابة فوق
$new_filename = uniqid('img_', true) . '.' . $file_ext;
// تعيين دليل الرفع
$upload_dir = 'uploads/';
// إنشاء الدليل إذا لم يكن موجودًا
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
$destination = $upload_dir . $new_filename;
// نقل الملف المرفوع
if (move_uploaded_file($temp_path, $destination)) {
$success = true;
$uploaded_file = $new_filename;
} else {
$errors[] = "فشل في نقل الملف المرفوع";
}
}
} else {
$error_code = $_FILES['photo']['error'];
if ($error_code === UPLOAD_ERR_NO_FILE) {
$errors[] = "يرجى اختيار ملف";
} else {
$errors[] = "رمز خطأ الرفع: $error_code";
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>رفع ملف آمن</title>
<style>
.error { color: red; }
.success { color: green; }
img { max-width: 300px; margin-top: 10px; }
</style>
</head>
<body>
<h1>رفع صورة</h1>
<?php if (!empty($errors)): ?>
<div class="error">
<h3>أخطاء:</h3>
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div class="success">
<h3>تم الرفع بنجاح!</h3>
<p>اسم الملف: <?php echo htmlspecialchars($uploaded_file); ?></p>
<img src="uploads/<?php echo htmlspecialchars($uploaded_file); ?>" alt="صورة مرفوعة">
</div>
<?php endif; ?>
<form method="post" action="" enctype="multipart/form-data">
<label for="photo">اختر صورة (JPG، PNG، GIF - الحد الأقصى 5 ميجابايت):</label>
<input type="file" id="photo" name="photo" accept="image/*">
<button type="submit">رفع</button>
</form>
</body>
</html>
رفع ملفات متعددة
<!-- نموذج HTML لملفات متعددة -->
<form method="post" action="upload_multiple.php" enctype="multipart/form-data">
<label>اختر صورًا متعددة:</label>
<input type="file" name="photos[]" multiple accept="image/*">
<button type="submit">رفع الكل</button>
</form>
<?php
// upload_multiple.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES['photos'])) {
$upload_dir = 'uploads/';
$uploaded_files = [];
$errors = [];
// عد عدد الملفات
$file_count = count($_FILES['photos']['name']);
// المرور عبر كل ملف
for ($i = 0; $i < $file_count; $i++) {
// التحقق من أن الملف ليس به خطأ
if ($_FILES['photos']['error'][$i] === 0) {
$filename = $_FILES['photos']['name'][$i];
$temp_path = $_FILES['photos']['tmp_name'][$i];
$file_size = $_FILES['photos']['size'][$i];
$file_ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
// التحقق
if (!in_array($file_ext, $allowed)) {
$errors[] = "$filename: نوع ملف غير صحيح";
continue;
}
if ($file_size > 5 * 1024 * 1024) {
$errors[] = "$filename: الملف كبير جدًا";
continue;
}
// إنشاء اسم ملف فريد
$new_filename = uniqid('img_', true) . '.' . $file_ext;
$destination = $upload_dir . $new_filename;
// رفع الملف
if (move_uploaded_file($temp_path, $destination)) {
$uploaded_files[] = $new_filename;
} else {
$errors[] = "$filename: فشل الرفع";
}
}
}
echo "<h3>تم رفع " . count($uploaded_files) . " ملفات</h3>";
foreach ($uploaded_files as $file) {
echo "<p>$file</p>";
}
if (!empty($errors)) {
echo "<h3>أخطاء:</h3>";
foreach ($errors as $error) {
echo "<p style='color:red;'>$error</p>";
}
}
}
}
?>
دوال مساعدة لرفع الملفات
<?php
// دالة مساعدة لتنسيق حجم الملف
function formatFileSize($bytes) {
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' جيجابايت';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' ميجابايت';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' كيلوبايت';
} else {
return $bytes . ' بايت';
}
}
// دالة مساعدة لإنشاء اسم ملف آمن
function generateSafeFilename($original_name) {
$ext = strtolower(pathinfo($original_name, PATHINFO_EXTENSION));
return uniqid('file_', true) . '.' . $ext;
}
// دالة مساعدة للتحقق من الصورة
function validateImage($file) {
$errors = [];
// التحقق من وجود الملف
if (!isset($file) || $file['error'] !== 0) {
$errors[] = "خطأ في رفع الملف";
return $errors;
}
// التحقق من الامتداد
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
if (!in_array($ext, $allowed)) {
$errors[] = "امتداد ملف غير صحيح";
}
// التحقق من نوع MIME
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($file['type'], $allowed_mime)) {
$errors[] = "نوع MIME غير صحيح";
}
// التحقق من الحجم (5 ميجابايت)
if ($file['size'] > 5 * 1024 * 1024) {
$errors[] = "الملف كبير جدًا (الحد الأقصى 5 ميجابايت)";
}
// التحقق من أنها صورة حقيقية
if (getimagesize($file['tmp_name']) === false) {
$errors[] = "الملف ليس صورة صالحة";
}
return $errors;
}
// الاستخدام
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$errors = validateImage($_FILES['photo']);
if (empty($errors)) {
$new_filename = generateSafeFilename($_FILES['photo']['name']);
$destination = 'uploads/' . $new_filename;
if (move_uploaded_file($_FILES['photo']['tmp_name'], $destination)) {
echo "تم الرفع: $new_filename (" . formatFileSize($_FILES['photo']['size']) . ")";
}
} else {
foreach ($errors as $error) {
echo "<p style='color:red;'>$error</p>";
}
}
}
?>
معالجة الصور (تغيير الحجم)
<?php
// دالة لتغيير حجم الصورة المرفوعة
function resizeImage($source, $destination, $max_width, $max_height) {
// الحصول على الأبعاد الأصلية
list($orig_width, $orig_height, $type) = getimagesize($source);
// حساب الأبعاد الجديدة
$ratio = min($max_width / $orig_width, $max_height / $orig_height);
$new_width = intval($orig_width * $ratio);
$new_height = intval($orig_height * $ratio);
// إنشاء صورة من المصدر بناءً على النوع
switch ($type) {
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($source);
break;
case IMAGETYPE_PNG:
$image = imagecreatefrompng($source);
break;
case IMAGETYPE_GIF:
$image = imagecreatefromgif($source);
break;
default:
return false;
}
// إنشاء صورة جديدة
$new_image = imagecreatetruecolor($new_width, $new_height);
// الحفاظ على الشفافية لـ PNG/GIF
if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_GIF) {
imagealphablending($new_image, false);
imagesavealpha($new_image, true);
$transparent = imagecolorallocatealpha($new_image, 0, 0, 0, 127);
imagefill($new_image, 0, 0, $transparent);
}
// تغيير الحجم
imagecopyresampled($new_image, $image, 0, 0, 0, 0,
$new_width, $new_height, $orig_width, $orig_height);
// حفظ الصورة المُعدلة الحجم
switch ($type) {
case IMAGETYPE_JPEG:
imagejpeg($new_image, $destination, 90);
break;
case IMAGETYPE_PNG:
imagepng($new_image, $destination, 9);
break;
case IMAGETYPE_GIF:
imagegif($new_image, $destination);
break;
}
// تحرير الذاكرة
imagedestroy($image);
imagedestroy($new_image);
return true;
}
// الاستخدام بعد الرفع
if (move_uploaded_file($_FILES['photo']['tmp_name'], $destination)) {
// إنشاء صورة مصغرة
$thumbnail = 'uploads/thumb_' . $new_filename;
resizeImage($destination, $thumbnail, 300, 300);
echo "تم رفع الصورة وإنشاء صورة مصغرة";
}
?>
أفضل ممارسات الأمان
قائمة مراجعة أمان رفع الملفات:
- تحقق دائمًا من امتداد الملف ونوع MIME
- استخدم
getimagesize()للتحقق من الصور الحقيقية - حدد حدودًا لحجم الملف الأقصى
- أنشئ أسماء ملفات فريدة (استخدم
uniqid()) - احفظ الرفوعات خارج جذر الويب عندما يكون ذلك ممكنًا
- لا تنفذ أبدًا الملفات المرفوعة
- عيّن أذونات الملفات الصحيحة (0644 للملفات)
- تحقق من محتوى الملف، وليس الامتداد فقط
- استخدم
move_uploaded_file()(يتحقق من الملف) - افحص الملفات بمضاد فيروسات إذا أمكن
الثغرات الشائعة:
- الثقة في اسم الملف المقدم من المستخدم (أعد تسمية الملفات!)
- عدم التحقق من نوع MIME (يمكن تزويره)
- السماح بأنواع ملفات قابلة للتنفيذ (.php، .exe)
- تخزين الرفوعات في أدلة يمكن الوصول إليها عبر الويب بدون حماية
- عدم تحديد حجم الملف (هجوم DoS)
تمرين تطبيقي
المهمة: أنشئ نظام رفع صورة الملف الشخصي مع:
- نموذج رفع يقبل الصور فقط (JPG، PNG، GIF)
- التحقق من نوع الملف ونوع MIME والحجم (الحد الأقصى 2 ميجابايت)
- إنشاء اسم ملف فريد مع طابع زمني
- إنشاء نسختين: أصلية وصورة مصغرة (150×150)
- عرض كلتا النسختين بعد الرفع
- تخزين الملفات في دليل "uploads/"
- إظهار حجم الملف بصيغة قابلة للقراءة
إضافي: أضف القدرة على حذف الرفوعات السابقة وتطبيق واجهة رفع السحب والإفلات.
الملخص
في هذا الدرس، تعلمت:
- بنية المتغير العمومي الفائق
$_FILES - إنشاء نماذج رفع مع
enctypeالصحيح - معالجة رفع الملفات الأساسية باستخدام
move_uploaded_file() - رموز أخطاء الرفع والمعالجة
- التحقق الآمن من الملفات (الامتداد، نوع MIME، الحجم، المحتوى)
- إنشاء أسماء ملفات فريدة
- التعامل مع رفع ملفات متعددة
- دوال مساعدة لعمليات الملفات
- تغيير حجم الصور وإنشاء صور مصغرة
- أفضل ممارسات الأمان الحاسمة
بعد ذلك، سنستكشف الجلسات والكوكيز في PHP!