Глава 112 · От первого шага до справочника
Сессии PHP: cookie, состояние, безопасность и полный API
От сохранения корзины между запросами до сроков входа, блокировок, собственного хранилища и всех функций/методов session.
Начать с наблюдаемого состояния, затем выбрать API
Почему корзина сохраняется между запросами
PHP-массив живёт в запросе; хранилище связывает его со следующим запросом.
- Браузер отправляет cookie с ID
В cookie обычно находится идентификатор, а не весь массив корзины.
- session_start
Обработчик читает серверное состояние по ID; files обычно удерживает блокировку.
- $_SESSION["cart"]
PHP читает и меняет данные текущего запроса.
- Закрытие сессии
Запись/завершение освобождает ресурс, следующий запрос может прочитать сохранённое.
Идентификатор сессии не заменяет проверку прав. Срок cookie, срок входа и уборка старых файлов — разные механизмы.
Сначала получите две страницы с общим языком и полный кабинет. Далее эта глава раскрывает все настройки, блокировки и методы. Сессия хранит состояние; полномочия, сроки и CSRF приложение проверяет отдельно.
Корзина не должна исчезать при переходе на другую страницу
Посетитель выбрал два товара и открыл доставку. Обычная переменная первого PHP-запроса уже исчезла; следующему запросу нужно найти его корзину. Сессия связывает запросы непрозрачным идентификатором: браузер хранит ID в cookie, сервер — значения корзины. Полезный результат: следующий запрос снова читает число2.
- Браузер: cookie с непрозрачным ID
- PHP получает ID и вызывает session_start
- Хранилище возвращает cart_qty=2
- PHP создаёт $_SESSION для текущего запроса
- write_close сохраняет изменения и освобождает хранилище
Аналогия с номером ячейки помогает понять поиск, но ID обладает силой ключа доступа: знающий его может предъявить его вместо пользователя. В cookie не лежит весь $_SESSION; авторизация не появляется автоматически. $_SESSION — обычные данные конкретного запроса после чтения, а не разделяемая живая память всех PHP-процессов.
session_start() — вызов без обязательных аргументов; () запускает функцию. $_SESSION — доступный из разных областей программы массив текущей сессии. В записи $_SESSION['cart_qty'] = 2; строка в квадратных скобках выбирает ячейку, знак = сохраняет в неё число, точка с запятой заканчивает действие. Это ещё не запись на диск: сохранение выполняет обработчик при закрытии.
Самостоятельные примеры подключают приложенный bootstrap.php через require. Он создаёт каталог рядом с примером и настраивает только текущий CLI-процесс; cookie в нём отключены, ID не передаются по сети. Примеры настройки cookie отдельно включают use_cookies=1, но не отправляют браузерный запрос. На сайте cookie включены — полный HTTP-стенд. if (!...) обрабатывает отказ, === сравнивает без преобразования типов, echo печатает результат. Основы: переменные, массивы, вызовы и return.
<?php
require __DIR__ . '/bootstrap.php';
session_start();
$_SESSION['cart_qty'] = 2;
$quantity = $_SESSION['cart_qty'];
session_write_close();
echo $quantity, "
";
Ожидаемый результат
2
Первые четыре действия: открыть → присвоить → прочитать → сохранить/закрыть. Самостоятельный CLI-пример не притворяется браузером.
Синтаксис уже разобран выше. В настоящем контроллере проверяют false от start, валидируют входные данные и права. Следующий шаг — session_start с повторным чтением, затем HTTP-стенд ниже.
Установка, минимальная лаборатория и границы окружения
Стандартное расширение session не требует внешней библиотеки и обычно включено; сборка может отключить его через --disable-session. В Windows оно встроено, отдельная DLL для базового files не требуется. Команда php -m и extension_loaded проверяют именно тот runtime, где исполняется приложение; PHP веб-сервера может отличаться от CLI.
- Один временный каталог примера
- bootstrap создаёт session-data рядом
- Только этот процесс выбирает files и отключает HTTP-cookie
- После выхода лабораторная папка может быть удалена целиком
<?php
var_dump(extension_loaded('session'), function_exists('session_start'));
Ожидаемый результат
bool(true)
bool(true)
Для проверенного runtime8.5.11; не выводим phpinfo на публичную страницу.
<?php
// Only a local CLI laboratory: the HTTP example uses real cookies instead.
$directory = __DIR__ . '/session-data';
if (!is_dir($directory) && !mkdir($directory, 0700)) {
throw new RuntimeException('Cannot create the laboratory directory');
}
ini_set('session.save_handler', 'files');
ini_set('session.use_cookies', '0');
ini_set('session.use_only_cookies', '1');
ini_set('session.use_strict_mode', '1');
ini_set('session.gc_probability', '0');
ini_set('session.serialize_handler', 'php_serialize');
session_cache_limiter('');
session_save_path($directory);
session_name('ManualSession');
var_dump(is_dir(session_save_path()));
Ожидаемый результат
bool(true)
mkdir создаёт каталог; 0700 — намерение Unix-доступа, наWindows действуют ACL. Все ini_set локальны текущему процессу; gc_probability=0 допустим здесь, потому что весь каталог одноразовый.
Files создаёт sess_ID уже при открытии, даже если массив пуст. Замечание общего введения Manual о записи только после добавления переменной нельзя считать универсальным для files: собственный backend может откладывать создание.
Специальный вариант --with-mm[=DIR] подключает внешнюю mm для общей памяти на поддерживаемых Unix-системах, не Windows; taggedPHP8.5.11 запрещает сочетать mm с ZTS. Manual отдельно отмечает ограничения конкурентной блокировки mm и потерю памяти при перезапуске. Файлы на tmpfs сохраняют штатную блокировку files, но не переживают утрату такого RAM-хранилища. Это выбор инфраструктуры, а не обязательный шаг для первой корзины.
Как ID проходит между запросами и почему URL — плохой носитель
Обычный маршрут: ответ Set-Cookie → браузер хранит cookie → следующий запрос содержит Cookie → PHP извлекает ID. Для одного приложения имя/path/domain должны совпадать на всех страницах. Если cookie заблокирована или выбрана неверная область, очередной запрос становится новым посетителем; session_start не умеет заставить браузер принять cookie.
- GET /state без Cookie → новая сессия
- Ответ Set-Cookie: имя=непрозрачный-ID
- POST /cart с Cookie → та же серверная запись
- Новый GET /state читает сохранённое количество
Исторически PHP поддерживает GET/POST-ID и trans_sid: переписывание относительных ссылок/форм, сPHP7.1 также полных URL для разрешённых хостов; arg_separator.output задаёт разделитель параметров. SID можно было экранировать htmlspecialchars перед вставкой в HTML. Это описание старого механизма, не современный рецепт: URL попадает в историю, bookmark, аналитику и Referer. СPHP8.4 включение trans_sid, отключение use_only_cookies, изменение rewrite-настроек и использование SID устаревают. Для нового сайта cookies обязательны; при отказе показывайте понятное сообщение.
SameSite описывает site, а не точное равенство origin/домена. Lax разрешает некоторые верхнеуровневые безопасные переходы с другого сайта, а не любой cross-site GET/fetch; Strict строже, None нужен для обоснованных cross-site-сценариев и требует Secure. Ведущая точка Domain современными браузерами игнорируется. Для CORS/fetch настройка credentials и серверные CORS-заголовки отдельны от session API.
Полные файлы обработчиков и значение каждого этапа
Приложению нужна корзина, а PHP нужен объект, умеющий открыть хранилище, прочитать байты, записать их и закрыть ресурсы. Интерфейс перечисляет обязательные методы. Регистрация не вызывает все методы сразу: PHP выбирает их по жизненному циклу сессии.
- new LessonStore создаёт rows/events
- session_set_save_handler регистрирует объект
- session_start → open → проверка ID/генератор → read
- PHP превращает строку в $_SESSION; приложение меняет cart
- write_close → write либо updateTimestamp → close
class определяет тип, implements обещает набор методов интерфейса, public разрешает вызов. $this — текущий объект; rows хранит записи, events — имена вызовов, now — учебное время. Метод read возвращает строку или false, write — bool, gc — число удалений или false. return передаёт значение PHP, не отправляет HTML.
Полный файл LessonStore.php
<?php
// A deterministic one-process teaching store, NOT a production backend.
class LessonStore implements SessionHandlerInterface, SessionIdInterface, SessionUpdateTimestampHandlerInterface {
public array $rows = [];
public array $events = [];
public int $now = 1000;
public bool $failOpen = false;
public bool $failRead = false;
public bool $failWrite = false;
public bool $closed = true;
public function open(string $path, string $name): bool {
$this->events[] = 'open'; $this->closed = false;
return !$this->failOpen;
}
public function close(): bool {
$this->events[] = 'close'; $this->closed = true; return true;
}
public function read(string $id): string|false {
$this->events[] = 'read';
return $this->failRead ? false : ($this->rows[$id]['data'] ?? '');
}
public function write(string $id, string $data): bool {
$this->events[] = 'write';
if ($this->failWrite) { return false; }
$this->rows[$id] = ['data' => $data, 'updated' => $this->now]; return true;
}
public function destroy(string $id): bool {
$this->events[] = 'destroy'; unset($this->rows[$id]); return true;
}
public function gc(int $max_lifetime): int|false {
$this->events[] = 'gc'; $count = 0;
foreach ($this->rows as $id => $row) {
if ($row['updated'] < $this->now - $max_lifetime) { unset($this->rows[$id]); $count++; }
}
return $count;
}
public function create_sid(): string {
$this->events[] = 'create_sid';
do { $id = bin2hex(random_bytes(16)); } while (isset($this->rows[$id]));
return $id;
}
public function validateId(string $id): bool {
$this->events[] = 'validateId'; return isset($this->rows[$id]);
}
public function updateTimestamp(string $id, string $data): bool {
$this->events[] = 'updateTimestamp';
if (!isset($this->rows[$id])) { return false; }
$this->rows[$id]['updated'] = $this->now; return true;
}
}
LessonStore предназначен для изучения контрактов в одном процессе. Массив не переживает новый HTTP-запрос, не блокирует соседний процесс и не является готовым backend. Файлы всех карточек включают его целиком.
Полный файл TraceFiles.php
<?php
// Observe native files callbacks without logging IDs or session contents.
class TraceFiles extends SessionHandler {
public array $events = [];
public function open(string $path, string $name): bool { $this->events[] = 'open'; return parent::open($path, $name); }
public function close(): bool { $this->events[] = 'close'; return parent::close(); }
public function read(string $id): string|false { $this->events[] = 'read'; return parent::read($id); }
public function write(string $id, string $data): bool { $this->events[] = 'write'; return parent::write($id, $data); }
public function destroy(string $id): bool { $this->events[] = 'destroy'; return parent::destroy($id); }
public function gc(int $max_lifetime): int|false { $this->events[] = 'gc'; return parent::gc($max_lifetime); }
public function create_sid(): string { $this->events[] = 'create_sid'; return parent::create_sid(); }
}
TraceFiles наследует SessionHandler. Каждый метод добавляет имя в events и вызывает parent с теми же аргументами. Это позволяет наблюдать реальный files без копирования его файловой реализации. Native SessionHandler не следует считать универсальным обещанием strict-mode-проверки для любого собственного backend: validateId требуется проектировать отдельно.
Упражнение: одноразовое сообщение после сохранения формы
Требование: после POST перенаправить пользователя на страницу результата и показать «Saved» только один раз. Сохраните сообщение в сессию до ответа303. GET читает сообщение, сразу удаляет его ключ и закрывает сессию. Это flash-сообщение; оно не должно повторяться после обновления.
- POST: notice=Saved и close
- GET: прочитать notice и unset только этого ключа
- close сохраняет удаление
- Следующий GET: сообщения нет
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['notice'] = 'Saved'; $_SESSION['cart_qty'] = 2; session_write_close();
session_start(); $notice = $_SESSION['notice'] ?? ''; unset($_SESSION['notice']); session_write_close();
session_start(); $again = $_SESSION['notice'] ?? ''; $qty = $_SESSION['cart_qty']; session_write_close();
echo $notice, "
"; var_dump($again === ''); echo $qty, "
";
Ожидаемый результат
Saved
bool(true)
2
Оператор ?? даёт запасное значение отсутствующего ключа. В HTTP-версии сообщение при выводе в HTML экранируют htmlspecialchars. Вторая вкладка может потребить общее flash раньше — для сообщений конкретной операции используйте идентификатор операции.
Продолжение: добавьте массив сообщений по ID заявки, чтобы вкладки не забирали чужое уведомление. Саму заявку храните в БД; сессия содержит только сообщение и проверенный идентификатор.
Рабочий HTTP-стенд: два запроса, корзина, вход и выход
Теперь cookie переносится настоящими HTTP-заголовками. Создайте папки public и data рядом; сохраните код как public/router.php. Запуск из родительской папки: php -S 127.0.0.1:8011 -t public public/router.php. Стенд использует только127.0.0.1; data находится вне public. Остановите процесс после опыта. Это учебная модель одного аккаунта, без БД и ограничения попыток входа.
- GET /state → cookie и отдельный csrf-токен
- POST /cart с cookie, csrf и qty2 → проверка и запись
- GET /state с той же cookie → qty2
- POST /login demo-only → новый cookie, права только ему
- POST /logout → отзыв на сервере и истечение cookie
| Запрос | Ожидаемое действие |
|---|---|
| GET /state | JSON: visits увеличивается, qty сохранён; csrf можно использовать в следующем POST |
| POST /cart | qty=1..9, csrf из state; отсутствие csrf →403, массив qty[] →422 |
| POST /login | password=demo-only и csrf; после успеха использовать новый csrf из ответа |
| POST /logout | csrf из нового состояния; cookie истекает; старый ID повторно →409 |
| Любой пришедший неизвестный ID | files + strict mode создаёт новый вместо принятия навязанного |
В коде функция reply закрывает сессию и завершает запрос через exit; тип never сообщает, что она не возвращается вызывающему коду. $_SERVER описывает HTTP-запрос, $_POST содержит поля формы; они не становятся доверенными автоматически. is_string отсекает массив вместо поля, filter_var проверяет число и границы, hash_equals сравнивает отдельный CSRF-токен. ??= задаёт значение, только когда оно отсутствует/null. Формы и функции объясняют эти части подробнее.
<?php
// Local educational fixture only: php -S 127.0.0.1:8011 -t public public/router.php
if (($_SERVER['REMOTE_ADDR'] ?? '') !== '127.0.0.1') {
http_response_code(403); exit;
}
$savePath = dirname(__DIR__) . '/data';
if (!is_dir($savePath)) { http_response_code(503); exit; }
ini_set('session.save_handler', 'files');
ini_set('session.save_path', $savePath);
ini_set('session.gc_probability', '0'); // Entire local fixture is disposable.
ini_set('session.serialize_handler', 'php_serialize');
session_name('LessonSession');
function reply(int $status, array $data): never {
if (session_status() === PHP_SESSION_ACTIVE) { session_write_close(); }
http_response_code($status);
header('Content-Type: application/json; charset=UTF-8');
header('Cache-Control: no-store');
echo json_encode($data, JSON_THROW_ON_ERROR);
exit;
}
if (!session_start([
'use_strict_mode' => true, 'use_only_cookies' => true, 'use_cookies' => true,
'cookie_lifetime' => 0, 'cookie_path' => '/', 'cookie_httponly' => true,
'cookie_samesite' => 'Lax',
'cookie_secure' => false, // Only this loopback HTTP laboratory. HTTPS site: true.
])) { reply(503, ['error' => 'session-unavailable']); }
if (isset($_SESSION['revoked_at']) || isset($_SESSION['retired_at'])) {
reply(409, ['error' => 'session-retired']);
}
$now = time();
if (isset($_SESSION['user_id'])) {
if ($now - $_SESSION['last_seen'] >= 900 || $now - $_SESSION['created_at'] >= 3600) {
unset($_SESSION['user_id']); $_SESSION['revoked_at'] = $now;
reply(401, ['error' => 'login-expired']);
}
$_SESSION['last_seen'] = $now;
}
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET' && $path === '/state') {
$_SESSION['visits'] = ($_SESSION['visits'] ?? 0) + 1;
reply(200, ['visits' => $_SESSION['visits'], 'qty' => $_SESSION['qty'] ?? 0,
'csrf' => $_SESSION['csrf'], 'logged_in' => isset($_SESSION['user_id'])]);
}
if ($method !== 'POST') { reply(405, ['error' => 'post-required']); }
$token = $_POST['csrf'] ?? null;
if (!is_string($token) || !hash_equals($_SESSION['csrf'], $token)) {
reply(403, ['error' => 'csrf']);
}
if ($path === '/cart') {
$raw = $_POST['qty'] ?? null;
$qty = is_string($raw) ? filter_var($raw, FILTER_VALIDATE_INT,
['options' => ['min_range' => 1, 'max_range' => 9]]) : false;
if ($qty === false) { reply(422, ['error' => 'quantity']); }
$_SESSION['qty'] = $qty; reply(200, ['qty' => $qty]);
}
if ($path === '/login') {
$password = $_POST['password'] ?? null;
// One public demo account; real application retrieves a precomputed hash from its DB.
$demoHash = password_hash('demo-only', PASSWORD_DEFAULT);
if (!is_string($password) || !password_verify($password, $demoHash)) {
reply(401, ['error' => 'credentials']);
}
$_SESSION['retired_at'] = $now;
if (!session_regenerate_id(false)) { reply(503, ['error' => 'rotation']); }
unset($_SESSION['retired_at']);
$_SESSION['user_id'] = 42; $_SESSION['created_at'] = $now; $_SESSION['last_seen'] = $now;
$_SESSION['csrf'] = bin2hex(random_bytes(32));
reply(200, ['logged_in' => true, 'csrf' => $_SESSION['csrf']]);
}
if ($path === '/logout') {
unset($_SESSION['user_id'], $_SESSION['csrf']); $_SESSION['revoked_at'] = $now;
$cookie = session_get_cookie_params();
setcookie(session_name(), '', [
'expires' => 1, 'path' => $cookie['path'], 'domain' => $cookie['domain'],
'secure' => $cookie['secure'], 'httponly' => $cookie['httponly'],
'samesite' => $cookie['samesite'],
]);
reply(200, ['logged_in' => false]);
}
reply(404, ['error' => 'route']);
Это HTTP-пример: обычный запуск php router.php не создаёт браузерный запрос. В локальном стенде фактически проверены cookie, перенос корзины, негативные POST, ротация и отзыв. Secure=false — строго для loopback HTTP; сайт требует HTTPS и Secure=true.
Учебный вход вычисляет хеш публичного demo-пароля на месте для самодостаточности; реальный сайт хранит заранее вычисленный password_hash в БД и ограничивает попытки. Стенд немедленно отвергает retired-ID кодом409: это безопасный отказ без бесшовного восстановления параллельных вкладок. Production-менеджер должен спроектировать переходное окно и восстановление, описанные выше. Истечение cookie в браузере не отзывает копию у злоумышленника, поэтому сервер хранит revoked_at. GC/планировщик физически удаляет такие записи позже.
Вход, фиксация, отзыв, сроки и несколько параллельных запросов
Сессия сохраняет состояние, но сама не проверяет пароль, права и CSRF. Кража ID даёт предъявителю доступ к соответствующим данным. HTTPS на всём сайте и корректный HSTS уменьшают сетевую утечку; Secure/HttpOnly/SameSite ограничивают передачу, но не лечат XSS, вредоносное расширение браузера или скомпрометированный компьютер. Не выводите ID в HTML/журналы и не используйте его вместо CSRF-токена.
- Проверены форма, CSRF, пароль, ограничения попыток
- Помечен старый гостевой ID; ротация успешна
- Только новому ID назначены user_id, created_at, last_seen
- Каждый закрытый маршрут проверяет сроки, отзыв и права
| Механизм | Что решает | Что остаётся |
|---|---|---|
| Strict mode | Отказ от несуществующего ID атакующего | Уже созданный чужой ID может существовать; нужна ротация |
| Ротация ID | Меняет ключ после входа/MFA и по выбранной политике | Старые данные при false надо ограничить timestamps; true создаёт гонки |
| Idle timeout | Отказать после бездействия | Постоянно активный похищенный ключ требует абсолютного срока |
| Absolute timeout | Ограничить общий возраст входа | Для продолжения требуется новая аутентификация |
| GC | Убрать устаревшие записи физически | Не заменяет проверку доступа перед действием |
В приложении держат created_at, last_seen, retired_at/revoked_at. При повышении прав сначала ротируют, потом добавляют авторизацию — старый гостевой ID её не получает. Для плановой ротации уже вошедшего пользователя требуется политика короткого переходного окна: старый ID ограничен по времени/операциям, новые чувствительные действия требуют нового. Нельзя просто оставить старую авторизацию навсегда при false. Немедленное удаление при true может заставить конкурентный запрос создать ещё один ID, а запоздалый Set-Cookie заменить новый. Не ротируйте каждый AJAX и не вызывайте вслед за ротацией destroy новой активной сессии.
При проблемной сети повторный старый ID не доказывает атаку. Приложение может вести реестр активных сессий пользователя: создана/последняя активность/отзыв/устройство, хранить серверную версию авторизации, принудительно отзывать все входы и предлагать повторную проверку. Согласование ротации, реестра и backend требует атомарности; PHP его не создаёт. Префикс ID допускается, но не должен раскрывать секретный user_id/email; лучше отдельный индекс user_id→sessions, при необходимости HMAC вместо открытой личной метки.
<?php
function accessAllowed(array $session, int $now): bool {
if (isset($session['revoked_at'])) { return false; }
if (!isset($session['user_id'], $session['created_at'], $session['last_seen'])) { return false; }
return $now - $session['last_seen'] < 900 && $now - $session['created_at'] < 3600;
}
$state = ['user_id' => 42, 'created_at' => 1000, 'last_seen' => 1500];
var_dump(accessAllowed($state, 1600), accessAllowed($state, 2400));
$state['last_seen'] = 4500;
var_dump(accessAllowed($state, 4600));
Ожидаемый результат
bool(true)
bool(false)
bool(false)
Это функция политики над собственными целыми timestamps, не получение непроверенных значений от браузера. Ровно на границе отказ. После успешной проверки защищённый запрос обновляет last_seen; бизнес-права проверяются отдельно.
«Запомнить меня» — отдельный долгоживущий одноразовый случайный токен, защищённый cookie-атрибутами, с хешем и сроком на сервере, ротацией после использования и возможностью отзыва. Не делайте обычный session-ID бессрочным. Одновременное потребление токена нужно атомарно ограничить. Нельзя сохранять пароль пользователя в $_SESSION.
POST и SameSite сами по себе не устраняют CSRF. В стенде используется отдельный random_bytes-токен и hash_equals после проверки типа; настоящее приложение также валидирует вход, права, ограничивает попытки входа и корректно экранирует HTML. Подробнее проверка формы и защита веб-приложения.
Почему соседний AJAX ждёт и что на самом деле освобождает close
Файловый backend удерживает эксклюзивную блокировку от чтения до закрытия. Это защищает обновление корзины от потери при двух запросах одного ID. Если первый запрос ждёт внешний API с открытой сессией, второй ждёт ещё на start. Прочитайте проверенный user_id, внесите нужные изменения, закройте сессию и только затем выполняйте долгую независимую работу.
- A открыл ID и держит lock
- B дошёл до start того же ID и ждёт
- A записал qty2 и закрыл
- B прочитал qty2 и продолжил
Смена files на Redis не гарантирует устранения ожидания: поведение определяет handler и его настройки блокировки. Простое отключение lock даёт lost update. read_and_close сокращает удержание, но не обновляет mtime/TTL тем же путём, что обычное закрытие. Сессия не блокирует общий товар для двух разных пользователей: последнюю единицу защищают транзакция/ограничение БД.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['user_id'] = 42;
$userId = $_SESSION['user_id'];
session_write_close();
// A slow report can now use the validated local user ID.
$queryScope = ['owner_id' => $userId];
echo $queryScope['owner_id'], "
";
var_dump(session_status() === PHP_SESSION_NONE);
Ожидаемый результат
42
bool(true)
user_id задан самим учебным примером, реальный вход устанавливает его после проверки пароля. Код не имитирует долгий отчёт sleep и не обещает транзакцию БД.
Отдельный локальный двухпроцессный опыт проверяет ожидание до закрытия и чтение после освобождения. Однопоточный встроенный PHP-сервер не подходит для доказательства session-lock: он сам последовательно обслуживает запросы, независимо от session.
Лаборатория блокировки: два независимых PHP-процесса
Этот опыт запускают в одной одноразовой папке. Оба файла используют приложенный CLI-bootstrap и его session-data. Сначала holder.php открывает сессию и записывает её ID в локальный ready.json. Второй процесс reader.php получает этот ID только от первого, а не от внешнего запроса. После появления reader-started контроллер проверяет, что reader-acquired ещё нет, затем создаёт пустой файл release. Ничего не запускайте в существующей папке с чужими служебными файлами.
- A: записал ready, lock удерживается
- B: reader-started создан, start ждёт
- Контроллер: release создан
- A: qty2 сохранено, lock отпущен
- B: reader-acquired создан, qty2 прочитан
microtime(true) задаёт предел ожидания10секунд, usleep(20000) — паузу20мс между проверками локального барьера. Это управление опытом, не рецепт ожидания внешнего API. Контроллер обязан завершить свои дочерние процессы в finally даже при ошибке. Реальный прогон делался двумя процессами PHP, без встроенного HTTP-сервера, поэтому его однопоточность не объясняет результат.
<?php
require __DIR__ . '/bootstrap.php';
if (!session_start()) { throw new RuntimeException('Start failed'); }
file_put_contents(__DIR__ . '/ready.json', json_encode(['id' => session_id()], JSON_THROW_ON_ERROR));
$deadline = microtime(true) + 10;
while (!is_file(__DIR__ . '/release')) {
if (microtime(true) >= $deadline) { session_abort(); throw new RuntimeException('Release timeout'); }
usleep(20000);
}
$_SESSION['cart_qty'] = 2;
session_write_close();
echo "Holder saved\n";
Контекстный файл ожидает второй процесс и локальный сигнал release. В одиночку завершится ошибкой таймаута, поэтому не помечен обычным run.
<?php
require __DIR__ . '/bootstrap.php';
if (!isset($argv[1]) || !is_string($argv[1])) { throw new RuntimeException('Fixture ID required'); }
session_id($argv[1]);
file_put_contents(__DIR__ . '/reader-started', 'before session_start');
if (!session_start()) { throw new RuntimeException('Start failed'); }
file_put_contents(__DIR__ . '/reader-acquired', 'after session_start');
$qty = $_SESSION['cart_qty'];
session_write_close();
echo 'Reader qty: ', $qty, "\n";
Команда: php reader.php ID-из-ready.json. Фиксируется факт блокировки и значение2 после закрытия A; конкретная скорость не обещается.
Зачем менять хранилище и кто вызывает методы
На одном сервере files хранит корзину между запросами. При нескольких серверах нужен общий backend или согласованная маршрутизация: иначе следующий сервер не найдёт файл. Пользовательский handler позволяет оставить приложение с $_SESSION, заменив транспорт хранения.
- Приложение регистрирует объект
- PHP: open → validateId/create_sid → read
- Приложение читает и меняет $_SESSION
- PHP: write или updateTimestamp → close
Это основной путь, не неизменный список: create_sid вызывается только для нового ID; validateId — при переданном ID и strict mode; GC может запускаться по политике. abort/read_and_close пропускают запись; destroy удаляет запись. Разбор session_set_save_handler показывает все формы и параметры.
В примерах ниже class определяет новый тип объекта, new LessonStore() создаёт его. implements обещает PHP конкретные методы интерфейса; public разрешает их вызывать, $this — текущий объект, -> обращается к его методу/полю. : bool обозначает true/false, string|false — строку либо отказ. return отдаёт результат вызывающему PHP, а не печатает его. Интерфейсы и наследование раскрывают этот синтаксис отдельно.
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; session_write_close();
echo implode(' → ', $store->events), "\n";
Ожидаемый результат
open → create_sid → read → write → close
Порядок наблюдается в изолированном файле с gc_probability=0, поэтому случайного GC в журнале нет.
SessionHandler: добавить наблюдение, сохранив родной backend
Если нужно измерить количество чтений файлового хранилища, необязательно переписывать I/O. Наследник SessionHandler перехватывает метод и вызывает parent; новый backend через interface — другая задача.
- PHP вызывает TraceFiles::write(id,data)
- Журнал получает только слово write
- parent::write передаёт точные байты files
- Результат возвращается PHP
В примерах ниже class определяет новый тип объекта, new LessonStore() создаёт его. implements обещает PHP конкретные методы интерфейса; public разрешает их вызывать, $this — текущий объект, -> обращается к его методу/полю. : bool обозначает true/false, string|false — строку либо отказ. return отдаёт результат вызывающему PHP, а не печатает его. Интерфейсы и наследование раскрывают этот синтаксис отдельно.
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; session_write_close();
session_start(); $qty = $_SESSION['cart_qty']; session_write_close();
echo $qty, "\n"; var_dump(in_array('read', $store->events, true));
Ожидаемый результат
2
bool(true)
Полная TraceFiles приложена. Она не логирует идентификаторы/содержимое и не шифрует данные.
SessionHandler реализует SessionHandlerInterface и SessionIdInterface, доступен сPHP5.4; create_sid добавлен в5.5.1. Сначала выбирают внутренний handler, потом регистрируют обёртку. У стандартной обёртки нет validateId: регистрация требует отдельного решения strict mode. Пример шифрования должен обеспечивать аутентичность данных и управление ключами; произвольное преобразование/base64 не даёт защиты.
SessionIdInterface: добавить генератор, а не всю сессию
Интерфейс содержит только create_sid():string и добавляется к основному SessionHandlerInterface. Без основного договора объект не становится хранилищем. PHP вызывает его при необходимости нового ID; SessionIdInterface::create_sid показывает новое и повторное открытие.
- Основной handler уже умеет read/write
- SessionIdInterface добавляет create_sid
- PHP получает непредсказуемый ID, данные сохраняет другим методом
При собственном ID атомарное резервирование/уникальный индекс относится к backend. Проверка существования в памяти одного процесса не обещает отсутствие межпроцессных гонок.
SessionUpdateTimestampHandlerInterface: существование и продление
Два метода решают разные задачи: validateId проверяет принесённый ID до чтения, updateTimestamp обновляет время неизменившихся данных при закрытии. Они добавляются к основным шести методам; наличие одного названия без корректной реализации защиты не даёт.
| Метод | Когда нужен | Что проверяет |
|---|---|---|
| validateId | При start со strict mode и готовым ID | Запись существует, ID допустим для backend |
| updateTimestamp | При lazy_write и неизменных байтах | Запись получает новый updated_at/TTL |
SessionUpdateTimestampHandlerInterface::validateId и SessionUpdateTimestampHandlerInterface::updateTimestamp содержат отдельные исполняемые сценарии. Обновлённый TTL хранилища не отменяет абсолютный срок входа приложения.
Все session-настройки: единицы, область действия и версии
Сначала определите, что настраивается: хранение, cookie, HTTP-кэш, уборка или автоматический прогресс upload. Ни одна длительность не заменяет остальные. Ниже значения из официальной базовой конфигурации; дистрибутив и php.ini могут их переопределить. INI_ALL разрешает runtime-переопределение, но session-настройку обычно нельзя менять уже после старта; INI_PERDIR задают конфигурацией до запроса, а не поздним ini_set.
| Настройка | Default / доступность | Смысл и последствия |
|---|---|---|
| session.save_handler | files · INI_ALL | Backend хранения; files встроен, другие зависят от расширений. Пользовательский объект — через set_save_handler, не строку user. |
| session.save_path | пусто · INI_ALL | Аргумент backend. Для files закрытый абсолютный каталог, необязательные N;MODE;path; подробнее ниже. |
| session.name | PHPSESSID · INI_ALL | Имя cookie/исторического параметра ID; одинаково в bootstrap всех маршрутов приложения. |
| session.auto_start | 0 · INI_PERDIR | Открыть сессию до пользовательского кода. Определения классов для её объектов нужно загрузить заранее через auto_prepend_file; проще явно start после автозагрузчика. |
| session.serialize_handler | php · INI_ALL | php/php_binary/php_serialize. Последний кодирует целый массив и снимает ограничения корневых ключей. Формат всех процессов должен совпадать. |
| session.gc_probability | 1 · INI_ALL | Числитель вероятности запуска GC при старте; >=0. Ноль отключает случайную уборку. |
| session.gc_divisor | 100 · INI_ALL | Знаменатель вероятности, >0. 1/100 = 1%, а не гарантированно каждый сотый запрос. |
| session.gc_maxlifetime | 1440 · INI_ALL | Возраст в секундах для уборки, не срок авторизации. Минимальный срок одного приложения может удалить общие файлы другого. |
| session.lazy_write | 1 · INI_ALL | Не переписывать неизменившийся payload при поддерживаемом timestamp-пути; не отменяет start, чтение и возможную блокировку. |
| session.use_cookies | 1 · INI_ALL | Передавать ID cookie. Ноль в наших CLI-labs исключает HTTP; на сайте используется1. |
| session.use_only_cookies | 1 · INI_ALL | Не брать ID из GET/POST. Отключение deprecated с8.4; не используйте как обход отказа cookies. |
| session.use_strict_mode | 0 · INI_ALL | Отвергать ID, которого нет в backend. Для сайта включить1; кастомному handler нужен validateId/validate_sid. |
| session.cookie_lifetime | 0 · INI_ALL | Секунды жизни cookie;0 — session-cookie. Не определяет TTL файла, не гарантирует logout при восстановлении браузера. |
| session.cookie_path | / · INI_ALL | На каких путях браузер прикладывает cookie; не барьер авторизации. |
| session.cookie_domain | пусто · INI_ALL | Без Domain — host-only. Указанный допустимый домен включает поддомены; ведущая точка современным браузерам не нужна. |
| session.cookie_secure | 0 · INI_ALL | Cookie только через HTTPS. Для production HTTPS включить1; доPHP7.2 default был пустой строкой. |
| session.cookie_httponly | 0 · INI_ALL | Запретить чтение cookie JavaScript. Не блокирует действия XSS от имени пользователя; до7.2 default был пустой строкой. |
| session.cookie_samesite | пусто · INI_ALL | PHP7.3+: пусто не добавляет атрибут; Lax/Strict/None выбирают browser-policy. None требует Secure. |
| session.cookie_partitioned | 0 · INI_ALL | PHP8.5+: Partitioned/CHIPS для изоляции по top-level-site. Требует Secure; иначе PHP предупреждает и не отправляет session-cookie. |
| session.cache_limiter | nocache · INI_ALL | nocache/private/private_no_expire/public либо пусто для ручного cache-control. Не определяет cookie и срок входа. |
| session.cache_expire | 180 · INI_ALL | Минуты HTTP-кэша; nocache игнорирует. Не путать с gc_maxlifetime в секундах. |
| session.use_trans_sid | 0 · INI_ALL | Автоматическая подстановка ID в HTML-ссылки/формы. Включение deprecated с8.4; риск утечки через URL. |
| session.trans_sid_tags | a=href,area=href,frame=src,form= · INI_ALL | Какие теги/атрибуты переписывать; form создаёт hidden-поле. Изменение default deprecated8.4. До7.1 использовался url_rewriter.tags; fieldset больше не особый тег. |
| session.trans_sid_hosts | пусто (текущий HTTP_HOST) · INI_ALL | Разрешённые хосты для URL rewriting, через запятую без пробелов. Явное непустое значение deprecated8.4; чужой host раскрывает ID. |
| session.referer_check | пусто · INI_ALL | Историческая проверка подстроки Referer при принятии URL-ID. Непустое значение deprecated8.4; Referer может отсутствовать, это не надёжная защита. |
| session.sid_length | 32 · INI_ALL | PHP7.1+: допустимая длина22..256. Изменение default deprecated8.4; оставьте генерацию современному PHP. |
| session.sid_bits_per_character | 4 · INI_ALL | PHP7.1+:4(hex),5(0–9 a–v),6(0–9 a–z A–Z , -). Изменение default deprecated8.4; не увеличивайте наугад. |
| session.upload_progress.enabled | 1 · INI_PERDIR | Записывать метрики multipart upload до исполнения скрипта; отключите, если не используете серверный прогресс. |
| session.upload_progress.cleanup | 1 · INI_PERDIR | Удалить прогресс после чтения всего POST. Обычно оставить включённым. |
| session.upload_progress.prefix | upload_progress_ · INI_PERDIR | Префикс ключа в $_SESSION; дополняется значением клиентского hidden-поля. |
| session.upload_progress.name | PHP_SESSION_UPLOAD_PROGRESS · INI_PERDIR | Имя hidden-поля POST, которое включает наблюдение. Его value — идентификатор операции; это не имя ID-cookie. |
| session.upload_progress.freq | 1% · INI_PERDIR | Как часто обновлять: байты либо процент от размера POST. Частая запись увеличивает I/O/конкуренцию. |
| session.upload_progress.min_freq | 1 · INI_PERDIR | Минимальная задержка между обновлениями в секундах; вместе с freq ограничивает частоту. |
| session.hash_function | 0 · удалена7.1 | Исторически MD5/SHA-1 или алгоритм hash. Не существует как рабочая настройка PHP8.x. |
| session.hash_bits_per_character | 4 · удалена7.1 | Старое кодирование4/5/6 бит; заменено sid_bits_per_character, изменение которого теперь также устаревает. |
| session.entropy_file | исторически путь источника · удалена7.1 | Файл случайности для старого генератора; наWindows использовался системный RNG. ВPHP8.x не настраивается. |
| session.entropy_length | исторически число байтов · удалена7.1 | Сколько дополнительных байтов случайности читать. Не рецепт для современного генератора. |
Для files запись "2;0600;/srv/app/sessions" означает два уровня подкаталогов и режим создаваемого файла0600. Все подкаталоги заранее существуют; MODE не изменяет umask. При N>0 автоматическая файловая GC не проходит дерево. Рост числа директорий экспоненциальный: при hex и N=3 нужно16³=4096 каталогов. Не выбирайте большие N без измеренной необходимости; храните папку вне public и не делайте доступной посторонним пользователям ОС.
Для новой схемы данных можно явно выбрать php_serialize во всех процессах. Форматы php/php_binary ограничивают корневые числовые ключи; php также запрещает символ | в корневом имени. Вложенные ключи этих ограничений не имеют. В PHP8.5 отказ записи из-за | сопровождается предупреждением. Упоминание WDDX в старых объяснениях относится к расширению, вынесенному из дистрибутива вPHP7.4; оно не означает доступный сериализатор в любой PHP8.5-сборке.
- session_set_cookie_params: lifetime600
- ini_get: та же настройка видна как строка600
- Срок входа приложения остаётся отдельной величиной
<?php
require __DIR__ . '/bootstrap.php';
ini_set('session.use_cookies', '1');
session_set_cookie_params(['lifetime' => 600]);
echo ini_get('session.cookie_lifetime'), "
";
echo ini_get('session.serialize_handler'), "
";
Ожидаемый результат
600
php_serialize
Настройка API и ini_get согласованы в одном запросе, но следующий процесс начинает со своего php.ini.
Состояния сессии и историческая константа SID
| Константа | Значение и назначение |
|---|---|
| PHP_SESSION_DISABLED | 0: механизм недоступен/отключён |
| PHP_SESSION_NONE | 1: сейчас нет открытой сессии; файл и ID при этом могут существовать |
| PHP_SESSION_ACTIVE | 2: сессия открыта в текущем запросе, но это ещё не авторизация |
| SID | Историческая строка name=ID или пусто при подходящей cookie; deprecated8.4. Не вставляйте её в ссылки. |
- status===NONE → допустим start
- status===ACTIVE → можно менять $_SESSION
- close → сноваNONE
<?php
require __DIR__ . '/bootstrap.php';
$before = session_status() === PHP_SESSION_NONE;
session_start(); $during = session_status() === PHP_SESSION_ACTIVE; session_abort();
var_dump($before, $during);
Ожидаемый результат
bool(true)
bool(true)
Числа приведены как контракт справочника; рабочий код использует осмысленные константы.
Прогресс загрузки: данные появляются до запуска обработчика формы
При multipart-загрузке PHP может записывать количество принятых байтов в сессию ещё до запуска upload.php. Отдельный запрос опрашивает это состояние. Это прогресс приёма HTTP-body, а не обработка видео/импорт файла после загрузки. Для долгой обработки нужна отдельная задача и её собственный статус.
- Форма уже получила session-cookie и upload_id
- multipart: hidden PHP_SESSION_UPLOAD_PROGRESS идёт перед file
- PHP принимает байты и обновляет upload_progress_ + value
- Другой запрос с той же cookie читает текущие счётчики
- После чтения POST cleanup обычно удаляет запись
| Поле/настройка | Как использовать |
|---|---|
| Имя и ключ | hidden.name берётся из session.upload_progress.name; key=prefix+ЗНАЧЕНИЕ hidden. Это не prefix+имя поля. ID операции заранее связывают с текущей сессией. |
| Общие поля | start_time — timestamp начала; content_length — размер всего POST; bytes_processed — принятые байты; done — завершён разбор тела, даже если файл ошибочен. |
| files[] | field_name, name, tmp_name, error, done, start_time, bytes_processed по файлу. name/field_name пришли от клиента, не являются безопасным HTML или PHP-кодом. |
| Отмена | cancel_upload=true отменяет текущий и оставшиеся файлы; уже принятые не удаляет. В $_FILES появляется UPLOAD_ERR_EXTENSION. |
| Обновление | freq в байтах/% и min_freq в секундах; слишком частое обновление повышает I/O и конкуренцию. |
| Cleanup | По умолчанию запись исчезает после разбора POST. Отсутствие означает unknown, а не 100% успеха. |
| Окружение | Reverse proxy должен передавать тело потоково, иначе PHP увидит загрузку лишь после её буферизации. Один worker не обслужит опрос параллельно. |
| До кода | Изменение name/save_path через поздний session_name/ini_set не влияет на уже записанный прогресс. Нужна конфигурация до запроса. Объектный custom-handler из скрипта для этой стадии не используется. |
Форма использует POST и enctype=multipart/form-data. Hidden-поле с меткой операции ставится перед file-input; его имя и значение при HTML-выводе экранируются. csrf хранится отдельным полем, а права/тип/размер файла всё равно проверяет обработчик upload. Не подключайте содержимое session-файла через include/require: в прогресс попадают клиентские имена. Если серверный прогресс не нужен, отключите enabled в конфигурации до запроса.
<?php
$progress = ['bytes_processed' => 1500, 'content_length' => 6000, 'done' => false];
$total = $progress['content_length'];
$percent = $total > 0 ? min(100, (int) floor(100 * $progress['bytes_processed'] / $total)) : null;
echo $percent, "%\n";
echo $progress['done'] ? "Body received\n" : "Receiving\n";
Ожидаемый результат
25%
Receiving
Это детерминированный снимок структуры, не измерение реальной длительной передачи. Общий POST содержит служебные multipart-байты, поэтому content_length не обязательно равен сумме размеров файлов.
Следующая форма создаёт собственную метку операции и отдельный csrf. Короткий PHP-тег <?= выводит подготовленное значение; функция html экранирует его для HTML-атрибута. Поле progress обязательно стоит раньше file. upload.php должен отдельно проверить CSRF, тип/размер и результат загрузки; здесь не создаётся обработчик сохранения файла.
<?php
// Configure session.name/save_path/upload_progress.* before the request, in the server configuration.
if (!session_start()) { http_response_code(503); exit; }
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
$id = bin2hex(random_bytes(8));
$_SESSION['allowed_uploads'][$id] = time();
$csrf = $_SESSION['csrf'];
$field = ini_get('session.upload_progress.name');
session_write_close();
function html(string $value): string {
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
?>
<!doctype html>
<html lang="ru"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Учебная загрузка</title>
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="<?= html($field) ?>" value="<?= html($id) ?>">
<input type="hidden" name="csrf" value="<?= html($csrf) ?>">
<label for="report">Файл отчёта</label>
<input id="report" name="report" type="file" required>
<button type="submit">Загрузить</button>
</form>
</html>
HTTP-контекст с теми же серверными name/save_path, что у endpoints. В реальном приложении ограничьте число allowed_uploads и убирайте старые метки после завершения; эта форма создаёт одну учебную операцию.
Перед следующим кодом общий bootstrap уже выбрал те же session name/path, что используются конфигурацией загрузки, и страница формы записала allowed_uploads[$id] и отдельный csrf. Вызов ctype_alnum здесь ограничивает только метку операции; имя загружаемого файла не используют как ключ доступа.
<?php
// HTTP endpoint; common bootstrap has configured session name/path before request parsing.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); exit; }
if (!session_start(['read_and_close' => true])) { http_response_code(503); exit; }
$token = $_POST['csrf'] ?? null;
$id = $_POST['upload_id'] ?? null;
if (!is_string($token) || !isset($_SESSION['csrf']) || !hash_equals($_SESSION['csrf'], $token)) {
http_response_code(403); exit;
}
if (!is_string($id) || strlen($id) > 64 || !ctype_alnum($id)
|| !isset($_SESSION['allowed_uploads'][$id])) { http_response_code(400); exit; }
$key = ini_get('session.upload_progress.prefix') . $id;
$progress = $_SESSION[$key] ?? null;
header('Content-Type: application/json; charset=UTF-8');
header('Cache-Control: no-store');
if (!is_array($progress)) {
echo json_encode(['state' => 'unknown'], JSON_THROW_ON_ERROR); exit;
}
echo json_encode([
'state' => $progress['done'] ? 'received' : 'receiving',
'bytes' => $progress['bytes_processed'], 'total' => $progress['content_length'],
], JSON_THROW_ON_ERROR);
HTTP-контекст с настроенным multipart progress и параллельными workers. Пример проверен синтаксически; реальное время обновления зависит от SAPI/proxy и не заявляется измеренным этим примером.
<?php
// HTTP endpoint after the same bootstrap; separate request from the upload.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); exit; }
if (!session_start()) { http_response_code(503); exit; }
$token = $_POST['csrf'] ?? null;
$id = $_POST['upload_id'] ?? null;
$valid = is_string($token) && isset($_SESSION['csrf']) && hash_equals($_SESSION['csrf'], $token)
&& is_string($id) && strlen($id) <= 64 && ctype_alnum($id)
&& isset($_SESSION['allowed_uploads'][$id]);
if (!$valid) { session_abort(); http_response_code(403); exit; }
$key = ini_get('session.upload_progress.prefix') . $id;
if (isset($_SESSION[$key]) && !$_SESSION[$key]['done']) {
$_SESSION[$key]['cancel_upload'] = true;
}
session_write_close(); http_response_code(204);
Та же cookie и тот же серверный allowed_uploads. При завершившейся операции код возвращает204 без создания ложной progress-записи. Успешно загруженные файлы он не удаляет.
Все функции и методы этой ветки
По задаче выбирайте функцию, затем переходите к договору и двум разным примерам. Прежние карточки сессий сохранены в главе авторизации; ссылки ведут к их существующим якорям.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
session_commitСохранить текущие данные и освободить сессию для соседних запросов. Это точный псевдоним session_write_close.
session_commit(): boolСохранить выбор тем же действием под именем commit. session_commit() — псевдоним session_write_close: запись и закрытие сессии. Слова commit здесь не означают транзакцию SQL или всего заказа.
- delivery=Пункт выдачи
- commit сохраняет и закрывает, как write_close
- Новый start читает Пункт выдачи
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Сохранить текущие данные и освободить сессию для соседних запросов. Это точный псевдоним session_write_close.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. Вызывать после последнего изменения $_SESSION, до долгого API-запроса/рендеринга. |
| Результат | true при закрытии активной сессии; false, если активной сессии нет. Bool у write_close с PHP7.2 (раньше void); commit — alias с PHP4.4. |
| Граница гарантии | В PHP8.5.11 true не удостоверяет успешную долговечную запись: внутренний write может предупредить об отказе, а функция всё равно вернуть true. Не делайте сессию единственным журналом платежа/заказа. |
| После закрытия | $_SESSION остаётся доступен в памяти. Изменения без нового старта не попадут в хранилище; блокировка files уже освобождена. Это не завершает весь HTTP-ответ и не отправляет его немедленно. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['delivery'] = 'Пункт выдачи'; session_commit();
session_start(); $saved = $_SESSION['delivery']; session_write_close();
echo 'Сохранённая доставка: ', $saved, "\n";
Ожидаемый результат
Сохранённая доставка: Пункт выдачи
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Второе закрытие false не записывает устаревшую локальную копию. Для реальной базы авторизация и изменения заказа выполняются отдельно.
<?php
require __DIR__ . '/bootstrap.php';
session_start();
$_SESSION['notice'] = 'Saved';
session_commit();
$_SESSION['notice'] = 'Only in memory';
$again = session_commit();
session_start();
$saved = $_SESSION['notice'];
session_write_close();
var_dump($again);
echo $saved, "\n";
Ожидаемый результат
bool(false)
Saved
Дальше: session_abort; session_reset; session_start.
session_abortЗакрыть сессию, отказавшись от несохранённых изменений.
session_abort(): boolОтменить несохранённую смену доставки. До редактирования сохранён «Самовывоз». Новое значение пока только в памяти. abort закрывает работу без записи; внешний платёж или запрос к БД он не отменяет.
- Сохранено Самовывоз → в памяти выбран Курьер
- abort закрывает без новой записи
- Новый start возвращает Самовывоз
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Закрыть сессию, отказавшись от несохранённых изменений.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет; требуется активная сессия. |
| Результат | true при закрытии; false без активной сессии. Доступна с PHP5.6, bool с7.2, ранее void. |
| Что отменяется | Только текущая запись сессии. Уже выполненный запрос к БД или отправленное письмо не откатываются. Массив в памяти может содержать отвергнутые значения. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['delivery'] = 'Самовывоз'; session_write_close();
session_start(); $_SESSION['delivery'] = 'Курьер'; session_abort();
session_start(); $saved = $_SESSION['delivery']; session_write_close();
echo 'После отмены: ', $saved, "\n";
Ожидаемый результат
После отмены: Самовывоз
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Не используйте оставшийся массив как доказательство сохранения.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['draft'] = 'not saved';
$ok = session_abort();
$local = $_SESSION['draft'];
$second = session_abort();
var_dump($ok, $second);
echo $local, "
";
Ожидаемый результат
bool(true)
bool(false)
not saved
Дальше: session_reset; session_write_close.
session_resetПеречитать сохранённую сессию и продолжить работать с ней.
session_reset(): boolПеречитать выбор и продолжить редактирование. reset заменяет текущие значения сохранёнными, оставляя сессию открытой. Поэтому после возврата к самовывозу можно выбрать пункт выдачи и сохранить его.
- Сохранено Самовывоз → в памяти выбран Курьер
- reset возвращает Самовывоз и оставляет сессию открытой
- Новый выбор Пункт выдачи сохраняется через write_close
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Перечитать сохранённую сессию и продолжить работать с ней.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет; активная сессия обязательна. |
| Результат | true при выполнении для активной сессии, false без неё. PHP5.6+, bool с7.2. |
| Поведение | Отбрасывает текущие изменения $_SESSION, заново вызывает чтение обработчика. В отличие от abort нормальное состояние остаётся ACTIVE. Это не транзакционный откат внешних действий. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['delivery'] = 'Самовывоз'; session_write_close();
session_start(); $_SESSION['delivery'] = 'Курьер'; session_reset();
$restored = $_SESSION['delivery'];
$_SESSION['delivery'] = 'Пункт выдачи'; session_write_close();
session_start(); $saved = $_SESSION['delivery']; session_write_close();
echo 'Восстановлено: ', $restored, "\nСохранено затем: ", $saved, "\n";
Ожидаемый результат
Восстановлено: Самовывоз
Сохранено затем: Пункт выдачи
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Второй reset без открытой сессии ничего не делает.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['currency'] = 'RUB'; session_write_close();
session_start(); $_SESSION['currency'] = 'USD'; session_reset();
$_SESSION['currency'] = 'EUR'; session_write_close();
session_start(); $value = $_SESSION['currency']; session_write_close();
$outside = session_reset(); echo $value, "
"; var_dump($outside);
Ожидаемый результат
EUR
bool(false)
Дальше: session_abort; session_start.
session_unsetОчистить все ячейки открытой сессии, сохранив её идентификатор.
session_unset(): boolОчистить всю гостевую корзину. Вызов удаляет все ячейки открытого массива. Поэтому при наличии auth он удалил бы и вход: для одной корзины лучше unset($_SESSION['cart']). Здесь сессия намеренно содержит только выбор гостя.
- Гостевая сессия: cart=Кисть:2, coupon=DEMO
- unset очищает обе ячейки → close сохраняет очистку
- Следующее открытие: Корзина пуста
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Очистить все ячейки открытой сессии, сохранив её идентификатор.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. Для одной ячейки используйте unset($_SESSION['key']). |
| Результат | true для активной сессии, false без неё; bool с PHP7.2, ранее void. |
| После вызова | $_SESSION пуст; сессия остаётся активной. Чтобы очистка пережила запрос, нужно сохранить/закрыть. Cookie и ID не удаляются. Не вызывайте unset($_SESSION) целиком. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION = ['cart' => 'Кисть:2', 'coupon' => 'DEMO'];
session_unset(); session_write_close();
session_start(); $cart = $_SESSION['cart'] ?? 'Корзина пуста'; session_write_close();
echo $cart, "\n";
Ожидаемый результат
Корзина пуста
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
unset — конструкция языка, session_unset — функция полного очищения активной сессии.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION = ['cart_qty' => 2, 'coupon' => 'SALE'];
unset($_SESSION['coupon']); session_write_close();
session_start(); $qty = $_SESSION['cart_qty']; $hasCoupon = isset($_SESSION['coupon']); session_write_close();
echo $qty, "
"; var_dump($hasCoupon, session_unset());
Ожидаемый результат
2
bool(false)
bool(false)
Дальше: session_destroy; session_write_close.
session_idУзнать текущий ID или выбрать уже существующую сессию до старта.
session_id(?string $id = null): string|falseОткрыть собственный сохранённый выбор по его ключу. Без аргумента функция читает непрозрачный ID, со строкой выбирает ID до start. В этом опыте ключ получен самим PHP; на сайте его передаёт session-cookie, а поле user_id из формы таким доказательством не является.
- PHP создал ID и сохранил language=Русский
- После закрытия выбираем свой ID перед новым start
- Чтение восстанавливает Русский
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Узнать текущий ID или выбрать уже существующую сессию до старта.
| Часть вызова | Что означает |
|---|---|
| id | Без аргумента/null — чтение. Строка задаёт ID до start; возвращается предыдущее значение. Пустая строка сбрасывает выбранный ID. |
| Результат | Строка ID, пустая строка если не выбран, false при отказе изменить. Аргумент nullable с PHP8.0. |
| Границы | У files допустимы a–z A–Z 0–9 , -. Нельзя доверять ID из GET/POST: это ключ чужого состояния. Непустой ID не доказывает ACTIVE. |
| Cookie | Явно заданный ID при последующем старте может снова послать cookie даже с тем же значением. Для нового безопасного ID используйте regenerate_id. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['language'] = 'Русский'; $own = session_id(); session_write_close();
session_id(''); session_id($own);
session_start(); $language = $_SESSION['language']; session_write_close();
echo 'Восстановлен язык: ', $language, "\n";
Ожидаемый результат
Восстановлен язык: Русский
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
По session_id нельзя решать, открыта ли сессия для записи.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); session_write_close();
var_dump(session_id() !== '', session_status() === PHP_SESSION_ACTIVE);
Ожидаемый результат
bool(true)
bool(false)
Дальше: session_status; session_create_id; session_regenerate_id.
session_create_idСоздать случайный кандидат ID, ещё не переключая текущую сессию.
session_create_id(string $prefix = ""): string|falseПодготовить кандидат, сохранив текущую корзину. Строка shop- — необязательный несекретный префикс. Возвращённый кандидат ещё не переключает сессию и не выдаёт cookie. Для обычного входа используется regenerate_id.
- Рабочий ID связан с cart=Кисть:2
- create_id(shop-) возвращает отдельный кандидат
- Рабочая сессия по-прежнему содержит Кисть:2
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Создать случайный кандидат ID, ещё не переключая текущую сессию.
| Часть вызова | Что означает |
|---|---|
| prefix | Необязательный несекретный префикс: a–z A–Z 0–9 , -. Сам префикс не длиннее256символов. ID после присоединения длиннее префикса; учитывайте лимиты своего backend. |
| Результат | Новая строка или false при отказе. Не устанавливает session_id и не отправляет cookie. Вне активной сессии проверка коллизии хранилищем пропускается. |
| Проверка существования | При активной сессии использует обработчик; возможности проверки зависят от его validate_sid. Проверка кандидата не является атомарным резервированием в произвольной БД. |
| Ошибки | Неподходящий символ даёт предупреждение и false; префикс >256 даёт ValueError в PHP8.5.11. Не включайте email, user_id, токен или другие секреты в префикс. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['cart'] = 'Кисть:2';
$candidate = session_create_id('shop-');
if ($candidate === false) { throw new RuntimeException('ID не создан'); }
session_write_close(); session_start(); $cart = $_SESSION['cart']; session_write_close();
echo 'Текущая корзина: ', $cart, "\n";
echo str_starts_with($candidate, 'shop-') ? "Подготовлен отдельный кандидат shop-…; cookie не менялась.\n" : "Неверный префикс\n";
Ожидаемый результат
Текущая корзина: Кисть:2
Подготовлен отдельный кандидат shop-…; cookie не менялась.
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
PHP8.5.11: проверка параметра до создания кандидата. Это не проверка наличия записи в хранилище.
<?php
require __DIR__ . '/bootstrap.php';
try { session_create_id(str_repeat('a', 257)); }
catch (ValueError $error) { echo "Prefix too long
"; }
Ожидаемый результат
Prefix too long
Дальше: session_id; session_regenerate_id.
session_nameЗадать имя cookie, под которым браузер приносит ID.
session_name(?string $name = null): string|falseДать приложению устойчивое имя cookie. Имя одинаково для всех посетителей, значение ID различается. Вызов со строкой меняет настройку текущего запроса и возвращает прежнее имя; общий bootstrap повторяет настройку на каждой странице.
- До настройки имя ManualSession
- session_name(ShopSession) до start
- Cookie приложения будет называться ShopSession
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Задать имя cookie, под которым браузер приносит ID.
| Часть вызова | Что означает |
|---|---|
| name | null/пропуск читает; строка меняет и возвращает старое имя. До start и до вывода. PHP8.0 nullable. |
| Имя | Короткая буквенно-цифровая строка, хотя бы одна буква; не только цифры. Имя одинаково для всех посетителей приложения, ID разный. |
| Область | Действует текущий запрос; повторяйте в общем bootstrap. Разные имена не заменяют раздельные save_path и права доступа. Смена при ACTIVE с PHP7.2 отвергается. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
$before = session_name('ShopSession');
session_start(); $_SESSION['notice'] = 'Заказ принят'; session_write_close();
echo 'Имя было: ', $before, "\nИмя cookie теперь: ", session_name(), "\n";
Ожидаемый результат
Имя было: ManualSession
Имя cookie теперь: ShopSession
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Не добавляйте user_id в имя; браузер различает сессии по непрозрачному значению cookie.
<?php
require __DIR__ . '/bootstrap.php';
session_name('ShopSession'); session_start(); $one = session_name(); session_write_close();
session_start(); $two = session_name(); session_write_close();
var_dump($one === $two); echo $two, "
";
Ожидаемый результат
bool(true)
ShopSession
Дальше: session_id; session_set_cookie_params.
session_save_pathУказать обработчику место хранения данных до старта.
session_save_path(?string $path = null): string|falseНайти файл сохранённой корзины в своём каталоге. Для обработчика files путь выбирает закрытую папку данных. В приложенном bootstrap она создаётся возле примера. Настройка пути сама не создаёт каталог и не делает его недоступным по HTTP.
- Bootstrap выбрал свою папку session-data
- close записал cart=Кисть:2 в sess_ID
- Из собственного файла читаются точные php_serialize-байты
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Указать обработчику место хранения данных до старта.
| Часть вызова | Что означает |
|---|---|
| path | null/пропуск читает; строка устанавливает, возвращается прежняя строка. Nullable с PHP8.0. |
| Для files | Существующий закрытый каталог вне public, доступный сервисному пользователю. Можно N;MODE;/path: вложенность/восьмеричный режим. Каталоги создаются заранее; N>0 отключает файловую автоматическую GC. |
| Для иных handlers | Смысл строки задаёт backend: это не всегда каталог. Эта функция не создаёт директории и не проверяет будущую запись. |
| Отказ | false/предупреждение при недопустимом моменте изменения. Windows ACL и Unix режимы различаются; 0700 в mkdir не заменяет проверку ACL. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['cart'] = 'Кисть:2'; $id = session_id(); session_write_close();
$file = session_save_path() . '/sess_' . $id;
$stored = file_get_contents($file);
if ($stored === false) { throw new RuntimeException('Не прочитан собственный файл'); }
echo 'Каталог: ', basename(session_save_path()), "\nСодержимое: ", $stored, "\n";
Ожидаемый результат
Каталог: session-data
Содержимое: a:1:{s:4:"cart";s:12:"Кисть:2";}
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Ни один пример не использует общий системный каталог для чужих сессий.
<?php
require __DIR__ . '/bootstrap.php';
$other = __DIR__ . '/other-data';
if (!mkdir($other, 0700)) { throw new RuntimeException('Directory failed'); }
$before = session_save_path($other);
session_start(); $id = session_id(); session_write_close();
var_dump($before !== $other, is_file($other . '/sess_' . $id));
Ожидаемый результат
bool(true)
bool(true)
Дальше: session_module_name; session_gc; session_start.
session_module_nameУзнать, какой backend хранит сессии: files или обработчик расширения.
session_module_name(?string $module = null): string|falseУзнать, кто сохраняет выбор между открытиями. Название backend отвечает на вопрос о хранилище. files использует файлы; слово user появляется после регистрации объекта. Название Redis само по себе не устанавливает расширение.
- Bootstrap выбрал files
- start/close сохраняют delivery=Самовывоз
- module_name сообщает files; повторное чтение подтверждает доставку
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Узнать, какой backend хранит сессии: files или обработчик расширения.
| Часть вызова | Что означает |
|---|---|
| module | null/пропуск читает; строка переключает доступный внутренний модуль до старта, возвращая прежний. PHP8.0 nullable. |
| user | Нельзя выбирать вручную; с PHP7.2 это явно запрещено. Пользовательский объект регистрируют session_set_save_handler. В PHP8.5.11 user вызывает ValueError; неизвестный модуль — предупреждение и false. |
| Доступность | Redis/Memcached требуют своего расширения и настройки; название само ничего не устанавливает. files доступен в стандартном session. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['delivery'] = 'Самовывоз'; session_write_close();
$backend = session_module_name();
session_start(); $delivery = $_SESSION['delivery']; session_write_close();
echo 'Хранилище: ', $backend, "\nПрочитанная доставка: ", $delivery, "\n";
Ожидаемый результат
Хранилище: files
Прочитанная доставка: Самовывоз
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Современное поведение PHP8.5.11: нужен реальный объект, реализующий договор хранения.
<?php
require __DIR__ . '/bootstrap.php';
try { session_module_name('user'); }
catch (ValueError $error) { echo "Use session_set_save_handler
"; }
Ожидаемый результат
Use session_set_save_handler
Дальше: session_set_save_handler; session_save_path.
session_get_cookie_paramsПрочитать эффективную политику session-cookie в текущем запросе.
session_get_cookie_params(): arrayУдалять cookie в той же области, где она была создана. Возвращаемый массив — настройки PHP, не подтверждение браузера. Для удаления setcookie нужны те же имя, path и domain. expires — абсолютный момент setcookie, lifetime — длительность настройки session.
- Cookie настроена для /account/ на текущем host
- get_cookie_params читает path/domain
- Удаление должно использовать именно эту область
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Прочитать эффективную политику session-cookie в текущем запросе.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. |
| Ключи | lifetime:int, path/domain/samesite:string, secure/httponly:bool; partitioned:bool с8.5. samesite существует с7.3. |
| Граница | Читает настройки PHP, а не содержимое браузера и не подтверждение, что cookie принята. В массиве нет значения session_id. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
ini_set('session.use_cookies', '1');
session_set_cookie_params(['path' => '/account/', 'domain' => '', 'httponly' => true, 'samesite' => 'Lax']);
$policy = session_get_cookie_params();
echo 'Удаляемое имя: ', session_name(), "\nПуть удаления: ", $policy['path'], "\n";
echo $policy['domain'] === '' ? "Область: текущий host\n" : $policy['domain'];
Ожидаемый результат
Удаляемое имя: ManualSession
Путь удаления: /account/
Область: текущий host
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
expires — абсолютное время setcookie, lifetime — длительность session-policy. Сам HTTP-вызов удаления показан в стенде; для Partitioned-cookie сохраняют также partitioned в PHP8.5.
<?php
require __DIR__ . '/bootstrap.php';
ini_set('session.use_cookies', '1');
session_set_cookie_params(['path' => '/account/', 'domain' => '', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
$policy = session_get_cookie_params();
$remove = ['expires' => 1, 'path' => $policy['path'], 'domain' => $policy['domain'], 'secure' => $policy['secure'], 'httponly' => $policy['httponly'], 'samesite' => $policy['samesite']];
echo $remove['path'], "
"; var_dump($remove['expires'] < time());
Ожидаемый результат
/account/
bool(true)
Дальше: session_set_cookie_params; session_destroy.
session_cache_expireПрочитать или изменить время HTTP-кэша ответа, связанного с сессией.
session_cache_expire(?int $value = null): int|falseПеревести настройку минут в срок HTTP-кэша. Функция задаёт минуты для session cache limiter. Это не idle timeout входа. При nocache хранение ответа запрещено независимо от установленного количества минут.
- Выбрана HTTP-политика private
- cache_expire(12) задаёт12минут =720секунд
- Срок авторизации от этой настройки не меняется
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Прочитать или изменить время HTTP-кэша ответа, связанного с сессией.
| Часть вызова | Что означает |
|---|---|
| value | null/пропуск читает; переданное значение меняет до start и возвращает прежнее. Nullable сPHP8.0. Настройка действует текущий запрос. |
| Результат | int|false, минуты; не секунды и не срок входа. |
| Ограничение | При nocache значения времени не разрешают кэширование. Нельзя этим продлить сессию. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_cache_limiter('private');
session_cache_expire(12);
echo 'Политика: ', session_cache_limiter(), "\n";
echo 'Время HTTP-кэша: ', session_cache_expire(), ' минут = ', session_cache_expire() * 60, " секунд\n";
echo "Срок входа проверяется отдельными timestamps.\n";
Ожидаемый результат
Политика: private
Время HTTP-кэша: 12 минут = 720 секунд
Срок входа проверяется отдельными timestamps.
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Централизованный bootstrap предпочтительнее случайных переопределений из включаемых файлов.
<?php
require __DIR__ . '/bootstrap.php';
session_cache_expire(180);
$original = session_cache_expire(12);
session_cache_expire($original);
echo session_cache_expire(), "\n";
Ожидаемый результат
180
Для кабинета с персональными данными public недопустим; private разрешает хранить ответ самому браузеру. Политика no-store уменьшает сохранение чувствительных ответов, но не отзывает уже выданные данные.
Дальше: session_start; session_get_cookie_params.
session_cache_limiterПрочитать или изменить политику HTTP-кэша ответа, связанного с сессией.
session_cache_limiter(?string $value = null): string|falseНе разрешать сохранять персональный ответ в HTTP-кэше. nocache выбирает набор cache-заголовков при последующем start. Это политика ответа; она не отзывает уже прочитанную человеком страницу и не заменяет проверку user_id.
- Кабинет содержит персональный ответ
- cache_limiter(nocache) до session_start
- HTTP-политика включает no-store; авторизация проверяется отдельно
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Прочитать или изменить политику HTTP-кэша ответа, связанного с сессией.
| Часть вызова | Что означает |
|---|---|
| value | null/пропуск читает; переданное значение меняет до start и возвращает прежнее. Nullable сPHP8.0. Настройка действует текущий запрос. |
| Результат | string|false; определяет заголовки кэширования, а не принятие cookie. |
| Ограничение | nocache: no-store/no-cache/must-revalidate + Pragma + старый Expires; private: private,max-age + старый Expires; private_no_expire: private,max-age без Expires; public: public,max-age + будущий Expires. Для трёх кэширующих режимов есть Last-Modified скрипта. '' отключает только автоматические cache-заголовки, не Set-Cookie. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_cache_limiter('nocache');
echo 'Политика кабинета: ', session_cache_limiter(), "\n";
echo "При HTTP session_start отправит Cache-Control с no-store.\n";
Ожидаемый результат
Политика кабинета: nocache
При HTTP session_start отправит Cache-Control с no-store.
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Централизованный bootstrap предпочтительнее случайных переопределений из включаемых файлов.
<?php
require __DIR__ . '/bootstrap.php';
session_cache_limiter('nocache');
$original = session_cache_limiter('private_no_expire');
session_cache_limiter($original);
echo session_cache_limiter(), "\n";
Ожидаемый результат
nocache
Для кабинета с персональными данными public недопустим; private разрешает хранить ответ самому браузеру. Политика no-store уменьшает сохранение чувствительных ответов, но не отзывает уже выданные данные.
Дальше: session_start; session_get_cookie_params.
session_encodeПолучить строку хранилища из текущего массива сессии.
session_encode(): string|falseУвидеть точную строку, которую сохраняет backend. Сессия уже открыта; encode снимает текущий массив в формате выбранного сериализатора. В bootstrap явно включён php_serialize. Число после s — длина строки в байтах, поэтому русские буквы занимают больше одного байта.
- Открытый массив содержит cart=Кисть:2
- encode с php_serialize формирует снимок
- Строка содержит s:12 для12 UTF-8-байтов Кисть:2
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Получить строку хранилища из текущего массива сессии.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет; сначала start. |
| Результат | Строка сериализованных данных или false. Это не JSON и не обязательно формат serialize: зависит от session.serialize_handler. |
| Форматы | php (по умолчанию), php_binary, php_serialize. Последний поддерживает числовые/специальные ключи и кодирует целый массив. Не переключайте формат существующего хранилища без миграции. |
| Данные | Не храните resource или зависимые от текущего процесса ссылки на переменные. Объект потребует определения класса при восстановлении и может вызвать magic-методы. Предпочитайте простые проверенные значения. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION = ['cart' => 'Кисть:2'];
$snapshot = session_encode(); session_write_close();
if ($snapshot === false) { throw new RuntimeException('Не создан снимок'); }
echo $snapshot, "\n";
Ожидаемый результат
a:1:{s:4:"cart";s:12:"Кисть:2";}
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Полученная строка — снимок на момент вызова; запись в настоящий backend происходит при закрытии.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION = ['cart_qty' => 2]; $before = session_encode();
$_SESSION['cart_qty'] = 3; $after = session_encode(); session_write_close();
var_dump(is_string($before), $before !== $after);
Ожидаемый результат
bool(true)
bool(true)
Дальше: session_decode; session_write_close; session_set_save_handler.
session_decodeЗагрузить доверенную строку сессии в открытый массив $_SESSION.
session_decode(string $data): boolВосстановить свой снимок корзины. Функция получает сериализованные байты и меняет $_SESSION, а возвращает лишь успех. Снимок создаётся здесь же, не поступает из cookie/POST: этот механизм не подходит для недоверенных строк.
- Собственный snapshot хранит Кисть:2
- В памяти Кисть:9 → decode(snapshot)
- Восстановлено Кисть:2; возвращаемый bool сам не является корзиной
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Загрузить доверенную строку сессии в открытый массив $_SESSION.
| Часть вызова | Что означает |
|---|---|
| data | Строка от session_encode/своего хранилища в том же формате serialize_handler. Никогда строка из GET, POST, cookie или внешнего API. |
| Результат | bool: изменяет $_SESSION, а не возвращает разобранный массив. Активная сессия обязательна. |
| Отказ | Повреждённый формат вызывает предупреждение/false и уничтожение текущей сессии; предусмотрите потерю текущего состояния. Это не безопасный общий парсер сериализации. |
| Версионная граница | Сериализация может восстанавливать объекты и вызывать код. Для внешних JSON используйте json_decode с валидацией схемы. |
Для следующего негативного примера set_error_handler временно собирает только ожидаемое предупреждение; finally обязательно возвращает прежний обработчик. Это учебное наблюдение отказа, а не подавление ошибок приложения.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION = ['cart' => 'Кисть:2'];
$snapshot = session_encode();
if ($snapshot === false) { throw new RuntimeException('Нет снимка'); }
$_SESSION['cart'] = 'Кисть:9';
if (!session_decode($snapshot)) { throw new RuntimeException('Снимок не восстановлен'); }
$cart = $_SESSION['cart']; session_write_close();
echo 'Восстановлена корзина: ', $cart, "\n";
Ожидаемый результат
Восстановлена корзина: Кисть:2
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Тест использует заведомо испорченную константу. Подавленный здесь текст предупреждения проверен наличием; в продукте отказ должен попасть в журнал без тела сессии.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['cart_qty'] = 2;
$warnings = [];
set_error_handler(function ($severity, $message) use (&$warnings) { $warnings[] = $message; return true; });
try { $ok = session_decode('broken-payload'); }
finally { restore_error_handler(); }
var_dump($ok, count($warnings) > 0, session_status() === PHP_SESSION_NONE);
Ожидаемый результат
bool(false)
bool(true)
bool(true)
Дальше: session_encode; session_destroy.
session_gcЗапустить удаление устаревших записей хранилища.
session_gc(): int|falseУдалить старую запись, сохранив свежую. В собственном каталоге есть две учебные записи. touch задаёт возраст старой, gc_maxlifetime60 задаёт границу уборки в секундах. Даже не удалённая ещё запись обязана пройти отдельную проверку срока входа.
- old старше3600секунд, fresh только что записан
- gc с maxlifetime60 обрабатывает собственную папку
- old удалён; fresh остаётся
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Запустить удаление устаревших записей хранилища.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. Нужен активный session для инициализации доступа к backend. |
| Результат | Число удалённых записей, в том числе0; false при ошибке. Старый backend может сообщать1 как успешность вместо настоящего количества. |
| Планировщик | Обычно отдельное задание под сервисным пользователем с тем же save_path/handler. gc_probability=0 отключает случайный запуск, но требует реально настроенного расписания. |
| Ограничение | gc_maxlifetime — возраст кандидата в секундах, не жёсткая граница авторизации. Разные приложения в общей папке могут удалить данные по меньшему сроку; N>0 у files требует отдельной уборки. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
ini_set('session.gc_maxlifetime', '60');
$path = session_save_path();
file_put_contents($path . '/sess_old', 'a:0:{}'); touch($path . '/sess_old', time() - 3600);
file_put_contents($path . '/sess_fresh', 'a:0:{}');
session_start(); $removed = session_gc(); session_destroy();
if ($removed === false) { throw new RuntimeException('Уборка не выполнена'); }
echo is_file($path . '/sess_old') ? "Старая запись осталась\n" : "Старая запись удалена\n";
echo is_file($path . '/sess_fresh') ? "Свежая запись сохранена\n" : "Свежая запись потеряна\n";
Ожидаемый результат
Старая запись удалена
Свежая запись сохранена
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Строгое сравнение отличает отсутствие мусора от ошибки backend.
<?php
require __DIR__ . '/bootstrap.php';
ini_set('session.gc_maxlifetime', '3600');
session_start(); $deleted = session_gc(); session_destroy();
var_dump($deleted, $deleted === false);
Ожидаемый результат
int(0)
bool(false)
Дальше: session_start; session_save_path.
session_register_shutdownЗарегистрировать закрытие сессии при штатном завершении скрипта.
session_register_shutdown(): voidСохранить сообщение при штатном завершении скрипта. Вызов регистрирует закрытие в очереди завершения. Наш следующий callback читает файл уже после закрытия. function определяет отложенное действие, use передаёт ему путь; аварийное убийство процесса не гарантирует запуск очереди.
- notice=Заказ принят находится в открытой сессии
- Очередь shutdown сначала вызывает write_close
- Наш следующий callback читает Заказ принят из файла
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Зарегистрировать закрытие сессии при штатном завершении скрипта.
| Часть вызова | Что означает |
|---|---|
| Аргументы/результат | Нет аргументов, нет полезного возвращаемого значения (void). Добавляет session_write_close в очередь shutdown; доступна с PHP5.4. |
| Отказ | При невозможности регистрации выдаёт E_WARNING; в PHP8.5.11 тогда пытается сразу закрыть активную сессию. |
| Когда нужно | При объектном session_set_save_handler($handler, true) регистрация предусмотрена автоматически. Явный write_close предпочтителен для раннего освобождения блокировки и наблюдения ошибок. |
| Предел | Не гарантирует выполнение при убийстве процесса/аварии ОС. Порядок пользовательских shutdown-функций важен, не записывайте в $_SESSION после её фактического закрытия. |
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['notice'] = 'Заказ принят';
$file = session_save_path() . '/sess_' . session_id();
session_register_shutdown();
register_shutdown_function(function () use ($file): void {
$stored = file_get_contents($file);
echo is_string($stored) && str_contains($stored, 'Заказ принят') ? "Следующая страница получит: Заказ принят\n" : "Сообщение не записано\n";
});
Ожидаемый результат
Следующая страница получит: Заказ принят
В архиве bootstrap.php создаёт только свою session-data, выбирает files/php_serialize и отключает cookie для CLI. Повторные start в одном запуске проверяют запись и чтение backend; это не имитация доставки cookie. В браузере тот же путь данных выполняют два HTTP-запроса в полном проекте входа. Полученные строки показывают конкретное сохранённое значение или действие; ID посетителя в вывод не попадает.
Повторное закрытие без активной сессии не создаёт новую запись.
<?php
require __DIR__ . '/bootstrap.php';
session_start(); $_SESSION['cart_qty'] = 4;
session_register_shutdown(); session_write_close();
register_shutdown_function(function () { echo session_status() === PHP_SESSION_NONE ? "Already closed
" : "Still active
"; });
Ожидаемый результат
Already closed
Дальше: session_set_save_handler; session_write_close.
session_set_save_handlerПодключить объект хранения: PHP вызывает его методы в нужные моменты сессии.
session_set_save_handler(object $sessionhandler, bool $register_shutdown = true): boolПодключить хранилище и восстановить сообщение. Регистрация получает объект и true для штатного закрытия при завершении. Она ещё ничего не читает. После неё привычные start/$_SESSION/write_close обращаются к методам объекта. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- Регистрируем LessonStore
- Сохраняем notice=Заказ принят
- Повторный start восстанавливает notice
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Подключить объект хранения: PHP вызывает его методы в нужные моменты сессии.
| Часть вызова | Что означает |
|---|---|
| sessionhandler | Объект SessionHandlerInterface; дополнительно SessionIdInterface и SessionUpdateTimestampHandlerInterface. Регистрируется до старта. |
| register_shutdown | true автоматически организует session_write_close при штатном shutdown. false требует осознанного управления жизненным циклом/явного закрытия. |
| Результат | true/false; при плохом объекте TypeError, при активной сессии предупреждение/false. Это регистрация, не start и не запись. |
| Старая форма | open, close, read, write, destroy, gc + необязательные create_sid, validate_sid, update_timestamp. Более2аргументов deprecated с PHP8.4; современный путь — объект. |
| Шесть основных callbacks | open(path,name):bool; close():bool; read(id):string|false; write(id,data):bool; destroy(id):bool; gc(max_lifetime):int|false. Старый процедурный gc мог возвращать bool; современный договор считает удалённые записи. |
| Три дополнительных | create_sid():string; validate_sid(id):bool проверяет существование для strict mode; update_timestamp(id,data):bool обновляет срок неизменившейся записи при lazy_write. |
| Надёжность | Хранилище возвращает точные байты write при read, сохраняет блокировку на весь read–modify–write, проверяет ID перед путями/SQL. Регистрация callback сама этого не добавляет. |
| Завершение | Закрывайте явно до вывода/долгой работы: иначе write/close попадут в shutdown, где зависимости могут уже разрушаться, CWD отличаться, а ошибка — уйти мимо обычного catch. Используйте абсолютные пути; не echo из backend. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['notice'] = 'Заказ принят'; session_write_close();
session_start(); $notice = $_SESSION['notice']; session_write_close();
echo 'Сообщение: ', $notice, "
";
echo 'Обработчик: ', session_module_name(), "
";
Ожидаемый результат
Сообщение: Заказ принят
Обработчик: user
Второе открытие использует тот же объект только в пределах этого процесса. Параллельная запись, срок и межпроцессное хранение должны быть реализованы постоянным backend.
Временный обработчик предупреждений только фиксирует ожидаемый отказ теста. В приложении сбой БД нельзя превращать в пустую успешную авторизацию. Пустая строка и false имеют разный смысл.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
$store->failRead = true;
$warnings = [];
set_error_handler(function ($severity, $message) use (&$warnings) { $warnings[] = $message; return true; });
try { $started = session_start(); }
finally { restore_error_handler(); }
var_dump($started, count($warnings) > 0, session_status() === PHP_SESSION_NONE);
Ожидаемый результат
bool(false)
bool(true)
bool(true)
Дальше: SessionHandlerInterface::read; SessionUpdateTimestampHandlerInterface::validateId; session_register_shutdown.
SessionHandlerInterface::openПодготовить backend к чтению; PHP первым вызывает open при старте.
public open(string $path, string $name): boolПодготовка собственной корзины. PHP вызывает open(path, name) перед чтением. path задаёт место хранилища, name — имя cookie, а ID приходит отдельным аргументом в read. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- session_start → open/read для текущего ID
- В памяти cart=Кисть:2; open выполняет свой этап
- Человек получает восстановленную корзину либо подтверждённое изменение хранилища
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Подготовить backend к чтению; PHP первым вызывает open при старте.
| Часть вызова | Что означает |
|---|---|
| path | Значение session.save_path; его смысл определяет backend. |
| name | Имя сессии, например ManualSession; не ID посетителя. |
| Результат | true готово, false отказ и start неуспешен. Не путать с конструктором объекта: open вызывается при каждом открытии. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
$first = $store->events[0];
session_start(); $cart = $_SESSION['cart']; session_write_close();
echo 'Первый callback: ', $first, "\nПрочитанная корзина: ", $cart, "\n";
Ожидаемый результат
Первый callback: open
Прочитанная корзина: Кисть:2
Входные записи заданы полностью. Формат php_serialize настраивается в bootstrap.php. Ни ID, ни содержимое настоящих сессий не используются.
После false от open PHP не продолжает обычное чтение. Возврат true без готового соединения скрыл бы проблему.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
$store->failOpen = true;
$warnings = [];
set_error_handler(function ($severity, $message) use (&$warnings) { $warnings[] = $message; return true; });
try { $ok = session_start(); } finally { restore_error_handler(); }
var_dump($ok, in_array('read', $store->events, true));
Ожидаемый результат
bool(false)
bool(false)
Дальше: session_set_save_handler; session_start; session_write_close.
SessionHandlerInterface::closeОсвободить ресурсы и блокировку, когда PHP завершает сессию.
public close(): boolОсвобождение собственной корзины. close освобождает ресурсы даже после abort без записи. В этом успешном проходе PHP сначала пишет данные, затем закрывает backend. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- session_start → open/read для текущего ID
- В памяти cart=Кисть:2; close выполняет свой этап
- Человек получает восстановленную корзину либо подтверждённое изменение хранилища
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Освободить ресурсы и блокировку, когда PHP завершает сессию.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. Состояние соединения хранится в объекте. |
| Результат | bool сообщает PHP успех освобождения. Обычно после write/updateTimestamp, но abort/read_and_close тоже закрывают без записи. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
echo 'Последние callbacks: ', implode(' → ', array_slice($store->events, -2)), "\n";
session_start(); $cart = $_SESSION['cart']; session_write_close();
echo 'После нового открытия: ', $cart, "\n";
Ожидаемый результат
Последние callbacks: write → close
После нового открытия: Кисть:2
Входные записи заданы полностью. Формат php_serialize настраивается в bootstrap.php. Ни ID, ни содержимое настоящих сессий не используются.
Нельзя делать close зависимым от того, что write обязательно был вызван.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 9; session_abort();
var_dump($store->closed, in_array('write', $store->events, true));
Ожидаемый результат
bool(true)
bool(false)
Дальше: session_set_save_handler; session_start; session_write_close.
SessionHandlerInterface::readНайти байты сессии по ID; PHP сам преобразует их в $_SESSION.
public read(string $id): string|falseЧтение собственной корзины. read(id) возвращает точные байты, ранее полученные write. Сам PHP разбирает их в $_SESSION. Пустая строка означает отсутствие данных, false — сбой чтения. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- session_start → open/read для текущего ID
- В памяти cart=Кисть:2; read выполняет свой этап
- Человек получает восстановленную корзину либо подтверждённое изменение хранилища
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Найти байты сессии по ID; PHP сам преобразует их в $_SESSION.
| Часть вызова | Что означает |
|---|---|
| id | Непрозрачный ключ посетителя. Для backend с файловыми путями его нельзя напрямую дописывать без строгой проверки; для SQL нужен параметр запроса. |
| Результат | Точная ранее записанная строка; '' если данных нет; false только отказ. null не подходит. Восстанавливает сам PHP по serialize_handler. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart']; session_write_close();
echo 'Восстановлена корзина: ', $cart, "\n";
Ожидаемый результат
Восстановлена корзина: Кисть:2
Входные записи заданы полностью. Формат php_serialize настраивается в bootstrap.php. Ни ID, ни содержимое настоящих сессий не используются.
Отсутствие записи — ожидаемый новый посетитель, а не ошибка чтения.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $empty = $_SESSION === []; session_write_close();
var_dump($empty, in_array('read', $store->events, true));
Ожидаемый результат
bool(true)
bool(true)
Дальше: session_set_save_handler; session_start; session_write_close.
SessionHandlerInterface::writeСохранить точную строку PHP под переданным идентификатором.
public write(string $id, string $data): boolЗапись собственной корзины. write получает ID и уже сериализованную строку только при сохранении сессии. Повторная сериализация или htmlspecialchars повредили бы формат backend. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- session_start → open/read для текущего ID
- В памяти cart=Кисть:2; write выполняет свой этап
- Человек получает восстановленную корзину либо подтверждённое изменение хранилища
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Сохранить точную строку PHP под переданным идентификатором.
| Часть вызова | Что означает |
|---|---|
| id | Ключ записи; его назначает/приносит механизм сессий. |
| data | Уже сериализованные байты $_SESSION: не сериализуйте повторно и не делайте HTML-экранирование. |
| Результат | true только после успешного сохранения; false сообщает ошибку PHP. write вызывается при закрытии, а не при каждом присваивании. lazy_write с timestamp-handler может его заменить. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; $id = session_id(); session_write_close();
$bytes = $store->rows[$id]['data'];
echo 'Backend получил: ', $bytes, "\n";
session_start(); $cart = $_SESSION['cart']; session_write_close();
echo 'PHP восстановил: ', $cart, "\n";
Ожидаемый результат
Backend получил: a:1:{s:4:"cart";s:12:"Кисть:2";}
PHP восстановил: Кисть:2
Входные записи заданы полностью. Формат php_serialize настраивается в bootstrap.php. Ни ID, ни содержимое настоящих сессий не используются.
Наблюдение PHP8.5.11: внутренний false вызвал предупреждение, хотя внешнее закрытие вернуло true. Для значимых записей нужны ошибки/мониторинг backend и отдельная транзакция бизнеса.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; $id = session_id(); $store->failWrite = true;
$warnings = [];
set_error_handler(function ($severity, $message) use (&$warnings) { $warnings[] = $message; return true; });
try { $closed = session_write_close(); } finally { restore_error_handler(); }
var_dump($closed, count($warnings) > 0, isset($store->rows[$id]));
Ожидаемый результат
bool(true)
bool(true)
bool(false)
Дальше: session_set_save_handler; session_start; session_write_close.
SessionHandlerInterface::destroyУдалить одну запись; не путать с очищением памяти и cookie.
public destroy(string $id): boolУдаление одной записи собственной корзины. destroy(id) касается конкретной сессии. Здесь сохраняем соседнюю запись, удаляем текущую и проверяем, что чужая корзина осталась. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- session_start → open/read для текущего ID
- В памяти cart=Кисть:2; destroy выполняет свой этап
- Человек получает восстановленную корзину либо подтверждённое изменение хранилища
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Удалить одну запись; не путать с очищением памяти и cookie.
| Часть вызова | Что означает |
|---|---|
| id | Только ID текущей удаляемой записи. Вызывается destroy, regenerate_id(true), а также при провале декодирования. |
| Результат | bool успеха удаления. Удаление отсутствующей записи допустимо сделать идемпотентным: true означает её отсутствие, а не обязательное существование до вызова. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
$store->rows['neighbour'] = ['data' => 'a:1:{s:4:"cart";s:7:"Brush:5";}', 'updated' => 1000];
session_start(); $_SESSION['cart'] = 'Кисть:2'; $id = session_id(); session_write_close();
session_start(); session_destroy();
echo isset($store->rows[$id]) ? "Текущая запись осталась\n" : "Текущая запись удалена\n";
echo 'Соседняя запись: ', $store->rows['neighbour']['data'], "\n";
Ожидаемый результат
Текущая запись удалена
Соседняя запись: a:1:{s:4:"cart";s:7:"Brush:5";}
Входные записи заданы полностью. Формат php_serialize настраивается в bootstrap.php. Ни ID, ни содержимое настоящих сессий не используются.
Это изолированная механика метода. В concurrent-web выборе true предшествует проектирование ротации, а не копирование этого теста.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; session_write_close();
session_start(); $old = session_id(); session_regenerate_id(true); session_write_close();
var_dump(in_array('destroy', $store->events, true), isset($store->rows[$old]));
Ожидаемый результат
bool(true)
bool(false)
Дальше: session_set_save_handler; session_start; session_write_close.
SessionHandlerInterface::gcУдалить записи старше заданного срока и вернуть число удалений.
public gc(int $max_lifetime): int|falseУборка по возрасту собственной корзины. gc(max_lifetime) получает возраст в секундах. Учебные часы now=1000 позволяют без ожидания сравнить запись с updated100 и запись с updated990. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- session_start → open/read для текущего ID
- В памяти cart=Кисть:2; gc выполняет свой этап
- Человек получает восстановленную корзину либо подтверждённое изменение хранилища
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Удалить записи старше заданного срока и вернуть число удалений.
| Часть вызова | Что означает |
|---|---|
| max_lifetime | Возраст в секундах из session.gc_maxlifetime. Сравнивайте с временем последнего обновления/продлеваемым TTL своего хранилища. |
| Результат | Количество (0 — успех без удалений) или false. До PHP7.1 договор был bool. |
| Цена | Поиск по индексу updated_at/TTL дешевле полного сканирования большого хранилища; измеряйте на своём backend. Не делайте массовую уборку в каждом запросе пользователя. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
$store->rows['old'] = ['data' => 'a:0:{}', 'updated' => 100];
$store->rows['fresh'] = ['data' => 'a:0:{}', 'updated' => 990];
ini_set('session.gc_maxlifetime', '60');
session_start(); session_gc(); session_destroy();
echo 'Остались записи: ', implode(', ', array_keys($store->rows)), "\n";
Ожидаемый результат
Остались записи: fresh
Входные записи заданы полностью. Формат php_serialize настраивается в bootstrap.php. Ни ID, ни содержимое настоящих сессий не используются.
Если backend использует собственное TTL-удаление, gc должен явно определять своё поведение, а не обещать точный подсчёт чужих операций.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $count = session_gc(); session_abort(); var_dump($count, $count === false);
Ожидаемый результат
int(0)
bool(false)
Дальше: session_set_save_handler; session_start; session_write_close.
SessionIdInterface::create_sidДать своему backend криптографически случайный новый ID.
public create_sid(): stringСоздать ID при первом старте. PHP использует дополнительный интерфейс, когда нужен новый ключ. Наш create_sid получает 16 случайных байтов, делает hex и проверяет собственные rows. Это ещё не атомарное резервирование в распределённой БД. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- Bootstrap задаёт строгий режим и php_serialize
- PHP вызывает SessionIdInterface::create_sid в соответствующий момент
- Вывод отражает доступ к корзине или время её хранения
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Дать своему backend криптографически случайный новый ID.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. PHP вызывает, когда ему нужен новый идентификатор. |
| Результат | Непустая допустимая для backend строка; это не изменение $_SESSION и не отправка cookie. PHP5.5.1+. |
| Требования | Непредсказуемость, достаточная энтропия, проверка/атомарность уникальности. Не time(), счётчик и не пароль. Нельзя рекурсивно вызывать собственный create_sid. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; $id = session_id(); session_write_close();
echo in_array('create_sid', $store->events, true) ? "PHP запросил новый ключ у create_sid.\n" : "Генератор не вызван.\n";
session_start(); $cart = $_SESSION['cart']; session_write_close();
echo 'Корзина под этим ключом: ', $cart, "\n";
Ожидаемый результат
PHP запросил новый ключ у create_sid.
Корзина под этим ключом: Кисть:2
Все ветви LessonStore.php видны в архиве. Таймеры и ID здесь служат изолированной демонстрации engine-контракта, не внешнему входу.
При возвращении посетителя strict mode проверяет существование и повторно использует ID.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; session_write_close();
$store->events = []; session_start(); session_write_close();
var_dump(in_array('create_sid', $store->events, true), in_array('validateId', $store->events, true));
Ожидаемый результат
bool(false)
bool(true)
Дальше: session_create_id; SessionUpdateTimestampHandlerInterface::validateId.
SessionUpdateTimestampHandlerInterface::validateIdПодтвердить, что принесённый ID уже существует у backend.
public validateId(string $id): boolОтклонить неизвестный ключ при strict mode. validateId отвечает на вопрос, существует ли запись. Проверка допустимых символов — другая задача. Переданный ниже ключ deliberatelymissing не зарегистрирован: PHP должен создать новое пустое состояние. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- Bootstrap задаёт строгий режим и php_serialize
- PHP вызывает SessionUpdateTimestampHandlerInterface::validateId в соответствующий момент
- Вывод отражает доступ к корзине или время её хранения
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Подтвердить, что принесённый ID уже существует у backend.
| Часть вызова | Что означает |
|---|---|
| id | Кандидат, полученный PHP для старта; strict mode вызывает проверку до чтения. |
| Результат | true только для существующей допустимой записи; false приводит к новому ID. Правильные символы сами по себе недостаточны. |
| Граница | Не проверяет пароль/права; существующую чужую сессию метод не отличает по личности. Стандартный SessionHandler этого интерфейса не реализует. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_id('deliberatelymissing'); session_start();
$message = isset($_SESSION['user_id']) ? 'Вход восстановлен' : 'Посетитель остаётся гостем';
session_write_close();
echo $message, "\n";
echo in_array('validateId', $store->events, true) ? "Проверено существование переданного ID.\n" : "Проверка ID не вызвана.\n";
Ожидаемый результат
Посетитель остаётся гостем
Проверено существование переданного ID.
Все ветви LessonStore.php видны в архиве. Таймеры и ID здесь служат изолированной демонстрации engine-контракта, не внешнему входу.
Для файлового/SQL backend существование и срок проверяются безопасным ключом; HTTP-пароль проверяется другим слоем.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; $id = session_id(); session_write_close();
session_start(); $same = session_id() === $id; $qty = $_SESSION['cart_qty']; session_write_close();
var_dump($same); echo $qty, "\n";
Ожидаемый результат
bool(true)
2
Дальше: session_start; SessionIdInterface::create_sid.
SessionUpdateTimestampHandlerInterface::updateTimestampПродлить срок неизменившейся записи без переписывания всего payload.
public updateTimestamp(string $id, string $data): boolПродлить неизменившуюся запись без повторной сериализованной записи. При lazy_write=1 неизменившиеся данные могут пройти updateTimestamp вместо write. Обновляется время backend, а содержимое корзины остаётся тем же; срок авторизации всё равно проверяется отдельно. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.
- Bootstrap задаёт строгий режим и php_serialize
- PHP вызывает SessionUpdateTimestampHandlerInterface::updateTimestamp в соответствующий момент
- Вывод отражает доступ к корзине или время её хранения
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Продлить срок неизменившейся записи без переписывания всего payload.
| Часть вызова | Что означает |
|---|---|
| id/data | ID и сериализованные данные. PHP при lazy_write=1 может вызвать этот метод вместо write, если прочитанные байты не изменились. |
| Результат | true когда timestamp/TTL обновлён, false при отказе. Не применяйте read_and_close, если ожидаете этот шаг: тот путь закрывает без сохранения. |
| Граница | Убедитесь, что запись существует и формат/TTL согласованы. Обновление timestamp — не продление максимального абсолютного срока входа. При lazy_write=0 вызовется write. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
Приложенный LessonStore.php полностью показывает реализацию. Это хранилище в массиве одного процесса: оно намеренно не переживает новый запрос и не имеет межпроцессной блокировки. Его задача — увидеть callbacks и проверить контракт; для сайта выбирайте поддерживаемый backend с атомарностью/TTL/блокировкой или реализуйте эти свойства явно. Нельзя перенести этот массив в production и считать сессии общими между PHP-процессами.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
ini_set('session.lazy_write', '1');
session_start(); $_SESSION['cart'] = 'Кисть:2'; $id = session_id(); session_write_close();
$before = $store->rows[$id]['updated']; $store->now = 1100;
session_start(); $cart = $_SESSION['cart']; session_write_close();
echo 'Корзина: ', $cart, "\nВремя backend: ", $before, ' → ', $store->rows[$id]['updated'], "\n";
Ожидаемый результат
Корзина: Кисть:2
Время backend: 1000 → 1100
Все ветви LessonStore.php видны в архиве. Таймеры и ID здесь служат изолированной демонстрации engine-контракта, не внешнему входу.
Здесь экономия записи меняет политику продления. Нельзя рекомендовать read_and_close без учёта срока хранения.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/LessonStore.php';
$store = new LessonStore();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; $id = session_id(); session_write_close();
$store->now = 1005; $store->events = []; session_start(['read_and_close' => true]);
echo $store->rows[$id]['updated'], "\n"; var_dump(in_array('updateTimestamp', $store->events, true));
Ожидаемый результат
1000
bool(false)
Дальше: session_start; SessionHandlerInterface::write.
SessionHandler::openОбёртка штатного backend: Подготовить backend к чтению; PHP первым вызывает open при старте.
public open(string $path, string $name): boolНаблюдать open у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::open передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. PHP вызывает open(path, name) перед чтением. path задаёт место хранилища, name — имя cookie, а ID приходит отдельным аргументом в read.
- TraceFiles подключён через session_set_save_handler
- PHP сохраняет cart=Кисть:2 в собственном каталоге
- Повторный start читает Кисть:2; журнал подтверждает этап open
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Обёртка штатного backend: Подготовить backend к чтению; PHP первым вызывает open при старте.
| Часть вызова | Что означает |
|---|---|
| path | Значение session.save_path; его смысл определяет backend. |
| name | Имя сессии, например ManualSession; не ID посетителя. |
| Результат | true готово, false отказ и start неуспешен. Не путать с конструктором объекта: open вызывается при каждом открытии. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
extends SessionHandler наследует обёртку текущего внутреннего backend; parent::open(...) передаёт работу ему. TraceFiles.php перехватывает вызовы для журнала имён операций. Методы предназначены для PHP lifecycle: вручную вместо start/close их вызывать нельзя; вне активного контекста они могут выдавать Error/отказ. Если создаёте новый backend, реализуйте интерфейс, а не наследуйте эту обёртку.
Это учебная обёртка files. SessionHandler не реализует validateId/updateTimestamp: не обещайте сохранение strict-mode-проверки при такой регистрации. Для production-обёртки спроектируйте эти методы вместе с проверкой существования backend; простой isset($_SESSION) их не заменяет.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart'];
session_write_close();
echo 'Восстановленная корзина: ', $cart, "\n";
echo in_array('open', $store->events, true) ? "Files callback: open\n" : "Callback не наблюдался\n";
Ожидаемый результат
Восстановленная корзина: Кисть:2
Files callback: open
В архиве полностью определён TraceFiles.php. Журнал содержит только названия этапов. Для gc здесь наблюдаем вызов, а настоящая уборка старых записей показана в карточке session_gc. Для destroy отдельно проверяется удаление одной записи в следующем опыте.
open не является одноразовым конструктором объекта.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; session_write_close();
session_start(); $qty = $_SESSION['cart_qty']; session_write_close();
echo count(array_filter($store->events, fn($event) => $event === 'open')), "\n", $qty, "\n";
Ожидаемый результат
2
2
Дальше: session_set_save_handler; SessionHandlerInterface::open.
SessionHandler::readОбёртка штатного backend: Найти байты сессии по ID; PHP сам преобразует их в $_SESSION.
public read(string $id): string|falseНаблюдать read у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::read передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. read(id) возвращает точные байты, ранее полученные write. Сам PHP разбирает их в $_SESSION. Пустая строка означает отсутствие данных, false — сбой чтения.
- TraceFiles подключён через session_set_save_handler
- PHP сохраняет cart=Кисть:2 в собственном каталоге
- Повторный start читает Кисть:2; журнал подтверждает этап read
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Обёртка штатного backend: Найти байты сессии по ID; PHP сам преобразует их в $_SESSION.
| Часть вызова | Что означает |
|---|---|
| id | Непрозрачный ключ посетителя. Для backend с файловыми путями его нельзя напрямую дописывать без строгой проверки; для SQL нужен параметр запроса. |
| Результат | Точная ранее записанная строка; '' если данных нет; false только отказ. null не подходит. Восстанавливает сам PHP по serialize_handler. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
extends SessionHandler наследует обёртку текущего внутреннего backend; parent::read(...) передаёт работу ему. TraceFiles.php перехватывает вызовы для журнала имён операций. Методы предназначены для PHP lifecycle: вручную вместо start/close их вызывать нельзя; вне активного контекста они могут выдавать Error/отказ. Если создаёте новый backend, реализуйте интерфейс, а не наследуйте эту обёртку.
Это учебная обёртка files. SessionHandler не реализует validateId/updateTimestamp: не обещайте сохранение strict-mode-проверки при такой регистрации. Для production-обёртки спроектируйте эти методы вместе с проверкой существования backend; простой isset($_SESSION) их не заменяет.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart'];
session_write_close();
echo 'Восстановленная корзина: ', $cart, "\n";
echo in_array('read', $store->events, true) ? "Files callback: read\n" : "Callback не наблюдался\n";
Ожидаемый результат
Восстановленная корзина: Кисть:2
Files callback: read
В архиве полностью определён TraceFiles.php. Журнал содержит только названия этапов. Для gc здесь наблюдаем вызов, а настоящая уборка старых записей показана в карточке session_gc. Для destroy отдельно проверяется удаление одной записи в следующем опыте.
Новая запись files существует даже без пользовательских значений.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $empty = $_SESSION === []; session_abort(); var_dump($empty, in_array('read', $store->events, true));
Ожидаемый результат
bool(true)
bool(true)
Дальше: session_set_save_handler; SessionHandlerInterface::read.
SessionHandler::writeОбёртка штатного backend: Сохранить точную строку PHP под переданным идентификатором.
public write(string $id, string $data): boolНаблюдать write у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::write передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. write получает ID и уже сериализованную строку только при сохранении сессии. Повторная сериализация или htmlspecialchars повредили бы формат backend.
- TraceFiles подключён через session_set_save_handler
- PHP сохраняет cart=Кисть:2 в собственном каталоге
- Повторный start читает Кисть:2; журнал подтверждает этап write
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Обёртка штатного backend: Сохранить точную строку PHP под переданным идентификатором.
| Часть вызова | Что означает |
|---|---|
| id | Ключ записи; его назначает/приносит механизм сессий. |
| data | Уже сериализованные байты $_SESSION: не сериализуйте повторно и не делайте HTML-экранирование. |
| Результат | true только после успешного сохранения; false сообщает ошибку PHP. write вызывается при закрытии, а не при каждом присваивании. lazy_write с timestamp-handler может его заменить. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
extends SessionHandler наследует обёртку текущего внутреннего backend; parent::write(...) передаёт работу ему. TraceFiles.php перехватывает вызовы для журнала имён операций. Методы предназначены для PHP lifecycle: вручную вместо start/close их вызывать нельзя; вне активного контекста они могут выдавать Error/отказ. Если создаёте новый backend, реализуйте интерфейс, а не наследуйте эту обёртку.
Это учебная обёртка files. SessionHandler не реализует validateId/updateTimestamp: не обещайте сохранение strict-mode-проверки при такой регистрации. Для production-обёртки спроектируйте эти методы вместе с проверкой существования backend; простой isset($_SESSION) их не заменяет.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart'];
session_write_close();
echo 'Восстановленная корзина: ', $cart, "\n";
echo in_array('write', $store->events, true) ? "Files callback: write\n" : "Callback не наблюдался\n";
Ожидаемый результат
Восстановленная корзина: Кисть:2
Files callback: write
В архиве полностью определён TraceFiles.php. Журнал содержит только названия этапов. Для gc здесь наблюдаем вызов, а настоящая уборка старых записей показана в карточке session_gc. Для destroy отдельно проверяется удаление одной записи в следующем опыте.
Изменение локального массива не вызывает callback автоматически.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 2; session_write_close();
$before = count($store->events); $_SESSION['cart_qty'] = 9; session_write_close();
var_dump(count($store->events) === $before);
Ожидаемый результат
bool(true)
Дальше: session_set_save_handler; SessionHandlerInterface::write.
SessionHandler::closeОбёртка штатного backend: Освободить ресурсы и блокировку, когда PHP завершает сессию.
public close(): boolНаблюдать close у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::close передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. close освобождает ресурсы даже после abort без записи. В этом успешном проходе PHP сначала пишет данные, затем закрывает backend.
- TraceFiles подключён через session_set_save_handler
- PHP сохраняет cart=Кисть:2 в собственном каталоге
- Повторный start читает Кисть:2; журнал подтверждает этап close
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Обёртка штатного backend: Освободить ресурсы и блокировку, когда PHP завершает сессию.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. Состояние соединения хранится в объекте. |
| Результат | bool сообщает PHP успех освобождения. Обычно после write/updateTimestamp, но abort/read_and_close тоже закрывают без записи. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
extends SessionHandler наследует обёртку текущего внутреннего backend; parent::close(...) передаёт работу ему. TraceFiles.php перехватывает вызовы для журнала имён операций. Методы предназначены для PHP lifecycle: вручную вместо start/close их вызывать нельзя; вне активного контекста они могут выдавать Error/отказ. Если создаёте новый backend, реализуйте интерфейс, а не наследуйте эту обёртку.
Это учебная обёртка files. SessionHandler не реализует validateId/updateTimestamp: не обещайте сохранение strict-mode-проверки при такой регистрации. Для production-обёртки спроектируйте эти методы вместе с проверкой существования backend; простой isset($_SESSION) их не заменяет.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart'];
session_write_close();
echo 'Восстановленная корзина: ', $cart, "\n";
echo in_array('close', $store->events, true) ? "Files callback: close\n" : "Callback не наблюдался\n";
Ожидаемый результат
Восстановленная корзина: Кисть:2
Files callback: close
В архиве полностью определён TraceFiles.php. Журнал содержит только названия этапов. Для gc здесь наблюдаем вызов, а настоящая уборка старых записей показана в карточке session_gc. Для destroy отдельно проверяется удаление одной записи в следующем опыте.
close должен быть корректен и после чтения без write.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart_qty'] = 9; session_abort();
echo end($store->events), "\n"; var_dump(in_array('write', $store->events, true));
Ожидаемый результат
close
bool(false)
Дальше: session_set_save_handler; SessionHandlerInterface::close.
SessionHandler::destroyОбёртка штатного backend: Удалить одну запись; не путать с очищением памяти и cookie.
public destroy(string $id): boolНаблюдать destroy у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::destroy передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. destroy(id) касается конкретной сессии. Здесь сохраняем соседнюю запись, удаляем текущую и проверяем, что чужая корзина осталась.
- TraceFiles подключён через session_set_save_handler
- PHP сохраняет cart=Кисть:2 в собственном каталоге
- Повторный start читает Кисть:2; журнал подтверждает этап destroy
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Обёртка штатного backend: Удалить одну запись; не путать с очищением памяти и cookie.
| Часть вызова | Что означает |
|---|---|
| id | Только ID текущей удаляемой записи. Вызывается destroy, regenerate_id(true), а также при провале декодирования. |
| Результат | bool успеха удаления. Удаление отсутствующей записи допустимо сделать идемпотентным: true означает её отсутствие, а не обязательное существование до вызова. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
extends SessionHandler наследует обёртку текущего внутреннего backend; parent::destroy(...) передаёт работу ему. TraceFiles.php перехватывает вызовы для журнала имён операций. Методы предназначены для PHP lifecycle: вручную вместо start/close их вызывать нельзя; вне активного контекста они могут выдавать Error/отказ. Если создаёте новый backend, реализуйте интерфейс, а не наследуйте эту обёртку.
Это учебная обёртка files. SessionHandler не реализует validateId/updateTimestamp: не обещайте сохранение strict-mode-проверки при такой регистрации. Для production-обёртки спроектируйте эти методы вместе с проверкой существования backend; простой isset($_SESSION) их не заменяет.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart'];
session_destroy();
echo 'Восстановленная корзина: ', $cart, "\n";
echo in_array('destroy', $store->events, true) ? "Files callback: destroy\n" : "Callback не наблюдался\n";
Ожидаемый результат
Восстановленная корзина: Кисть:2
Files callback: destroy
В архиве полностью определён TraceFiles.php. Журнал содержит только названия этапов. Для gc здесь наблюдаем вызов, а настоящая уборка старых записей показана в карточке session_gc. Для destroy отдельно проверяется удаление одной записи в следующем опыте.
Обёртка не меняет общую семантику session_destroy.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['language'] = 'ru'; session_destroy(); echo $_SESSION['language'], "\n";
Ожидаемый результат
ru
Дальше: session_set_save_handler; SessionHandlerInterface::destroy.
SessionHandler::gcОбёртка штатного backend: Удалить записи старше заданного срока и вернуть число удалений.
public gc(int $max_lifetime): int|falseНаблюдать gc у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::gc передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. gc(max_lifetime) получает возраст в секундах. Учебные часы now=1000 позволяют без ожидания сравнить запись с updated100 и запись с updated990.
- TraceFiles подключён через session_set_save_handler
- PHP сохраняет cart=Кисть:2 в собственном каталоге
- Повторный start читает Кисть:2; журнал подтверждает этап gc
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Обёртка штатного backend: Удалить записи старше заданного срока и вернуть число удалений.
| Часть вызова | Что означает |
|---|---|
| max_lifetime | Возраст в секундах из session.gc_maxlifetime. Сравнивайте с временем последнего обновления/продлеваемым TTL своего хранилища. |
| Результат | Количество (0 — успех без удалений) или false. До PHP7.1 договор был bool. |
| Цена | Поиск по индексу updated_at/TTL дешевле полного сканирования большого хранилища; измеряйте на своём backend. Не делайте массовую уборку в каждом запросе пользователя. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
extends SessionHandler наследует обёртку текущего внутреннего backend; parent::gc(...) передаёт работу ему. TraceFiles.php перехватывает вызовы для журнала имён операций. Методы предназначены для PHP lifecycle: вручную вместо start/close их вызывать нельзя; вне активного контекста они могут выдавать Error/отказ. Если создаёте новый backend, реализуйте интерфейс, а не наследуйте эту обёртку.
Это учебная обёртка files. SessionHandler не реализует validateId/updateTimestamp: не обещайте сохранение strict-mode-проверки при такой регистрации. Для production-обёртки спроектируйте эти методы вместе с проверкой существования backend; простой isset($_SESSION) их не заменяет.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart'];
session_gc();
session_write_close();
echo 'Восстановленная корзина: ', $cart, "\n";
echo in_array('gc', $store->events, true) ? "Files callback: gc\n" : "Callback не наблюдался\n";
Ожидаемый результат
Восстановленная корзина: Кисть:2
Files callback: gc
В архиве полностью определён TraceFiles.php. Журнал содержит только названия этапов. Для gc здесь наблюдаем вызов, а настоящая уборка старых записей показана в карточке session_gc. Для destroy отдельно проверяется удаление одной записи в следующем опыте.
Ноль — нормальный результат, не ошибка.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $count = session_gc(); session_abort(); var_dump($count);
Ожидаемый результат
int(0)
Дальше: session_set_save_handler; SessionHandlerInterface::gc.
SessionHandler::create_sidДелегировать создание нового ID текущему внутреннему обработчику.
public create_sid(): stringНаблюдать create_sid у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::create_sid передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. Генератор создаёт ID, когда PHP открывает новое состояние.
- TraceFiles подключён через session_set_save_handler
- PHP сохраняет cart=Кисть:2 в собственном каталоге
- Повторный start читает Кисть:2; журнал подтверждает этап create_sid
Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.
require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.
Делегировать создание нового ID текущему внутреннему обработчику.
| Часть вызова | Что означает |
|---|---|
| Аргументы | Нет. Вызывается PHP во время session lifecycle. |
| Результат | Допустимая строка ID от родного backend. Доступен с PHP5.5.1. |
У этих внутренних методов в PHP8.x предусмотрены tentative return types: начиная с8.1 старые реализации без совместимого возвращаемого типа могут выдавать deprecation. Новый код ниже объявляет типы явно. Атрибут ReturnTypeWillChange временно помогает миграции старого кода, но не меняет реальный договор результата.
extends SessionHandler наследует обёртку текущего внутреннего backend; parent::create_sid(...) передаёт работу ему. TraceFiles.php перехватывает вызовы для журнала имён операций. Методы предназначены для PHP lifecycle: вручную вместо start/close их вызывать нельзя; вне активного контекста они могут выдавать Error/отказ. Если создаёте новый backend, реализуйте интерфейс, а не наследуйте эту обёртку.
Это учебная обёртка files. SessionHandler не реализует validateId/updateTimestamp: не обещайте сохранение strict-mode-проверки при такой регистрации. Для production-обёртки спроектируйте эти методы вместе с проверкой существования backend; простой isset($_SESSION) их не заменяет.
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $_SESSION['cart'] = 'Кисть:2'; session_write_close();
session_start(); $cart = $_SESSION['cart'];
session_write_close();
echo 'Восстановленная корзина: ', $cart, "\n";
echo in_array('create_sid', $store->events, true) ? "Files callback: create_sid\n" : "Callback не наблюдался\n";
Ожидаемый результат
Восстановленная корзина: Кисть:2
Files callback: create_sid
В архиве полностью определён TraceFiles.php. Журнал содержит только названия этапов. Для gc здесь наблюдаем вызов, а настоящая уборка старых записей показана в карточке session_gc. Для destroy отдельно проверяется удаление одной записи в следующем опыте.
Рабочую ротацию инициирует функция верхнего уровня, а не прямой вызов метода.
<?php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/TraceFiles.php';
$store = new TraceFiles();
session_set_save_handler($store, true);
session_start(); $old = session_id(); $store->events = []; session_regenerate_id(false);
$changed = session_id() !== $old; session_write_close();
var_dump($changed, in_array('create_sid', $store->events, true));
Ожидаемый результат
bool(true)
bool(true)