Понимание принципов работы сессий в Laravel

Источник: «Understanding how Laravel sessions work»
Если мы не знаем, как что-то работает, то ещё больше запутываемся, когда что-то ведёт себя не так, как ожидалось.

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

У них был контроллер, устанавливающий значение сессии, если оно пустое, и затем возвращающий это значение. Но когда они добавили несколько отладочных операторов, то обнаружили, что значение всегда пустое, даже если несколько раз обновить страницу.

Предполагалось, что в первый раз он будет пуст, но затем будет присутствовать некое значение при последующих вызовах.

Вот упрощённая версия того, что они пытались сделать:

// внутри контроллера действие значительно упрощено для отображения в одном блоке

if (session()->has('some-key')) {
dump('key exists');
$value = session('some-key');
} else {
dump('key does not exist');
session(['some-key' => 'some-value']);
$value = session('some-key');
}

dd($value);

При каждом обновлении страницы выводилось значение key does not exist и some-value. Исходя из этого, можно было понять, что значение сессии не сохраняется, но почему?

Здесь очень полезно знать, как работают сессии в Laravel. Они не используют базовый механизм сессий PHP, а реализуются через Laravel SessionManager и middleware StartSession.

Давайте посмотрим на соответствующий метод в этом middleware (я удалил все комментарии, кроме своего, для упрощения)

protected function handleStatefulRequest(Request $request, $session, Closure $next)
{
$request->setLaravelSession(
$this->startSession($request, $session)
);

$this->collectGarbage($session);

$response = $next($request); // <--- Здесь выполняется действие вашего контроллера

$this->storeCurrentUrl($request, $session);

$this->addCookieToResponse($response, $session);

$this->saveSession($request);

return $response;
}

Обратите внимание, что метод saveSession вызывается только в ответной части middleware, выполняющейся после действия контроллера.

Поэтому, поместив dd() в действие нашего контроллера, мы убиваем PHP-запрос до того, как это middleware сможет закончить свою работу. Если бы вместо dd был использован dump, то все работало бы так, как и ожидалось.

Загадка разгадана!

P.S. dd иногда бывает полезен, но Xdebug — ещё лучший инструмент отладки.

Дополнительные материалы

Предыдущая Статья

Демистификация git cherry-pick: обзор команды с примерами

Следующая Статья

Новое в Symfony 6.4: Улучшения безопасности