طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 132 · تقنيات تطوير الواجهات الخلفية
الدرس 17 · الوحدة 8: تنفيذ كود برمجي لمعالجة وإدارة البيانات

استقبال البيانات وتحليلها

من مربعات الاختيار إلى ملفات Excel: البيانات كما تصل فعلًا. ⏱ نحو 35 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تستقبل الحقول متعددة القيم بأسماء تنتهي بـ [] وتصفّيها.
  • تستقبل الملفات المرفوعة عبر $_FILES وتتحقق من نوعها وحجمها.
  • تحلل بيانات CSV إلى صفوف ترابطية وتستخرج منها إحصاءات.
  • تطبّق قواعد أمان رفع الملفات.

١حقول تحمل أكثر من قيمة

بعض الحقول يختار منها المستخدم أكثر من قيمة: مربعات اختيار Checkboxes لمهاراته، أو قائمة متعددة. إن سمّيت الحقل باسم ينتهي بـ [] فسيجمع PHP القيم في مصفوفة:

<?phpif ($_SERVER['REQUEST_METHOD'] === 'POST') {    $skills = $_POST['skills'] ?? [];      // مصفوفة، أو غير موجودة إن لم يختر شيئًا    $allowed = ['HTML', 'CSS', 'PHP', 'SQL'];    $skills = array_values(array_intersect($skills, $allowed));   // تجاهل أي قيمة غريبة    echo "<p>اخترت " . count($skills) . ": " . implode('، ', $skills) . "</p>";    exit;}?><form method="post">  <label><input type="checkbox" name="skills[]" value="HTML" checked> HTML</label>  <label><input type="checkbox" name="skills[]" value="CSS"> CSS</label>  <label><input type="checkbox" name="skills[]" value="PHP" checked> PHP</label>  <label><input type="checkbox" name="skills[]" value="SQL"> SQL</label>  <button>حفظ</button></form>

الطلب الثاني يرسل skills[] ثلاث مرات، إحداها قيمة غريبة أضافها مهاجم بطلب يدوي. array_intersect أبقت المسموح فقط.

مربع الاختيار غير المؤشَّر لا يُرسل أصلًا. فإن لم يختر المستخدم أي شيء لا يوجد المفتاح skills، ولهذا كتبنا ?? [].

٢رفع الملفات

لرفع ملف يحتاج النموذج شرطين: الطريقة post، والسمة enctype="multipart/form-data"؛ فيُرسل الطلب بصيغة خاصة تقسم الجسم إلى أجزاء، كل حقل وكل ملف في جزء. ويضع PHP الملف المرفوع في مجلد مؤقت، ويصف لك المصفوفة $_FILES:

المفتاحمعناه
$_FILES['sheet']['name']اسم الملف في جهاز المستخدم (لا تثق به).
['tmp_name']مسار النسخة المؤقتة على الخادم.
['size']الحجم بالبايت.
['error']UPLOAD_ERR_OK (صفر) إن نجح الرفع.

وهذا تطبيق كامل: يرفع المشرف ملف درجات CSV، فيحلّله الخادم ويعرض متوسط كل مقرر:

<?phpif ($_SERVER['REQUEST_METHOD'] !== 'POST') { ?>  <form method="post" enctype="multipart/form-data">    <p>ملف الدرجات (CSV): <input type="file" name="sheet" accept=".csv"></p>    <button>حلّل</button>  </form><?php exit; } $f = $_FILES['sheet'] ?? null;if (!$f || $f['error'] !== UPLOAD_ERR_OK) {    http_response_code(400);    exit('لم يصل ملف');}if (strtolower(pathinfo($f['name'], PATHINFO_EXTENSION)) !== 'csv' || $f['size'] > 100_000) {    http_response_code(422);    exit('نقبل ملفات CSV حتى 100 كيلوبايت فقط');} $rows = array_map('str_getcsv', file($f['tmp_name'], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES));$head = array_shift($rows);          // السطر الأول: أسماء الأعمدة $by = [];foreach ($rows as $r) {    $row = array_combine($head, $r);    $by[$row['course']][] = (float) $row['grade'];}echo "<p>الملف: " . htmlspecialchars($f['name']) . " · " . count($rows) . " صفوف</p><ul>";foreach ($by as $course => $grades) {    $avg = round(array_sum($grades) / count($grades), 1);    echo "<li>$course: " . count($grades) . " متدربين، المتوسط $avg</li>";}echo "</ul>";

افتح الطلب الثاني لترى شكل multipart: حدود (boundary) تفصل الأجزاء. والطلب الثالث ملف صورة فرُفض بـ 422.

🧪 مختبر الخادماضغط ▶ تشغيل، ثم اختر ملف CSV من جهازك (احفظ grades.csv من التبويب أعلاه في ملف إن شئت) واضغط «حلّل».

٣تحليل CSV خطوة بخطوة

CSV (قيم مفصولة بفواصل) أبسط صيغة لتبادل الجداول، ويصدّرها Excel ونظام رايات وغيرهما. خطوات تحليلها:

<?php$text = "name,course,grade\nسعود,PHP,92\nنورة,PHP,88\nخالد,SQL,75"; $lines = explode("\n", trim($text));$rows = array_map('str_getcsv', $lines);   // كل سطر ← مصفوفة قيم$head = array_shift($rows);                // انزع سطر العناوين foreach ($rows as $r) {    $row = array_combine($head, $r);       // ['name' => 'سعود', 'course' => 'PHP', …]    echo "{$row['name']} ← {$row['grade']}\n";} $grades = array_column(array_map(fn($r) => array_combine($head, $r), $rows), 'grade');echo "الأعلى: " . max($grades);
الدالةعملها
str_getcsv($line)تقسم سطر CSV إلى قيم، وتفهم القيم بين علامتي تنصيص التي فيها فواصل.
array_shift($a)تنزع العنصر الأول وتعيده (سطر العناوين).
array_combine($keys, $values)تصنع مصفوفة ترابطية من مصفوفتين.
array_column($rows, 'grade')تستخرج عمودًا واحدًا من صفوف ترابطية.

٤أمان الملفات المرفوعة

رفع الملفات من أخطر الوظائف: لو قبلت ملف shell.php ووضعته في مجلد مكشوف، يستطيع المهاجم تشغيله على خادمك!

<?php$ext = strtolower(pathinfo($_FILES['photo']['name'], PATHINFO_EXTENSION));if (!in_array($ext, ['jpg', 'jpeg', 'png'], true)) exit('نوع غير مسموح'); $name = bin2hex(random_bytes(8)) . '.' . $ext;move_uploaded_file($_FILES['photo']['tmp_name'], __DIR__ . '/../uploads/' . $name);

توقّع وتحقّق

ليصل PHP عدة قيم لحقل واحد يكون اسمه:

فتصبح مصفوفة.

السمة اللازمة في النموذج لرفع الملفات:

مع method="post".

أي دالة تحوّل سطر CSV إلى مصفوفة قيم؟

تفهم علامات التنصيص.

أأمن طريقة لتسمية الملف المرفوع عند حفظه:

لا تثق بالاسم الأصلي.

الخلاصة

تحدٍّ برمجي: تقرير الحضور من CSV

متقدم

المشرف يلصق بيانات الحضور (CSV) في حقل نصي ويرسلها POST. اكتب attendance.php يحللها.

المطلوب:
  • الحقل اسمه data، وأول سطر فيه العناوين: name,present والقيمة present إما 1 أو 0.
  • اطبع لكل متدرب نسبة حضوره (عدد الأسطر التي present فيها 1 ÷ عدد أسطره) بالشكل: <li>سعود: 67%</li> مقرّبة لعدد صحيح، بترتيب أول ظهور.
  • ومن نسبته أقل من 75% يضاف بعد النسبة: ⚠ حرمان (مثال: <li>سعود: 67% ⚠ حرمان</li>).
  • إن لم يُرسل data أو كان فارغًا: 422 والنص لا توجد بيانات.
👀 اعرض الاستجابة المطلوبة
✓ التحقق الآلي: يرسل المختبر هذه الطلبات إلى خادمك بالترتيب ثم يفحص الاستجابات
  1. POST /attendance.php (data=name,present سعود,1 نورة,1 سعود,0 نورة,1 سعود,1 نورة,1 خالد,0)
  2. POST /attendance.php (data=name,present ريم,1 ريم,1 ريم,1 ريم,0)
  3. POST /attendance.php
  • الكود يعمل بلا أخطاء أو تحذيرات
  • سعود 67% ⚠ حرمان، ونورة 100%، وخالد 0% ⚠ حرمان، بالترتيب
  • ريم: 75% بلا حرمان (الحد 75 ليس أقل منه)
  • بلا بيانات ← 422
  • تستخدم str_getcsv

💡 تلميح 1
اجمع لكل اسم عدد الأسطر وعدد مرات الحضور في مصفوفة ترابطية مفتاحها الاسم.
💡 تلميح 2
المصفوفة الترابطية تحفظ ترتيب أول إضافة، فيخرج الترتيب صحيحًا تلقائيًّا.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

<?php$data = trim($_POST['data'] ?? '');if ($data === '') {    http_response_code(422);    exit('لا توجد بيانات');}$rows = array_map('str_getcsv', explode("\n", $data));$head = array_map('trim', array_shift($rows)); $stats = [];foreach ($rows as $r) {    $row = array_combine($head, array_map('trim', $r));    $n = $row['name'];    $stats[$n]['total'] = ($stats[$n]['total'] ?? 0) + 1;    $stats[$n]['present'] = ($stats[$n]['present'] ?? 0) + (int) $row['present'];} echo "<ul>";foreach ($stats as $name => $s) {    $pct = round($s['present'] / $s['total'] * 100);    echo "<li>$name: $pct%" . ($pct < 75 ? " ⚠ حرمان" : "") . "</li>";}echo "</ul>";
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس