High speed Yii : suppress PHP sessions / подавление создания сессий

Одна из задач грамотного кэширования страниц на уровне веб-сервера nginx — не создавать сессии PHP без необходимости. При этом пользователям без сессии должна отдаваться версия страницы из кэша, а авторизованным — без кэширования. Выключил опцию ‘autoStart’ => false в настройках сессии. Ожидал, что сессия не будет создаваться, но в куках всё равно вижу PHPSESSID. Тогда я сделал так…

Веб приложения с высокой скоростью

Веб приложения с высокой скоростью

Сессия PHP открывается вызовом функции session_start. При этом в в браузер передаётся кука с определённым именем, обычно это PHPSESSID. Имя куки можно получить из функции session_name. Наличие уже открытой сессии можно определить по непустому значению функции session_id.

Чтобы не создавать сессию для каждого посетителя, в приложениях на PHP пользуются такой конструкцией

$isSessionCookieExists = isset($_COOKIE[session_name()]); // признак "Имеется кука PHPSESSID?"
if ($isSessionCookieExists)
{
    session_id() || session_start(); // проверить наличие открытой сессии и открыть при её отсутствии
}

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

  • входа для пользователей имеет адрес http:///login/
  • страница регистрации новых пользователей http:///register/
  • страница панели модератора или администратора http:///admin/

В этом случае код создания сессии получается такой

$uri = $_SERVER['REQUEST_URI'];
$isLoginPage = strpos($uri, '/login/'); // признак "Это страница входа?"
$isRegisterPage = strpos($uri, '/register/'); // "Это страница регистрации?"
$isAdminPage = strpos($uri, '/admin/'); // "Это панель управления?"
$isSessionCookieExists = isset($_COOKIE[session_name()]); // "Имеется кука PHPSESSID?"
if ($isLoginPage || $isRegisterPage || $isAdminPage || $isSessionCookieExists)
{
    session_id() || session_start(); // проверить наличие открытой сессии и открыть при её отсутствии
}

Заплатка для приложения на базе фреймворка Yii

Заплатка подавляет лишнее создание сессии.

Создал компоненту, которая должна обрабатывать следующую ситуацию: создавать сессию PHP, только если она нужна или если она уже была создана ранее, о чём говорит наличие куки с именем PHPSESSID.

<?php

/**
 * Class LazyHttpSession
 * Обеспечивает подавление создания сессий PHP
 *
 * @author pavel.volyntsev@gmail.com
 * @link http://copi.st/nMen
 */
class LazyHttpSession extends CCacheHttpSession // здесь надо указать супер-класс, используемый в твоём приложении
{
	/**
	 * Подавить создание сессии : открывать сессию PHP только при наличии сессионной куки
	 * @var bool
	 */
	public $suppressStart = true;

	public function open()
	{
		// если включено подавление старта и сессионная кука не найдена, сессию не открывать
		if ($this->suppressStart && !isset($_COOKIE[session_name()]))
			return;

		// иначе обычное поведение [ CHttpSession::open() ]
		parent::open();
	}
}

Для использования такого поведения необходимо задать настройки в файле конфигурации. Чтобы не сломать механизм авторизации, сессию обязательно необходимо стартовать на страницах, где происходит авторизация или регистрация.

<?php
/**
 * @file protected/config/main.php
 */

// ...

$request = new \CHttpRequest; // издержка - требуется дополнительный объект для разбора входящего запроса
$uri = $request->getRequestUri();

return array(
 	// ...
 	'components'=>array(
		// ...

		// Настройки для базы данных
		'session' => array(
 			'class' => 'application.components.LazyHttpSession', // указать класс для работы с сессией
			// ...
			'autoStart' => false, // выключить сессии для анонимных пользователей
			'suppressStart' => (
			    // сессию стартовать только при наличии сессионной куки
			    // а также на страницах регистрации, логина или панели администратора
				false === strpos($uri, '/login/')
				&& false === strpos($uri, '/register/')
				&& false === strpos($uri, '/admin/')
			),
		),

		// ...
	),
	// ...
);

Как проявляется действие заплатки?

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

Код класса LazyHttpSession

Yii HTTP Sessions component with logic «Supress PHP Session» at gist.github.com

Что даёт патч?

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

В интернете много и правильных и неправильных примеров. Буквально сегодня нарвался на пример настройки кэширования, прокомментированный следующим образом:

user1:
Жесткие конфиги. Всмысле пи**ец говноконфиги.

user2:
Меня всегда умиляла фраза: Более подробную информацию ищите в гугле
А как гугл найдет что-то, если все пишут именно так? Ась?

Нужна ли статья про кэширование страниц в nginx? Такой механизм мы используем в проекте icons8.com. Он позволяет «выстреливать» ответы за 3-5 миллисекунд и не портит авторизацию.

Павел Волынцев

Уже более 15 лет занимаюсь разработкой веб-проектов. Fullstack Senior Developer. IT евангелист — доношу свет знаний об информационных технологиях. Профессиональные цели: Дать людям возможность дать людям больше.

Читайте также: