استدعِ session_start() في أعلى الصفحة، ثم اقرأ $_SESSION['key'] واكتبها مثل أي مصفوفة: تحفظها PHP على الخادم وتجدها مرة أخرى في الطلب التالي للزائر. ولقيمة صغيرة يجب أن يحتفظ بها المتصفح، استدعِ setcookie('name', 'value', time() + 86400) واقرأها في الطلبات اللاحقة من $_COOKIE['name'].
<?php
session_start(); // before any output
setcookie('theme', 'dark', time() + 60 * 60 * 24 * 30); // 30 days, also before output
$_SESSION['visits'] = ($_SESSION['visits'] ?? 0) + 1;
$theme = $_COOKIE['theme'] ?? 'light'; // set on an earlier visit
echo "You have opened this page {$_SESSION['visits']} times.";
لا معنى للجلسات وملفات تعريف الارتباط إلا عبر عدة طلبات HTTP من متصفح، لذا فالأمثلة التي تحتاج إليها شيفرة عادية. أما المنطق المحيط بها فهو PHP عادية على المصفوفات، وذلك الجزء يعمل هنا: في كل كتلة قابلة للتشغيل، تقوم $session مقام $_SESSION، وكل استدعاء طلب واحد.
كيف تعمل جلسة PHP
- تنشئ أول
session_start()معرّفًا عشوائيًا، وترسله إلى المتصفح في ملف تعريف ارتباط اسمهPHPSESSID، وتعطيك$_SESSIONفارغة. - في نهاية الطلب تحفظ PHP المصفوفة
$_SESSIONعلى الخادم (في ملفات افتراضيًا، أو Redis، أو قاعدة بيانات). - في الطلب التالي يرسل المتصفح ملف تعريف الارتباط مرة أخرى، فتقرأ
session_start()المعرّف وتحمّل المصفوفة المحفوظة.
لا يرى المتصفح البيانات أبدًا، بل المعرّف فقط. ولهذا مكان معرّف المستخدم المسجل دخوله $_SESSION لا ملف تعريف ارتباط خاص بك أبدًا: يستطيع الزائر تعديل ملفات تعريف الارتباط إلى أي قيمة، لكنه لا يستطيع تعديل $_SESSION.
تسجيل دخول مستخدم وخروجه بجلسة
تسجيل الدخول هو: افحص كلمة المرور، وأعد توليد معرّف الجلسة، وخزّن معرّف المستخدم. وتسجيل الخروج هو العكس، وله ثلاث خطوات.
<?php
// login.php
session_start();
$user = find_user_by_email($_POST['email'] ?? '');
if ($user && password_verify($_POST['password'] ?? '', $user['password_hash'])) {
session_regenerate_id(true); // new id after login: stops session fixation
$_SESSION['user_id'] = $user['id'];
header('Location: /dashboard.php');
exit;
}
$error = 'Wrong email or password.';
<?php
// any protected page
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: /login.php');
exit;
}
<?php
// logout.php
session_start();
$_SESSION = []; // 1. forget the data
$p = session_get_cookie_params(); // 2. delete the cookie
setcookie(session_name(), '', time() - 3600, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
session_destroy(); // 3. delete the stored session
header('Location: /');
exit;
لـ session_regenerate_id(true) أهمية أكبر مما يبدو. إذا استطاع مهاجم جعل ضحية تستخدم معرّف جلسة يعرفه المهاجم (مثلًا عبر رابط)، وبقي المعرّف نفسه بعد تسجيل الدخول، فالمهاجم مسجل دخوله أيضًا. والمعرّف الجديد عند تسجيل الدخول يسد تلك الثغرة. أما فحص كلمة المرور نفسه فمشروح في صفحة password_hash.
إنهاء الجلسة بعد عدم النشاط
التنظيف الخاص بـ PHP (session.gc_maxlifetime، 1440 ثانية افتراضيًا) يعمل عشوائيًا وليس مهلة يُعتمد عليها. خزّن وقت آخر طلب وافحصه بنفسك. تستخدم الكتلة طوابع زمنية ثابتة لترى كل حالة؛ غيّر $timeout أو الأوقات.
الفجوة من 09:05 إلى 09:16:40 هي 700 ثانية، تحت حد الدقائق الخمس عشرة، فيبقى المستخدم داخلًا. والفجوة التالية 1000 ثانية، فتُمسح الجلسة.
الرسائل المؤقتة
الرسالة المؤقتة تُضبط في طلب وتُعرض مرة واحدة في الطلب التالي، عادة "Saved." بعد تحويل. خزّنها في الجلسة، ثم اقرأها واحذفها في خطوة واحدة:
ضبط ملف تعريف ارتباط بـ setcookie
تأخذ setcookie() الاسم والقيمة ومصفوفة خيارات (PHP 7.3 وما بعدها). اضبط هذه الخيارات الأربعة على كل ملف تعريف ارتباط ما لم يكن لديك سبب لعدم ذلك:
<?php
setcookie('theme', 'dark', [
'expires' => time() + 60 * 60 * 24 * 30, // 30 days; 0 means "until the browser closes"
'path' => '/', // send it on every page, not just this folder
'secure' => true, // only over HTTPS
'httponly' => true, // JavaScript cannot read it
'samesite' => 'Lax', // not sent on most cross-site requests
]);
$theme = $_COOKIE['theme'] ?? 'light';
أمران يوقعان الناس:
- لا تتغير
$_COOKIEفي الطلب نفسه. فهي تحمل ما أرسله المتصفح مع هذا الطلب. وملف تعريف الارتباط الذي تضبطه الآن يظهر في$_COOKIEفي الطلب التالي. - ملفات تعريف الارتباط ترويسات، لذا يجب أن تعمل
setcookie()قبل أي مخرجات، تمامًا مثلsession_start()وheader().
لحذف ملف تعريف ارتباط، اضبطه مرة أخرى بالاسم وpath نفسيهما مع تاريخ انتهاء في الماضي: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']).
تخزين مصفوفة في ملف تعريف ارتباط
قيمة ملف تعريف الارتباط نص لا يتجاوز نحو 4 KB، لذا خزّن البيانات المنظمة على شكل JSON، وعامل ما يعود على أنه غير موثوق: يستطيع الزائر تغييره. ولا تستخدم unserialize() على ملف تعريف ارتباط أبدًا: فهي قادرة على إنشاء كائنات من أصنافك وتشغيل توابعها السحرية، بينما لا تعيد json_decode() إلا مصفوفات ونصوصًا وأعدادًا وقيمًا منطقية وnull أو stdClass.
تشرح صفحة json_encode الأعلام. ولأي شيء أكبر من بضع قيم قصيرة، احتفظ بالبيانات في الجلسة (أو قاعدة بيانات) ولا تخزّن في ملف تعريف الارتباط إلا مفتاحًا.
رموز «تذكرني»
يجب ألا يحمل ملف تعريف الارتباط «تذكرني» معرّف المستخدم ولا تجزئة كلمة المرور. خزّن رمزًا عشوائيًا طويلًا في ملف تعريف الارتباط وتجزئته فقط في قاعدة البيانات، وقارن بـ hash_equals():
تتغير القيم في كل تشغيل، لأن random_bytes() عشوائية. وإذا تسربت قاعدة البيانات، فالتجزئات عديمة الفائدة دون ملفات تعريف الارتباط، وتقارن hash_equals() في زمن ثابت فلا يسرّب الفحص معلومات توقيت.
الجلسة مقابل ملف تعريف الارتباط: أيهما تستخدم
الجلسة ($_SESSION) | ملف تعريف الارتباط ($_COOKIE) | |
|---|---|---|
| مكان التخزين | على الخادم | في المتصفح |
| يستطيع الزائر قراءتها أو تعديلها | لا (المعرّف فقط) | نعم |
| الحجم | محدود بمساحة تخزينك | نحو 4 KB لكل ملف |
| المدة | حتى إغلاق المتصفح أو انتهاء الجلسة | حتى وقت expires الخاص به |
| استخدمها لـ | المستخدم المسجل دخوله، وسلة الشراء، ورمز CSRF، والرسائل المؤقتة | المظهر، واللغة، واختيار الموافقة، ورمز «تذكرني» |
خطأ شائع: headers already sent
ترسل session_start() وsetcookie() وheader() كلها ترويسات HTTP، ولا يمكن إرسال الترويسات إلا قبل الجسم. وأي مخرجات قبلها، حتى سطر فارغ قبل <?php في ملف مُضمَّن، تنتج:
Warning: session_start(): Session cannot be started after headers have already been sent in /var/www/page.php on line 3
Warning: Cannot modify header information - headers already sent by (output started at /var/www/page.php:2) in /var/www/page.php on line 4
تخبرك الرسالة الثانية أين بدأت المخرجات (page.php:2): اذهب إلى ذلك السطر واحذف المخرجات، أو انقل session_start() فوقه. وفي الملفات التي لا تحتوي إلا PHP، يمنع حذف الوسم الختامي ?> المسافات الشاردة بعده من أن تصبح مخرجات.
الأسئلة الشائعة
ما الفرق بين الجلسة وملف تعريف الارتباط في PHP؟
يُخزَّن ملف تعريف الارتباط في متصفح الزائر ويُرسل مع كل طلب، فيستطيع الزائر قراءته وتغييره. أما الجلسة فتخزّن بياناتها على الخادم؛ ولا يحمل المتصفح إلا معرّف جلسة عشوائيًا في ملف تعريف ارتباط (PHPSESSID). ضع كل ما يجب الوثوق به، مثل معرّف المستخدم المسجل دخوله، في $_SESSION.
كيف أدمّر جلسة عند تسجيل الخروج في PHP؟
أفرغ البيانات، واحذف ملف تعريف ارتباط الجلسة، ودمّر الجلسة المخزنة: $_SESSION = [];، ثم setcookie(session_name(), '', time() - 3600, '/')، ثم session_destroy();. وحوّل بعد ذلك لتبدأ الصفحة التالية من جديد.
كيف أحذف ملف تعريف ارتباط في PHP؟
اضبطه مرة أخرى بالاسم والمسار والنطاق نفسها مع تاريخ انتهاء في الماضي: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']);. واستخدم أيضًا unset($_COOKIE['theme']) إذا كانت بقية السكربت الحالي تقرؤه، لأن $_COOKIE لا تتغير إلا في الطلب التالي.
كم تدوم جلسة PHP؟
افتراضيًا يدوم ملف تعريف ارتباط الجلسة حتى إغلاق المتصفح (session.cookie_lifetime = 0) وقد يحذف الخادم بيانات الجلسة بعد 1440 ثانية (24 دقيقة) دون نشاط (session.gc_maxlifetime). ولمهلة ثابتة، خزّن وقت آخر طلب في الجلسة وافحصه بنفسك.
لماذا تقول session_start() إن الترويسات أُرسلت أصلًا؟
ينتقل معرّف الجلسة في ترويسة ملف تعريف ارتباط، ويجب إرسال الترويسات قبل أي مخرجات. وecho أو HTML أو حتى سطر فارغ أو BOM قبل <?php يرسل المخرجات أولًا. استدعِ session_start() في أعلى السكربت تمامًا، قبل طباعة أي شيء.