קראו ל-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.";
ל-sessions ולעוגיות יש משמעות רק על פני כמה בקשות HTTP מדפדפן, ולכן הדוגמאות שצריכות אחת כזו הן קוד רגיל. הלוגיקה שסביבן היא PHP רגילה על מערכים, והחלק הזה רץ כאן: בכל בלוק שאפשר להריץ, $session עומד במקום $_SESSION, וכל קריאה היא בקשה אחת.
איך session ב-PHP עובד
- ה-
session_start()הראשון יוצר מזהה אקראי, שולח אותו לדפדפן בעוגייה בשםPHPSESSID, ונותן לכם$_SESSIONריק. - בסוף הבקשה PHP שומרת את
$_SESSIONבשרת (בקבצים כברירת מחדל, או ב-Redis, או במסד נתונים). - בבקשה הבאה הדפדפן שולח את העוגייה בחזרה,
session_start()קוראת את המזהה וטוענת את המערך השמור.
הדפדפן אף פעם לא רואה את הנתונים, רק את המזהה. לכן המזהה של המשתמש המחובר שייך ל-$_SESSION ואף פעם לא לעוגייה משלכם: מבקר יכול לערוך את העוגיות שלו לכל ערך, אבל הוא לא יכול לערוך את $_SESSION.
התחברות והתנתקות של משתמש עם 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) חשובה יותר ממה שהיא נראית. אם תוקף יכול לגרום לקורבן להשתמש במזהה session שהתוקף מכיר (למשל דרך קישור), והמזהה נשאר אותו דבר אחרי ההתחברות, התוקף מחובר גם הוא. מזהה חדש בהתחברות סוגר את החור הזה. בדיקת הסיסמה עצמה מוסברת בעמוד password_hash.
תפוגת session אחרי חוסר פעילות
הניקוי של PHP עצמה (session.gc_maxlifetime, 1440 שניות כברירת מחדל) רץ באקראי ואינו timeout אמין. שמרו את זמן הבקשה האחרונה ובדקו אותו בעצמכם. הבלוק משתמש בחותמות זמן קבועות כדי שתוכלו לראות כל מקרה; שנו את $timeout או את הזמנים.
הפער מ-09:05 עד 09:16:40 הוא 700 שניות, מתחת למגבלה של 15 דקות, ולכן המשתמש נשאר מחובר. הפער הבא הוא 1000 שניות, ולכן ה-session מתנקה.
הודעות flash
הודעת flash מוגדרת בבקשה אחת ומוצגת פעם אחת בבקשה הבאה, בדרך כלל "Saved." אחרי הפניה. שמרו אותה ב-session, ואז קראו והסירו אותה בצעד אחד:
הגדרת עוגייה עם 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 מכסה את הדגלים. לכל דבר גדול מכמה ערכים קצרים, השאירו את הנתונים ב-session (או במסד נתונים) ושמרו בעוגייה רק מפתח.
טוקנים של "remember me"
עוגיית "remember me" לא אמורה להחזיק את מזהה המשתמש או את ה-hash של הסיסמה. שמרו בעוגייה טוקן אקראי ארוך ובמסד הנתונים רק את ה-hash שלו, והשוו עם hash_equals():
הערכים משתנים בכל הרצה, כי random_bytes() אקראית. אם מסד הנתונים דולף, ה-hashes חסרי תועלת בלי העוגיות, ו-hash_equals() משווה בזמן קבוע כך שהבדיקה לא מדליפה מידע תזמון.
session מול עוגייה: באיזה להשתמש
Session ($_SESSION) | עוגייה ($_COOKIE) | |
|---|---|---|
| נשמר | בשרת | בדפדפן |
| המבקר יכול לקרוא או לערוך אותו | לא (רק את המזהה) | כן |
| גודל | מוגבל על ידי האחסון שלכם | כ-4 KB לעוגייה |
| מחזיק | עד שהדפדפן נסגר או שה-session פג | עד זמן ה-expires שלה |
| השתמשו בו ל | משתמש מחובר, עגלה, טוקן CSRF, הודעות flash | ערכת נושא, שפה, בחירת הסכמה, טוקן "remember me" |
טעות נפוצה: 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, השמטת ה-?> הסוגר מונעת מרווחים לבנים תועים אחריו להפוך לפלט.
שאלות נפוצות
מה ההבדל בין session לעוגייה ב-PHP?
עוגייה נשמרת בדפדפן של המבקר ונשלחת עם כל בקשה, ולכן המבקר יכול לקרוא ולשנות אותה. session שומר את הנתונים שלו בשרת; הדפדפן מחזיק רק מזהה session אקראי בעוגייה (PHPSESSID). שימו כל דבר שחייבים לסמוך עליו, כמו מזהה המשתמש המחובר, ב-$_SESSION.
איך משמידים session בהתנתקות ב-PHP?
רוקנו את הנתונים, מחקו את עוגיית ה-session והשמידו את ה-session השמור: $_SESSION = [];, אחר כך setcookie(session_name(), '', time() - 3600, '/'), ואז session_destroy();. בצעו הפניה אחר כך כדי שהדף הבא יתחיל מחדש.
איך מוחקים עוגייה ב-PHP?
הגדירו אותה שוב עם אותו שם, path ו-domain ותאריך תפוגה בעבר: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']);. עשו גם unset($_COOKIE['theme']) אם שאר הסקריפט הנוכחי קורא אותה, כי $_COOKIE משתנה רק בבקשה הבאה.
כמה זמן מחזיק session ב-PHP?
כברירת מחדל עוגיית ה-session מחזיקה עד שהדפדפן נסגר (session.cookie_lifetime = 0) והשרת רשאי למחוק נתוני session אחרי 1440 שניות (24 דקות) בלי פעילות (session.gc_maxlifetime). ל-timeout קבוע, שמרו ב-session את זמן הבקשה האחרונה ובדקו אותו בעצמכם.
למה session_start() אומרת headers already sent?
מזהה ה-session עובר בכותרת עוגייה, וכותרות חייבות להישלח לפני כל פלט. echo, HTML, או אפילו שורה ריקה או BOM לפני <?php שולחים את הפלט קודם. קראו ל-session_start() ממש בראש הסקריפט, לפני שמשהו מודפס.