Production / Работа с Сессией

Теперь рассмотрим более прогрессивный способ работы с авторизацией, да и не только с ней: так называемую сессию.

Сессия это такие данные которые фиксируются за пользователем который заходит на наш сайт, и могут оставаться привязанными к нему даже после закрытия браузера. Могут, но как правило не остаются, и пропадают сразу после закрытия бразуера.

Ну то есть, например, когда вы на каком-то сайте логинитесь, и потом, например, через несколько дней заходите на тот же сайт и вам не приходится по новой вводить данные для входа. То есть каким-то образом факт вашей авторизации остается зафиксированным.

Как же это работает?

На самом деле достаточно просто. В PHP при активации механизма сессии создается специальный файлик с уникальным именем, в который можно сохранять разные данные.

Это имя передается браузеру и браузер начинает это имя хранить. Хранит он его в тех самых куках про которые пишет каждый первый сайт:

Мы их еще разберем подробнее, но попозже.

Как только браузер получает это уникальное имя, он его теперь сам при каждом запросе начинает передавать серверу. И когда сервер видит, что браузер прислал это самое уникальное имя, он понимает, что надо взять файлик по этому уникальному имени открыть его и достать из него данные.

Пробрасываем данные между страницами

Чтобы не сразу кидаться во все тяжкие и реализовывать авторизацию. Попробуем просто добавить поле для ввода, в которое можно будет что-то написать, и чтобы оно стало доступно на всех страницах.

Создадим сначала SetWelcomeController, которые пока просто будет перенаправлять пользователя на страницу с которой будет отправляться запрос

вот этот $_SERVER['HTTP_REFERER'] как раз хранит адрес такой страницы.

Теперь привяжем контроллер к роутеру:

$router->add("/set-welcome/", SetWelcomeController::class);

и добавим поле для ввода в навигацию

получится такое:

теперь у меня такая идея, я хочу туда что-то написать, и чтобы это что-то стало доступно сразу на всех страницах. Причем мне не надо будет ничего фиксировать в БД.

И так, активируем механизм сессии. Так как нам надо чтобы сессия была доступна с любой страницы, то править будем BaseController, на самом деле конечно это не очень хорошо. И сессию лучше делать через middleware, но и наш подход допустим.

Правим функцию process_response, добавляем строчку

в принципе все, механизм сессии активировался! =)

Как это проверить?

Открываем консольку и идем сюда

То есть в куках (а куки это просто встроенное в бразуер хранилище в виде словарика, где есть ключ и привязанное к нему значение), под ключем PHPSESSID лежит уникальный номер вашей сессии. Вы можете удалить эту строчку через Delete перегрузить страницу и там появится уже новый номер.

А как посмотреть файлик который привязывается к этой сессии? Сначала надо найти папку куда PHP сохраняет сессии. Для этого в laragon надо зайти в настройки php

и найти там ключ session.save_path

тут и будет указано где хранятся файлики с сессиями. Давайте зайдем в папку и найдем файлик, соответствующий нашей сессии:

если его открыть он будет пустой. Давайте в него что-нибудь запишем. Идем в SetWelcomeController и добавляем строчку:

<?php

class SetWelcomeController extends BaseController {
    public function get(array $context) {
        $_SESSION['welcome_message'] = $_GET['message']; // добавил
        
        $url = $_SERVER['HTTP_REFERER'];
        header("Location: $url");
        exit;
    }
}

а теперь попробуем чего-нибудь написать в наше новое поле для ввода и тыкнуть Enter

вроде ничего не произошло, но теперь если открыть файл сессии, то увидим в нем

то есть наше сообщение зафиксировалось и теперь доступно с любой страницы.

Давайте, например, будем выводить его на странице объектов. Идем в ObjectController добавляем переменную в контекст со значением из сессии

правим шаблон:

открываем любой объект и пробуем что-то писать:

Но всегда надо помнить, что сессия привязана к одному браузеру.

Например, если открыть эту же страницу в режиме инкогнито, то мы и вовсе получим ошибку

почему так? Потому что открытие страницы в режиме инкогнито равносильно запуску одноразового браузера, у которого чистая история и чистые куки.

То есть инкогнито браузер запрашивает уже новую сессию в которой значение $_SESSION['welcome_message'] еще не определено. А поэтому ключ не доступен.

Поэтому во избежание такой ошибки надо добавить проверку на существование ключа, вот так

$context["my_session_message"] = isset($_SESSION['welcome_message']) ? $_SESSION['welcome_message'] : "";

вот теперь ошибки нет

можно глянуть их куки:

и увидеть, что у них разные значения ключей.

В принципе мы можем подделать сессию если скопируем значения ключа

вообще, такой прием называется подделкой сессии и является одной из возможных атак на сайт =)

Хранение массивов в сессии

Кстати в сессии можно хранить не обязательно простые типы, навроде строк или цифр. Можно, например, хранить массивы. Для этого нам надо просто изначально ключ в сессии инициализировать пустым списком, как-то так:

подключаем в контроллере:

class ObjectController extends BaseSpaceTwigController {
    public $template = "__object.twig";

    public function getContext(): array
    {
        // ...

        // передаем в контекст список messages
        $context["messages"] = isset($_SESSION['messages']) ? $_SESSION['messages'] : "";

        return $context;
    }
}   

и теперь можно выводить в шаблоне

тестим:

Сохранение сессии после закрытия браузера

Кстати вы возможно заметили, что если закрыть и открыть браузер, то все что мы тут вводили теряется.

Почему так происходит? Потому что стандартное поведение сессии — это забыться по закрытию браузера. И на самом деле забывает сессию именно бразуер. Помните вот эту картинку

вот это идентификатор сессии который можно посмотреть в бразуере в куках, имеет определенное время жизни. Увидеть его можно в cтолбике Expire

значение Session означает что эта кука удалится по закрытию браузера. Но можно тыкнуть по значению два раза и ввести туда какую-нибудь дату в формате YYYY-MM-DD (например, 2021-12-31)

попробовать закрыть и открыть браузер, и убедиться, что значения теперь сохранились.

Но тут есть две проблемы, первая – не будем же мы заставлять пользователя лазить в куки и прописывать значения вручную. И вторая – хотя мы и установили время жизни куки с идентификатором сессии на конкретную дату, на сервере все равно стоит другое значение. Можно зайти в настройки

и найти там такое значение

которое означает что сессия будет считаться просроченной через 36000 секунд, то есть примерно через 10 часов. То есть это означает, что файлик с сессией будет удален на сервер не раньше, чем через 10 часов.

Но мы ж вроде в браузере посидели всего несколько минут закрыли-открыли, и данных уже нет. Как сделать так чтобы данные сохранились хотя бы на ближайшие 10 часов.

Для этого есть еще одна функция которая называется session_set_cookie_params с помощью него можно указать как долго браузер будет хранить идентификатор сессии. Давайте загоним туда эти самые 10 часов.

Идем в BaseController и добавляем перед session_start:

проверяем, только не забудьте открыть и закрыть браузер, чтобы старое значение PHPSESSID сбросилось. Получается так:

Можно теперь смело закрывать браузер и не бояться потерять значение

3

Добавить историю посещений страниц в сессию и выводить список последних 10 посещенных страниц в отдельный блок. Примерно так: