في هذا الدرس 6تصفّح المواضيع
يتطلب جدول المستخدمين users الآمن مفتاحاً أساسياً تلقائياً، وقيد تفرد UNIQUE على عمود البريد email، وعمود VARCHAR(255) لحفظ تجزئة كلمة المرور.
- اجعل طول عمود كلمة المرور دائماً
VARCHAR(255)حتى لا يتم اقتطاع أي تجزئة مستقبلية لـ Bcrypt أو Argon2id. - أضف قيد
UNIQUEعلى مستوى قاعدة البيانات لعمودemailلمنع تكرار الحسابات. - أضف عمود
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMPلتوثيق وقت التسجيل.
تفرض الهيكلة قيد NOT NULL على جميع الحقول الأساسية وقيد UNIQUE على البريد الإلكتروني بترميز utf8mb4.
<?php
$sql = "CREATE TABLE IF NOT EXISTS users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(190) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
echo "Users DDL schema ready (" . strlen($sql) . " bytes)\n";
قبل الاتصال بقاعدة البيانات، هذّب الاسم والبريد بـ trim، وتحقق من صيغة البريد عبر filter_var، وافحص طول كلمة المرور وتطابق تأكيدها.
- وحّد حالة أحرف البريد عبر
strtolower(trim($_POST["email"] ?? ""))لمنع تكرار نفس البريد بأحرف كبيرة. - لا تستخدم
trimمع كلمة المرور، ولكن تحقق من أن طولها لا يقل عن 8 محارفstrlen($password) >= 8. - اجمع رسائل الخطأ لكل حقل في مصفوفة ترابطية
$errorsلعرضها تحت كل حقل.
اجتازت الحقول الثلاثة شروط التحقق وتم توحيد البريد الإلكتروني للأحرف الصغيرة.
<?php
$name = trim('Rami Adel');
$email = strtolower(trim('[email protected]'));
$pass = 'Secret#2026';
$confirm = 'Secret#2026';
$errors = [];
if ($name === '') $errors['name'] = 'Name is required.';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors['email'] = 'Invalid email.';
if (strlen($pass) < 8 || $pass !== $confirm) $errors['password'] = 'Invalid password.';
echo "Errors count: " . count($errors) . " | Normalized: $email\n";
استعلم من جدول users عبر استعلام محضر SELECT id FROM users WHERE email = :email LIMIT 1 لإظهار رسالة واضحة إذا كان البريد مسجلاً مسبقاً.
- استخدم
$stmt->fetchColumn()للتحقق مما إذا كان الاستعلام يرجع معرف مستخدم موجود. - حتى مع هذا الفحص المسبق، احتفظ بقيد
UNIQUEفي الجدول لمنع تضارب الطلبات المتزامنة. - التزم دائماً بالمعاملات المربوطة (
:email) لمنع حقن SQL.
ترجع fetchColumn() معرف السجل المطابق، مما ينبه التطبيق بأن البريد مسجل بالفعل.
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT UNIQUE)');
$pdo->exec("INSERT INTO users (email) VALUES ('[email protected]')");
$stmt = $pdo->prepare('SELECT id FROM users WHERE email = :email LIMIT 1');
$stmt->execute(['email' => '[email protected]']);
$exists = (bool) $stmt->fetchColumn();
echo $exists ? "Email already registered!\n" : "Email available.\n";
بمجرد نجاح التحقق، جزّئ كلمة المرور عبر password_hash($password, PASSWORD_DEFAULT) وأدرج السجل باستعلام PDO محضر.
- لا تخزن أو تطبع أو تسجل كلمة المرور النصية الخام في أي مكان أبداً.
- تولّد دالة
password_hash()ملحاً تشفيرياً عشوائياً تلقائياً وتدمجه داخل النص الناتج. - احصل على معرف المستخدم الجديد عبر
(int) $pdo->lastInsertId().
تمت تجزئة كلمة المرور بأمان قبل تنفيذ استعلام الإدراج المحضر، وأرجعت lastInsertId() المعرف 1.
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, password_hash TEXT)');
$hash = password_hash('StrongPass!99', PASSWORD_DEFAULT);
$stmt = $pdo->prepare('INSERT INTO users (name, email, password_hash) VALUES (:n, :e, :p)');
$stmt->execute(['n' => 'Nour', 'e' => '[email protected]', 'p' => $hash]);
echo "Created User ID: " . $pdo->lastInsertId() . " (Hash len: " . strlen($hash) . ")\n";
بعد إنشاء الحساب، جدّد معرف الجلسة، وخزّن رسالة ترحيب مؤقتة في $_SESSION["flash"]، وأرسل تحويل 303 See Other.
- التحويل بعد
POST /registerيمنع محاولة إعادة إرسال بيانات التسجيل عند تحديث الصفحة. - تدوير معرف الجلسة فور التسجيل يحمي المستخدم من ثغرات تثبيت الجلسة.
- احذف رسالة التنبيه المؤقتة فور قراءتها وعرضها في صفحة الوجهة.
تُقرأ رسالة الترحيب مرة واحدة لعرضها للمستخدم ثم تُحذف فوراً من الجلسة عبر unset().
<?php
$_SESSION = [];
$_SESSION['user_id'] = 42;
$_SESSION['flash_success'] = 'Welcome aboard! Your account is ready.';
$flash = $_SESSION['flash_success'] ?? null;
unset($_SESSION['flash_success']);
echo "Flash Alert: $flash\n";
echo "Remaining Flash: " . (isset($_SESSION['flash_success']) ? 'Yes' : 'Cleared') . "\n";
راجع خطوات الأمان الست المتسلسلة اللازمة لبناء نقطة نهاية تسجيل مستخدمين جاهزة للإنتاج.
- تحقق من توكن CSRF عبر
hash_equals()قبل قراءة حقول النموذج. - وحّد صيغة البريد وتحقق من صحة الحقول وطول كلمة المرور.
- جزّئ كلمة المرور بـ
password_hash()، وأدرجها باستعلام PDO محضر، وحوّل المسار بنمط PRG.
تنفيذ هذه الخطوات الخمس بالترتيب يحمي من ثغرات CSRF وحقن SQL وتسرب كلمات المرور وتكرار الإرسال.
| مرحلة التسجيل | الأداة / الدالة في PHP | التهديد الأمني الذي تمنعه |
|---|---|---|
| CSRF Check | hash_equals($_SESSION["csrf"], $token) | Cross-Site Request Forgery |
| Email Normalization | strtolower(trim($email)) + FILTER_VALIDATE_EMAIL | Duplicate case variants & malformed emails |
| Password Hashing | password_hash($pass, PASSWORD_DEFAULT) | Plaintext & rainbow-table password leaks |
| PDO Prepared Insert | $pdo->prepare("INSERT INTO users ...") | SQL Injection (SQLi) |
| PRG Redirect | header("Location: /dashboard", true, 303) | Duplicate POST form resubmission |
<?php
$steps = [
1 => 'Verify CSRF token (hash_equals)',
2 => 'Validate & normalize inputs',
3 => 'Check PDO email uniqueness',
4 => 'Hash password & INSERT row',
5 => 'Set flash & 303 Redirect (PRG)',
];
foreach ($steps as $n => $label) {
echo "Step $n: $label\n";
}
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
راجع خطوات الأمان الست المتسلسلة اللازمة لبناء نقطة نهاية تسجيل مستخدمين جاهزة للإنتاج. تحقق من توكن CSRF عبر hash_equals() قبل قراءة حقول النموذج. وحّد صيغة البريد وتحقق من صحة الحقول وطول كلمة المرور. جزّئ كلمة المرور بـ password_hash()، وأدرجها باستعلام PDO محضر، وحوّل المسار بنمط PRG.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 User Registration نظام تسجيل المستخدمين
- ابنِ نظام تسجيل مستخدمين متكاملاً في PHP 8 يجمع التحقق من CSRF وتنقية المدخلات وفحص عدم تكرار البريد وتشفير كلمة المرور وحفظها بـ PDO.
- للمراجعة
- 02 Designing the users Database Table تصميم جدول المستخدمين users في قاعدة البيانات
- المفهوم الأساسي لقسم تصميم جدول المستخدمين users في قاعدة البيانات في لغة PHP الحديثة.
- للمراجعة
- 03 Validating Registration Inputs and Rules التحقق من حقول التسجيل وتطابق كلمة المرور
- الصياغة البرمجية والتطبيق العملي المباشر لقسم التحقق من حقول التسجيل وتطابق كلمة المرور.
- للمراجعة
- 04 Checking Email Uniqueness with PDO فحص عدم مسجلية البريد الإلكتروني مسبقاً عبر PDO
- قواعد التحقق وأفضل الممارسات القياسية الخاصة بقسم فحص عدم مسجلية البريد الإلكتروني مسبقاً عبر PDO.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 PHP PHP
- PHP: Hypertext Preprocessor
- لغة البرمجة النصية على جانب الخادم لبناء تطبيقات الويب الديناميكية وواجهات البرمجة.
- للمراجعة
- 02 CLI CLI
- Command-Line Interface
- واجهة سطر الأوامر التي تتيح تشغيل ملفات PHP مباشرة عبر الطرفية.
- للمراجعة
- 03 FPM FPM
- FastCGI Process Manager
- مدير عمليات FastCGI عالي الأداء لمعالجة طلبات خادم الويب في PHP.
- للمراجعة
- 04 HTML HTML
- HyperText Markup Language
- لغة الترميز القياسية التي يولدها محرك PHP ويرسلها إلى متصفح العميل بعد التنفيذ.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
أنشئ ملف PHP محلياً ونفّذ المثال البرمجي المشروح في قسم تصميم جدول المستخدمين users في قاعدة البيانات للتحقق من المخرجات.
-
بدّل بين الحالات الثلاث في المحاكي التفاعلي ضمن قسم التحقق من حقول التسجيل وتطابق كلمة المرور وقارن مخرجات التنفيذ.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي8 أسئلة تنتظرك +
ما طول العمود الموصى به لتخزين مخرجات الدالة password_hash() في جدول المستخدمين بقاعدة البيانات؟
يوصي دليل PHP الرسمي بطول VARCHAR(255) لاستيعاب تجزئة Bcrypt وArgon2id الأطول مستقبلاً.
لماذا يجب توحيد البريد المدخل عبر strtolower(trim($email)) قبل فحص عدم تكراره وحفظه؟
يضمن التوحيد معاملة [email protected] و[email protected] كبريد إلكتروني واحد.
لماذا لا ينبغي أبداً تطبيق دالة trim() على كلمة مرور المستخدم قبل تجزئتها؟
تعديل كلمة المرور بصمت قبل التجزئة يربك المستخدم إذا اعتمد على مسافات مقصودة.
لماذا يظل وجود قيد UNIQUE على عمود البريد في قاعدة البيانات ضرورياً حتى لو قام PHP بفحص SELECT أولاً؟
فقط قيد UNIQUE في قاعدة البيانات هو الذي يضمن عدم التكرار الذري عند تزامن الطلبات.
أي دالة في PDO ترجع المعرف التلقائي (ID) للمستخدم المسجل حديثاً فور تنفيذ INSERT؟
ترجع lastInsertId() آخر معرف تلقائي تم توليده في نفس الاتصال.
ما الحقول التي يجب الاحتفاظ بها كمدخلات سابقة عند إعادة التوجيه بعد خطأ في نموذج التسجيل؟
لا يجوز أبداً تخزين كلمات المرور في الجلسة أو إعادة طباعتها في خاصية value داخل HTML.
ماذا تتضمن السلسلة النصية التي ترجعها الدالة password_hash("Secret123", PASSWORD_DEFAULT)؟
دمج الخوارزمية والتكلفة والملح يسمح لدالة password_verify() بالتحقق دون الحاجة لعمود ملح منفصل.
لماذا يجب أن يقوم معالج التسجيل بالتحويل عبر الرمز 303 See Other بعد إدراج المستخدم الجديد؟
يلزم الرمز 303 المتصفح بتحويل الطلب التالي إلى GET آمن.