nmk

Лекція 9 (2 години). Збереження стану користувача. Cookies та Sessions

План лекції

  1. Навіщо зберігати стан (State) в HTTP.
  2. Що таке Cookies і як ними маніпулювати (setcookie, $_COOKIE).
  3. Механізм Сесій (session_start, $_SESSION). Відмінності сесій від Cookies.
  4. Процес банальної авторизації (Логін та Логаут).

Перелік умовних скорочень

Вступ

Як ми неодноразово згадували, протокол HTTP не має пам’яті (Stateless). Коли ви авторизувалися в Instagram, а потім клікнули на чиєсь фото (ініціювавши новий HTTP GET запит) — технічно Instagram вас забув і мав би знову показати форму введення пароля. “Пригадати” вас йому допомагають дві технології: Cookies (печиво) на стороні вашого браузера, та Сесії на стороні їхніх серверів. Це базис будь-якої системи автентифікації, кошика покупок та персоналізованого контенту.


1. Навіщо зберігати стан (State) в HTTP

Якщо ми перейдемо зі сторінки page1.php, де ми задали $user = "Олег", на сторінку page2.php, змінна $user виявиться порожньою (Undefined). Як зробити змінну глобальною і “безсмертною” під час подорожі користувача сайтом? Ми не можемо змусити користувача весь час передавати своє ім’я чи токен доступу в адресній строчці ?user=Олег при кожному кліку — це архітектурно жахливо і небезпечно. Рішення розділилось на два підходи фронту та бекенду: Cookie та Сесії.

2. Що таке Cookies (setcookie())

Cookie — це крихітний текстовий рядок (макс. 4 КБ), який сервер відправляє в браузер, і браузер слухняно зберігає його десь глибоко на жорсткому диску самого користувача. Головна магія кук в іншому: Браузер зобов’язаний ПРИКРІПЛЯТИ до КОЖНОГО наступного запиту до цього конкретного сайту (на кожну картинку, на кожен клік) всі куки, які він зберіг. “Привіт, Сервер, пам’ятаєш, ти просив запам’ятати це? Ось воно знову”.

Встановлення Куки

Використовується функція setcookie(Ключ, Значення, Термін_Життя). Критичне правило: Відправляти Cookie можна ТІЛЬКИ ДО ТОГО (найвище у файлі), як ви використали команду echo або навіть вивели <!DOCTYPE html>. Куки — це HTTP-Заголовки. Їх не можна надіслати, якщо тіло HTML вже почало друкуватися. Headers already sent error.

<?php
// Встановлюємо куку 'theme' у значення 'dark'.
// Вона буде "жити" на комп'ютері клієнта 30 днів (в секундах: 60*60*24*30)
$expireDate = time() + (86400 * 30);
setcookie('theme', 'dark', $expireDate, '/');
?>

Читання Куки

На всіх інших сторінках сайту, при наступних візитах, PHP автоматично витягне ці куки з запиту браузера й покладе в масив $_COOKIE.

<?php
// Ми вже на іншій сторінці наступного дня
$siteTheme = $_COOKIE['theme'] ?? 'light'; // За замовчуванням 'light'
echo "Ваша улюблена тема: " . $siteTheme;

// ЗНИЩЕННЯ КУКИ:
// Щоб видалити куку (напр. "Вийти"), потрібно встановити їй час життя в МИНУЛЕ:
setcookie('theme', '', time() - 3600, '/');
?>

Головний недолік: Куки легко відкрити й відредагувати через DevTools або розширення. Ніколи не зберігайте в куках ціни товарів у кошику, права адміна (isAdmin=1) або паролі! Користувач їх підробить.

3. Механізм Сесій ($_SESSION)

Сесія працює інакше: вона зберігає всю “конфіденційну” інформацію (наприклад статус адміна чи товари в кошику) на диску самого Сервера в неприступному захищеному місці, і ніхто ззовні не може її підробити.

Як же тоді Сервер розпізнає, яка саме сесія в оперативній пам’яті належить якому браузеру? Магія: PHP при старті сесії непомітно для вас відсилає одну-єдину технічну Cookie в браузер, яка називається PHPSESSID. В ній лежить довгий рандомний хеш (наприклад abc123xyz). Браузер з кожним кліком надсилає PHPSESSID назад, а PHP дістає потрібний файл на диску з ім’ям abc123xyz і заповнює “оживляє” ваш суперглобальний масив $_SESSION.

Вмикання Сесій (Критично важливо!)

Масив $_SESSION порожній і недоступний, поки ви не викличете команду “Дозволяю працювати з сесіями на цій сторінці” — session_start(). Викликати її потрібно найпершим рядком вашого коду index.php (до HTML-тегів, з тієї ж причини, що й з setcookie()).

<?php
session_start();

// Збереження даних у сесію ПРОСТО як у звичайний масив:
$_SESSION['user_name'] = "Іван";
$_SESSION['role'] = "Admin";
$_SESSION['cart_items'] = 5;

// Цей процес зазвичай роблять в обробнику login.php ПІСЛЯ перевірки пароля.
?>

Читання даних з Сесії на інших сторінках

<?php
// ОБОВ'ЯЗКОВО наголошуємо "ми знову хочемо користуватись механізмом"
session_start();

// Перевіряємо, чи був збережений ключ?
if (isset($_SESSION['user_name'])) {
    echo "З поверненням у панель, " . $_SESSION['user_name'];
    // Іван не зможе через F12 змінити 'role' на щось інше,
    // бо ці дані лежать на нашому сервері!
} else {
    echo "Ви Гість. Зареєструйтесь.";
}
?>

4. Процес банальної авторизації (Логін та Логаут)

Об’єднавши знання з минулих уроків (форми) та сьогоднішньої (сесії), ми отримуємо класичний механізм авторизації будь-якого сайту.

Процес Логіну (login.php)

  1. Одержуємо $_POST['email'] та $_POST['password'].
  2. Перевіряємо, чи є такий логін і пароль у базі (поки що жорстко закодуємо в if).
  3. Якщо все вірно (паролі зійшлись) — створюємо прапорець у сесії ($_SESSION['is_logged'] = true) і перенаправляємо на профіль.
  4. Якщо пароль не вірний — видаємо помилку.
<?php
session_start();
if ($_POST['email'] === 'admin@mail.com' && $_POST['password'] === '123') {
    $_SESSION['is_logged'] = true;
    $_SESSION['uid'] = 99; // айді користувача
    header('Location: profile.php'); // Перенаправлення (Redirect)
    exit; // Обов'язково зупиняти код після header(Location)!
} else {
    echo "Невірний пароль!";
}
?>

Процес перевірки прав доступу (Private Route, profile.php)

У самому верху закритих сторінок, які доступні ТІЛЬКИ зареєстрованим:

<?php
session_start();
if (empty($_SESSION['is_logged'])) { // Або !isset()
    die("У вас немає доступу. Будь ласка, залогінтесь.");
    // На практиці роблять не die, а: header('Location: login.php'); exit;
}
echo "Вітаємо в секретній зоні (Профілі)!";
?>

Процес Логауту, або “Виходу з системи” (logout.php)

Для безпечного знищення всіх змінних сесії на сервері, і тим самим розлогінення користувача:

<?php
session_start();
session_unset();     // Очищає пам'ять масиву $_SESSION (всі змінні)
session_destroy();   // Повністю знищує файл сесії на диску сервера
header('Location: index.php'); // Викидаємо назад на головну
exit;
?>

Висновки

  1. Існує 2 механізми запам’ятовування користувача між його “кліками” (переходами) по сайту: на стороні браузера клієнта — Cookies; на стороні серверного диску — Сесії.
  2. Встановлювати setcookie() або ініціалізувати сесію через session_start() необхідно до будь-якого виводу даних в браузер (до HTML чи echo).
  3. Cookies ідеально підходять для довгострокового збереження некритичних даних (улюбленої мови, кольорової теми, факту, що “банер був закритий”). За їхньою природою їх може модифікувати будь-який хакер.
  4. Сесії ($_SESSION) зберігають дані лише на сервері, відповідно єдиним надійним місцем для зберігання фактів типу “Користувач успішно пройшов авторизацію і він Адміністратор”.
  5. Базова авторизація – це просто запис спеціального маркера (прапорця, напр. ['is_logged'] = true) в сесію під час перевірки правильного логіна/пароля.

Джерела

  1. Офіційна сторінка Session Control: https://www.php.net/manual/ru/book.session.php
  2. Різниця Cookie та Session з точки зору архітектури (ENG): https://www.guru99.com/difference-between-cookie-session.html

Запитання для самоперевірки

  1. Поясніть сутність повідомлення-помилки PHP під час розробки Cannot modify header information - headers already sent by...? В який конкретно момент роботи функції session_start() чи setcookie() ця помилка спрацьовує і як її гарантовано уникнути?
  2. Ви розробляєте інтернет-магазин. У вас немає реєстрації (отже немає користувачів у Базі Даних), але люди вже додають товари у “Кошик” покупок. Який механізм краще обрати для зберігання масиву цих товарів: Cookies чи $_SESSION? Обґрунтуйте ризики обох варіантів.
  3. Прочитайте код іншого розробника: if ($_COOKIE['user_is_super_admin'] === 'yes') { /* видалити всю базу даних */ }. Яка фундаментальна архітектурна вразливість допущена в цій системі перевірки прав адміністратора (Access Control)?
  4. Де фізично на комп’ютері розташовуються дані (значення), збережені всередині масиву $_SESSION['login'] = 'Ivan', і чому сторонній клієнт (навіть досвідчений програміст) принципово не здатен прочитати або змінити цей факт зі свого браузера (через F12)?
  5. Який зв’язок (місток) існує між Сесією (що лежить на сервері) та Браузером (який робить stateless-запити)? Як сервер взагалі розуміє, який файл сесії “підняти” для вас і як називається ця єдина технічна кука?