Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 112 · От первого шага до справочника

Сессии PHP: cookie, состояние, безопасность и полный API

От сохранения корзины между запросами до сроков входа, блокировок, собственного хранилища и всех функций/методов session.

Начать с наблюдаемого состояния, затем выбрать API

Наглядная модель

Почему корзина сохраняется между запросами

PHP-массив живёт в запросе; хранилище связывает его со следующим запросом.

  1. Браузер отправляет cookie с ID

    В cookie обычно находится идентификатор, а не весь массив корзины.

  2. session_start

    Обработчик читает серверное состояние по ID; files обычно удерживает блокировку.

  3. $_SESSION["cart"]

    PHP читает и меняет данные текущего запроса.

  4. Закрытие сессии

    Запись/завершение освобождает ресурс, следующий запрос может прочитать сохранённое.

Идентификатор сессии не заменяет проверку прав. Срок cookie, срок входа и уборка старых файлов — разные механизмы.

Сначала получите две страницы с общим языком и полный кабинет. Далее эта глава раскрывает все настройки, блокировки и методы. Сессия хранит состояние; полномочия, сроки и CSRF приложение проверяет отдельно.

Корзина не должна исчезать при переходе на другую страницу

Посетитель выбрал два товара и открыл доставку. Обычная переменная первого PHP-запроса уже исчезла; следующему запросу нужно найти его корзину. Сессия связывает запросы непрозрачным идентификатором: браузер хранит ID в cookie, сервер — значения корзины. Полезный результат: следующий запрос снова читает число2.

Cookie как номер ячейки, данные — на сервере
  1. Браузер: cookie с непрозрачным ID
  2. PHP получает ID и вызывает session_start
  3. Хранилище возвращает cart_qty=2
  4. PHP создаёт $_SESSION для текущего запроса
  5. 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.

Подготовка маленького опыта
  1. Один временный каталог примера
  2. bootstrap создаёт session-data рядом
  3. Только этот процесс выбирает files и отключает HTTP-cookie
  4. После выхода лабораторная папка может быть удалена целиком
Проверить наличие расширения без раскрытия конфигурацииСамостоятельный пример
<?php
var_dump(extension_loaded('session'), function_exists('session_start'));
Ожидаемый результат
bool(true)
bool(true)

Для проверенного runtime8.5.11; не выводим phpinfo на публичную страницу.

Полный bootstrap самостоятельных CLI-примеровСамостоятельный пример
<?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.

Два запроса одной корзины
  1. GET /state без Cookie → новая сессия
  2. Ответ Set-Cookie: имя=непрозрачный-ID
  3. POST /cart с Cookie → та же серверная запись
  4. Новый 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 выбирает их по жизненному циклу сессии.

Кто кого вызывает
  1. new LessonStore создаёт rows/events
  2. session_set_save_handler регистрирует объект
  3. session_start → open → проверка ID/генератор → read
  4. PHP превращает строку в $_SESSION; приложение меняет cart
  5. 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-сообщение; оно не должно повторяться после обновления.

Один ключ на два открытия
  1. POST: notice=Saved и close
  2. GET: прочитать notice и unset только этого ключа
  3. close сохраняет удаление
  4. Следующий 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. Остановите процесс после опыта. Это учебная модель одного аккаунта, без БД и ограничения попыток входа.

Проверяемый путь корзины
  1. GET /state → cookie и отдельный csrf-токен
  2. POST /cart с cookie, csrf и qty2 → проверка и запись
  3. GET /state с той же cookie → qty2
  4. POST /login demo-only → новый cookie, права только ему
  5. POST /logout → отзыв на сервере и истечение cookie
ЗапросОжидаемое действие
GET /stateJSON: visits увеличивается, qty сохранён; csrf можно использовать в следующем POST
POST /cartqty=1..9, csrf из state; отсутствие csrf →403, массив qty[] →422
POST /loginpassword=demo-only и csrf; после успеха использовать новый csrf из ответа
POST /logoutcsrf из нового состояния; cookie истекает; старый ID повторно →409
Любой пришедший неизвестный IDfiles + strict mode создаёт новый вместо принятия навязанного

В коде функция reply закрывает сессию и завершает запрос через exit; тип never сообщает, что она не возвращается вызывающему коду. $_SERVER описывает HTTP-запрос, $_POST содержит поля формы; они не становятся доверенными автоматически. is_string отсекает массив вместо поля, filter_var проверяет число и границы, hash_equals сравнивает отдельный CSRF-токен. ??= задаёт значение, только когда оно отсутствует/null. Формы и функции объясняют эти части подробнее.

router.php: проверяемая последовательность запросовПример для указанного окружения
<?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-токена.

Вход после проверки пароля
  1. Проверены форма, CSRF, пароль, ограничения попыток
  2. Помечен старый гостевой ID; ротация успешна
  3. Только новому ID назначены user_id, created_at, last_seen
  4. Каждый закрытый маршрут проверяет сроки, отзыв и права
МеханизмЧто решаетЧто остаётся
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, внесите нужные изменения, закройте сессию и только затем выполняйте долгую независимую работу.

Реальная последовательность двух процессов
  1. A открыл ID и держит lock
  2. B дошёл до start того же ID и ждёт
  3. A записал qty2 и закрыл
  4. 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. Ничего не запускайте в существующей папке с чужими служебными файлами.

Барьеры вместо предположения о скорости
  1. A: записал ready, lock удерживается
  2. B: reader-started создан, start ждёт
  3. Контроллер: release создан
  4. A: qty2 сохранено, lock отпущен
  5. B: reader-acquired создан, qty2 прочитан

microtime(true) задаёт предел ожидания10секунд, usleep(20000) — паузу20мс между проверками локального барьера. Это управление опытом, не рецепт ожидания внешнего API. Контроллер обязан завершить свои дочерние процессы в finally даже при ошибке. Реальный прогон делался двумя процессами PHP, без встроенного HTTP-сервера, поэтому его однопоточность не объясняет результат.

holder.php: удержать сессию до разрешенияПример для указанного окружения
<?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.

reader.php: прочитать только после освобожденияПример для указанного окружения
<?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, заменив транспорт хранения.

Полный цикл вызовов
  1. Приложение регистрирует объект
  2. PHP: open → validateId/create_sid → read
  3. Приложение читает и меняет $_SESSION
  4. 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-процессами.

Увидеть реальные callbacks одного сохраненияСамостоятельный пример
<?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 — другая задача.

Перехват одной операции
  1. PHP вызывает TraceFiles::write(id,data)
  2. Журнал получает только слово write
  3. parent::write передаёт точные байты files
  4. Результат возвращается 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 показывает новое и повторное открытие.

Генератор как дополнительная обязанность
  1. Основной handler уже умеет read/write
  2. SessionIdInterface добавляет create_sid
  3. 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_handlerfiles · INI_ALLBackend хранения; files встроен, другие зависят от расширений. Пользовательский объект — через set_save_handler, не строку user.
session.save_pathпусто · INI_ALLАргумент backend. Для files закрытый абсолютный каталог, необязательные N;MODE;path; подробнее ниже.
session.namePHPSESSID · INI_ALLИмя cookie/исторического параметра ID; одинаково в bootstrap всех маршрутов приложения.
session.auto_start0 · INI_PERDIRОткрыть сессию до пользовательского кода. Определения классов для её объектов нужно загрузить заранее через auto_prepend_file; проще явно start после автозагрузчика.
session.serialize_handlerphp · INI_ALLphp/php_binary/php_serialize. Последний кодирует целый массив и снимает ограничения корневых ключей. Формат всех процессов должен совпадать.
session.gc_probability1 · INI_ALLЧислитель вероятности запуска GC при старте; >=0. Ноль отключает случайную уборку.
session.gc_divisor100 · INI_ALLЗнаменатель вероятности, >0. 1/100 = 1%, а не гарантированно каждый сотый запрос.
session.gc_maxlifetime1440 · INI_ALLВозраст в секундах для уборки, не срок авторизации. Минимальный срок одного приложения может удалить общие файлы другого.
session.lazy_write1 · INI_ALLНе переписывать неизменившийся payload при поддерживаемом timestamp-пути; не отменяет start, чтение и возможную блокировку.
session.use_cookies1 · INI_ALLПередавать ID cookie. Ноль в наших CLI-labs исключает HTTP; на сайте используется1.
session.use_only_cookies1 · INI_ALLНе брать ID из GET/POST. Отключение deprecated с8.4; не используйте как обход отказа cookies.
session.use_strict_mode0 · INI_ALLОтвергать ID, которого нет в backend. Для сайта включить1; кастомному handler нужен validateId/validate_sid.
session.cookie_lifetime0 · INI_ALLСекунды жизни cookie;0 — session-cookie. Не определяет TTL файла, не гарантирует logout при восстановлении браузера.
session.cookie_path/ · INI_ALLНа каких путях браузер прикладывает cookie; не барьер авторизации.
session.cookie_domainпусто · INI_ALLБез Domain — host-only. Указанный допустимый домен включает поддомены; ведущая точка современным браузерам не нужна.
session.cookie_secure0 · INI_ALLCookie только через HTTPS. Для production HTTPS включить1; доPHP7.2 default был пустой строкой.
session.cookie_httponly0 · INI_ALLЗапретить чтение cookie JavaScript. Не блокирует действия XSS от имени пользователя; до7.2 default был пустой строкой.
session.cookie_samesiteпусто · INI_ALLPHP7.3+: пусто не добавляет атрибут; Lax/Strict/None выбирают browser-policy. None требует Secure.
session.cookie_partitioned0 · INI_ALLPHP8.5+: Partitioned/CHIPS для изоляции по top-level-site. Требует Secure; иначе PHP предупреждает и не отправляет session-cookie.
session.cache_limiternocache · INI_ALLnocache/private/private_no_expire/public либо пусто для ручного cache-control. Не определяет cookie и срок входа.
session.cache_expire180 · INI_ALLМинуты HTTP-кэша; nocache игнорирует. Не путать с gc_maxlifetime в секундах.
session.use_trans_sid0 · INI_ALLАвтоматическая подстановка ID в HTML-ссылки/формы. Включение deprecated с8.4; риск утечки через URL.
session.trans_sid_tagsa=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_length32 · INI_ALLPHP7.1+: допустимая длина22..256. Изменение default deprecated8.4; оставьте генерацию современному PHP.
session.sid_bits_per_character4 · INI_ALLPHP7.1+:4(hex),5(0–9 a–v),6(0–9 a–z A–Z , -). Изменение default deprecated8.4; не увеличивайте наугад.
session.upload_progress.enabled1 · INI_PERDIRЗаписывать метрики multipart upload до исполнения скрипта; отключите, если не используете серверный прогресс.
session.upload_progress.cleanup1 · INI_PERDIRУдалить прогресс после чтения всего POST. Обычно оставить включённым.
session.upload_progress.prefixupload_progress_ · INI_PERDIRПрефикс ключа в $_SESSION; дополняется значением клиентского hidden-поля.
session.upload_progress.namePHP_SESSION_UPLOAD_PROGRESS · INI_PERDIRИмя hidden-поля POST, которое включает наблюдение. Его value — идентификатор операции; это не имя ID-cookie.
session.upload_progress.freq1% · INI_PERDIRКак часто обновлять: байты либо процент от размера POST. Частая запись увеличивает I/O/конкуренцию.
session.upload_progress.min_freq1 · INI_PERDIRМинимальная задержка между обновлениями в секундах; вместе с freq ограничивает частоту.
session.hash_function0 · удалена7.1Исторически MD5/SHA-1 или алгоритм hash. Не существует как рабочая настройка PHP8.x.
session.hash_bits_per_character4 · удалена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-сборке.

Изменить только одну настройку до старта
  1. session_set_cookie_params: lifetime600
  2. ini_get: та же настройка видна как строка600
  3. Срок входа приложения остаётся отдельной величиной
API cookie меняет эффективные runtime-настройкиСамостоятельный пример
<?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_DISABLED0: механизм недоступен/отключён
PHP_SESSION_NONE1: сейчас нет открытой сессии; файл и ID при этом могут существовать
PHP_SESSION_ACTIVE2: сессия открыта в текущем запросе, но это ещё не авторизация
SIDИсторическая строка name=ID или пусто при подходящей cookie; deprecated8.4. Не вставляйте её в ссылки.
Выбрать действие по состоянию
  1. status===NONE → допустим start
  2. status===ACTIVE → можно менять $_SESSION
  3. 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, а не обработка видео/импорт файла после загрузки. Для долгой обработки нужна отдельная задача и её собственный статус.

Два запроса одной операции
  1. Форма уже получила session-cookie и upload_id
  2. multipart: hidden PHP_SESSION_UPLOAD_PROGRESS идёт перед file
  3. PHP принимает байты и обновляет upload_progress_ + value
  4. Другой запрос с той же cookie читает текущие счётчики
  5. После чтения 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, тип/размер и результат загрузки; здесь не создаётся обработчик сохранения файла.

Форма: правильный порядок hidden и fileПример для указанного окружения
<?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 и не заявляется измеренным этим примером.

Отмена загрузки отдельным проверенным POSTПример для указанного окружения
<?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.

PHP4.4+ · session

session_commit(): bool

Сохранить выбор тем же действием под именем commit. session_commit() — псевдоним session_write_close: запись и закрытие сессии. Слова commit здесь не означают транзакцию SQL или всего заказа.

Путь конкретных данных
  1. delivery=Пункт выдачи
  2. commit сохраняет и закрывает, как write_close
  3. Новый 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-ответ и не отправляет его немедленно.
Сохранить выбор тем же действием под именем commitСамостоятельный пример
<?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Закрыть сессию, отказавшись от несохранённых изменений.

PHP5.6+ · session

session_abort(): bool

Отменить несохранённую смену доставки. До редактирования сохранён «Самовывоз». Новое значение пока только в памяти. abort закрывает работу без записи; внешний платёж или запрос к БД он не отменяет.

Путь конкретных данных
  1. Сохранено Самовывоз → в памяти выбран Курьер
  2. abort закрывает без новой записи
  3. Новый 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Перечитать сохранённую сессию и продолжить работать с ней.

PHP5.6+ · session

session_reset(): bool

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

Путь конкретных данных
  1. Сохранено Самовывоз → в памяти выбран Курьер
  2. reset возвращает Самовывоз и оставляет сессию открытой
  3. Новый выбор Пункт выдачи сохраняется через 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 без открытой сессии ничего не делает.

После 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Очистить все ячейки открытой сессии, сохранив её идентификатор.

PHP 4+ · session

session_unset(): bool

Очистить всю гостевую корзину. Вызов удаляет все ячейки открытого массива. Поэтому при наличии auth он удалил бы и вход: для одной корзины лучше unset($_SESSION['cart']). Здесь сессия намеренно содержит только выбор гостя.

Путь конкретных данных
  1. Гостевая сессия: cart=Кисть:2, coupon=DEMO
  2. unset очищает обе ячейки → close сохраняет очистку
  3. Следующее открытие: Корзина пуста

Распакуйте все файлы архива вместе. Команда 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 или выбрать уже существующую сессию до старта.

PHP 4+ · session

session_id(?string $id = null): string|false

Открыть собственный сохранённый выбор по его ключу. Без аргумента функция читает непрозрачный ID, со строкой выбирает ID до start. В этом опыте ключ получен самим PHP; на сайте его передаёт session-cookie, а поле user_id из формы таким доказательством не является.

Путь конкретных данных
  1. PHP создал ID и сохранил language=Русский
  2. После закрытия выбираем свой ID перед новым start
  3. Чтение восстанавливает Русский

Распакуйте все файлы архива вместе. Команда 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 нельзя решать, открыта ли сессия для записи.

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, ещё не переключая текущую сессию.

PHP7.1+ · session

session_create_id(string $prefix = ""): string|false

Подготовить кандидат, сохранив текущую корзину. Строка shop- — необязательный несекретный префикс. Возвращённый кандидат ещё не переключает сессию и не выдаёт cookie. Для обычного входа используется regenerate_id.

Путь конкретных данных
  1. Рабочий ID связан с cart=Кисть:2
  2. create_id(shop-) возвращает отдельный кандидат
  3. Рабочая сессия по-прежнему содержит Кисть: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.

PHP 4+ · session

session_name(?string $name = null): string|false

Дать приложению устойчивое имя cookie. Имя одинаково для всех посетителей, значение ID различается. Вызов со строкой меняет настройку текущего запроса и возвращает прежнее имя; общий bootstrap повторяет настройку на каждой странице.

Путь конкретных данных
  1. До настройки имя ManualSession
  2. session_name(ShopSession) до start
  3. Cookie приложения будет называться ShopSession

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.

Задать имя cookie, под которым браузер приносит ID.

Часть вызоваЧто означает
namenull/пропуск читает; строка меняет и возвращает старое имя. До start и до вывода. PHP8.0 nullable.
ИмяКороткая буквенно-цифровая строка, хотя бы одна буква; не только цифры. Имя одинаково для всех посетителей приложения, ID разный.
ОбластьДействует текущий запрос; повторяйте в общем bootstrap. Разные имена не заменяют раздельные save_path и права доступа. Смена при ACTIVE с PHP7.2 отвергается.
Дать приложению устойчивое имя cookieСамостоятельный пример
<?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Указать обработчику место хранения данных до старта.

PHP 4+ · session

session_save_path(?string $path = null): string|false

Найти файл сохранённой корзины в своём каталоге. Для обработчика files путь выбирает закрытую папку данных. В приложенном bootstrap она создаётся возле примера. Настройка пути сама не создаёт каталог и не делает его недоступным по HTTP.

Путь конкретных данных
  1. Bootstrap выбрал свою папку session-data
  2. close записал cart=Кисть:2 в sess_ID
  3. Из собственного файла читаются точные php_serialize-байты

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.

Указать обработчику место хранения данных до старта.

Часть вызоваЧто означает
pathnull/пропуск читает; строка устанавливает, возвращается прежняя строка. 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 или обработчик расширения.

PHP 4+ · session

session_module_name(?string $module = null): string|false

Узнать, кто сохраняет выбор между открытиями. Название backend отвечает на вопрос о хранилище. files использует файлы; слово user появляется после регистрации объекта. Название Redis само по себе не устанавливает расширение.

Путь конкретных данных
  1. Bootstrap выбрал files
  2. start/close сохраняют delivery=Самовывоз
  3. module_name сообщает files; повторное чтение подтверждает доставку

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.

Узнать, какой backend хранит сессии: files или обработчик расширения.

Часть вызоваЧто означает
modulenull/пропуск читает; строка переключает доступный внутренний модуль до старта, возвращая прежний. 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: нужен реальный объект, реализующий договор хранения.

user нельзя включить одной строкой настройкиСамостоятельный пример
<?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_cache_expireПрочитать или изменить время HTTP-кэша ответа, связанного с сессией.

PHP4.2+ · session

session_cache_expire(?int $value = null): int|false

Перевести настройку минут в срок HTTP-кэша. Функция задаёт минуты для session cache limiter. Это не idle timeout входа. При nocache хранение ответа запрещено независимо от установленного количества минут.

Путь конкретных данных
  1. Выбрана HTTP-политика private
  2. cache_expire(12) задаёт12минут =720секунд
  3. Срок авторизации от этой настройки не меняется

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.

Прочитать или изменить время HTTP-кэша ответа, связанного с сессией.

Часть вызоваЧто означает
valuenull/пропуск читает; переданное значение меняет до start и возвращает прежнее. Nullable сPHP8.0. Настройка действует текущий запрос.
Результатint|false, минуты; не секунды и не срок входа.
ОграничениеПри nocache значения времени не разрешают кэширование. Нельзя этим продлить сессию.
Перевести настройку минут в срок HTTP-кэшаСамостоятельный пример
<?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-кэша ответа, связанного с сессией.

PHP4.0.3+ · session

session_cache_limiter(?string $value = null): string|false

Не разрешать сохранять персональный ответ в HTTP-кэше. nocache выбирает набор cache-заголовков при последующем start. Это политика ответа; она не отзывает уже прочитанную человеком страницу и не заменяет проверку user_id.

Путь конкретных данных
  1. Кабинет содержит персональный ответ
  2. cache_limiter(nocache) до session_start
  3. HTTP-политика включает no-store; авторизация проверяется отдельно

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.

Прочитать или изменить политику HTTP-кэша ответа, связанного с сессией.

Часть вызоваЧто означает
valuenull/пропуск читает; переданное значение меняет до 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.
Не разрешать сохранять персональный ответ в HTTP-кэшеСамостоятельный пример
<?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Получить строку хранилища из текущего массива сессии.

PHP 4+ · session

session_encode(): string|false

Увидеть точную строку, которую сохраняет backend. Сессия уже открыта; encode снимает текущий массив в формате выбранного сериализатора. В bootstrap явно включён php_serialize. Число после s — длина строки в байтах, поэтому русские буквы занимают больше одного байта.

Путь конкретных данных
  1. Открытый массив содержит cart=Кисть:2
  2. encode с php_serialize формирует снимок
  3. Строка содержит 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-методы. Предпочитайте простые проверенные значения.
Увидеть точную строку, которую сохраняет backendСамостоятельный пример
<?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.

PHP 4+ · session

session_decode(string $data): bool

Восстановить свой снимок корзины. Функция получает сериализованные байты и меняет $_SESSION, а возвращает лишь успех. Снимок создаётся здесь же, не поступает из cookie/POST: этот механизм не подходит для недоверенных строк.

Путь конкретных данных
  1. Собственный snapshot хранит Кисть:2
  2. В памяти Кисть:9 → decode(snapshot)
  3. Восстановлено Кисть: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Запустить удаление устаревших записей хранилища.

PHP7.1+ · session

session_gc(): int|false

Удалить старую запись, сохранив свежую. В собственном каталоге есть две учебные записи. touch задаёт возраст старой, gc_maxlifetime60 задаёт границу уборки в секундах. Даже не удалённая ещё запись обязана пройти отдельную проверку срока входа.

Путь конкретных данных
  1. old старше3600секунд, fresh только что записан
  2. gc с maxlifetime60 обрабатывает собственную папку
  3. 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Зарегистрировать закрытие сессии при штатном завершении скрипта.

PHP5.4+ · session

session_register_shutdown(): void

Сохранить сообщение при штатном завершении скрипта. Вызов регистрирует закрытие в очереди завершения. Наш следующий callback читает файл уже после закрытия. function определяет отложенное действие, use передаёт ему путь; аварийное убийство процесса не гарантирует запуск очереди.

Путь конкретных данных
  1. notice=Заказ принят находится в открытой сессии
  2. Очередь shutdown сначала вызывает write_close
  3. Наш следующий 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 вызывает его методы в нужные моменты сессии.

PHP 4+ · session

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.

Путь конкретных данных
  1. Регистрируем LessonStore
  2. Сохраняем notice=Заказ принят
  3. Повторный 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_shutdowntrue автоматически организует 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; современный путь — объект.
Шесть основных callbacksopen(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 имеют разный смысл.

Отказ backend отличается от пустой сессииСамостоятельный пример
<?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 при старте.

PHP5.4+ · session

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.

Путь конкретных данных
  1. session_start → open/read для текущего ID
  2. В памяти cart=Кисть:2; open выполняет свой этап
  3. Человек получает восстановленную корзину либо подтверждённое изменение хранилища

Распакуйте все файлы архива вместе. Команда 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 завершает сессию.

PHP5.4+ · session

public close(): bool

Освобождение собственной корзины. close освобождает ресурсы даже после abort без записи. В этом успешном проходе PHP сначала пишет данные, затем закрывает backend. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.

Путь конкретных данных
  1. session_start → open/read для текущего ID
  2. В памяти cart=Кисть:2; close выполняет свой этап
  3. Человек получает восстановленную корзину либо подтверждённое изменение хранилища

Распакуйте все файлы архива вместе. Команда 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 обязательно был вызван.

Отмена тоже освобождает backendСамостоятельный пример
<?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.

PHP5.4+ · 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.

Путь конкретных данных
  1. session_start → open/read для текущего ID
  2. В памяти cart=Кисть:2; read выполняет свой этап
  3. Человек получает восстановленную корзину либо подтверждённое изменение хранилища

Распакуйте все файлы архива вместе. Команда 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 под переданным идентификатором.

PHP5.4+ · session

public write(string $id, string $data): bool

Запись собственной корзины. write получает ID и уже сериализованную строку только при сохранении сессии. Повторная сериализация или htmlspecialchars повредили бы формат backend. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.

Путь конкретных данных
  1. session_start → open/read для текущего ID
  2. В памяти cart=Кисть:2; write выполняет свой этап
  3. Человек получает восстановленную корзину либо подтверждённое изменение хранилища

Распакуйте все файлы архива вместе. Команда 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 и отдельная транзакция бизнеса.

true у write_close не подтверждает запись 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.

PHP5.4+ · session

public destroy(string $id): bool

Удаление одной записи собственной корзины. destroy(id) касается конкретной сессии. Здесь сохраняем соседнюю запись, удаляем текущую и проверяем, что чужая корзина осталась. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.

Путь конкретных данных
  1. session_start → open/read для текущего ID
  2. В памяти cart=Кисть:2; destroy выполняет свой этап
  3. Человек получает восстановленную корзину либо подтверждённое изменение хранилища

Распакуйте все файлы архива вместе. Команда 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 предшествует проектирование ротации, а не копирование этого теста.

Ротация с true вызывает destroy старого 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();
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Удалить записи старше заданного срока и вернуть число удалений.

PHP5.4+ · session

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.

Путь конкретных данных
  1. session_start → open/read для текущего ID
  2. В памяти cart=Кисть:2; gc выполняет свой этап
  3. Человек получает восстановленную корзину либо подтверждённое изменение хранилища

Распакуйте все файлы архива вместе. Команда 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.

PHP5.5.1+ · session

public create_sid(): string

Создать ID при первом старте. PHP использует дополнительный интерфейс, когда нужен новый ключ. Наш create_sid получает 16 случайных байтов, делает hex и проверяет собственные rows. Это ещё не атомарное резервирование в распределённой БД. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.

Путь конкретных данных
  1. Bootstrap задаёт строгий режим и php_serialize
  2. PHP вызывает SessionIdInterface::create_sid в соответствующий момент
  3. Вывод отражает доступ к корзине или время её хранения

Распакуйте все файлы архива вместе. Команда 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-процессами.

Создать ID при первом стартеСамостоятельный пример
<?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.

При существующем 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.

PHP7.0+ · session

public validateId(string $id): bool

Отклонить неизвестный ключ при strict mode. validateId отвечает на вопрос, существует ли запись. Проверка допустимых символов — другая задача. Переданный ниже ключ deliberatelymissing не зарегистрирован: PHP должен создать новое пустое состояние. Код подключает весь LessonStore.php из архива. Его rows — массив записей с полями data и updated, events — журнал имён вызовов. Это лаборатория одного процесса: новое HTTP-обращение не сохранит этот объект. Для настоящего сайта подходит штатный files или поддерживаемое постоянное хранилище. new LessonStore() создаёт объект, -> выбирает его поле; методы вызывает PHP после регистрации. Полный файл и порядок callbacks.

Путь конкретных данных
  1. Bootstrap задаёт строгий режим и php_serialize
  2. PHP вызывает SessionUpdateTimestampHandlerInterface::validateId в соответствующий момент
  3. Вывод отражает доступ к корзине или время её хранения

Распакуйте все файлы архива вместе. Команда 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-процессами.

Отклонить неизвестный ключ при strict modeСамостоятельный пример
<?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.

PHP7.0+ · session

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.

Путь конкретных данных
  1. Bootstrap задаёт строгий режим и php_serialize
  2. PHP вызывает SessionUpdateTimestampHandlerInterface::updateTimestamp в соответствующий момент
  3. Вывод отражает доступ к корзине или время её хранения

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

require __DIR__ . '/bootstrap.php' выполняет приложенный общий файл: __DIR__ — папка примера, точка соединяет путь. Он создаёт собственную session-data, выбирает files/php_serialize и отключает cookie только для CLI. Cookie между настоящими HTTP-запросами показана отдельно.

Продлить срок неизменившейся записи без переписывания всего payload.

Часть вызоваЧто означает
id/dataID и сериализованные данные. 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 без учёта срока хранения.

read_and_close не продлевает timestampСамостоятельный пример
<?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 при старте.

PHP5.4+ · session

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.

Путь конкретных данных
  1. TraceFiles подключён через session_set_save_handler
  2. PHP сохраняет cart=Кисть:2 в собственном каталоге
  3. Повторный 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) их не заменяет.

Наблюдать open у родного файлового обработчикаСамостоятельный пример
<?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.

PHP5.4+ · session

public read(string $id): string|false

Наблюдать read у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::read передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. read(id) возвращает точные байты, ранее полученные write. Сам PHP разбирает их в $_SESSION. Пустая строка означает отсутствие данных, false — сбой чтения.

Путь конкретных данных
  1. TraceFiles подключён через session_set_save_handler
  2. PHP сохраняет cart=Кисть:2 в собственном каталоге
  3. Повторный 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) их не заменяет.

Наблюдать read у родного файлового обработчикаСамостоятельный пример
<?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 под переданным идентификатором.

PHP5.4+ · session

public write(string $id, string $data): bool

Наблюдать write у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::write передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. write получает ID и уже сериализованную строку только при сохранении сессии. Повторная сериализация или htmlspecialchars повредили бы формат backend.

Путь конкретных данных
  1. TraceFiles подключён через session_set_save_handler
  2. PHP сохраняет cart=Кисть:2 в собственном каталоге
  3. Повторный 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) их не заменяет.

Наблюдать write у родного файлового обработчикаСамостоятельный пример
<?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 автоматически.

После закрытия второй write не появляетсяСамостоятельный пример
<?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 завершает сессию.

PHP5.4+ · session

public close(): bool

Наблюдать close у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::close передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. close освобождает ресурсы даже после abort без записи. В этом успешном проходе PHP сначала пишет данные, затем закрывает backend.

Путь конкретных данных
  1. TraceFiles подключён через session_set_save_handler
  2. PHP сохраняет cart=Кисть:2 в собственном каталоге
  3. Повторный 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) их не заменяет.

Наблюдать close у родного файлового обработчикаСамостоятельный пример
<?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.

Закрыть без записи через abortСамостоятельный пример
<?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.

PHP5.4+ · session

public destroy(string $id): bool

Наблюдать destroy у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::destroy передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. destroy(id) касается конкретной сессии. Здесь сохраняем соседнюю запись, удаляем текущую и проверяем, что чужая корзина осталась.

Путь конкретных данных
  1. TraceFiles подключён через session_set_save_handler
  2. PHP сохраняет cart=Кисть:2 в собственном каталоге
  3. Повторный 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) их не заменяет.

Наблюдать destroy у родного файлового обработчикаСамостоятельный пример
<?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: Удалить записи старше заданного срока и вернуть число удалений.

PHP5.4+ · session

public gc(int $max_lifetime): int|false

Наблюдать gc у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::gc передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. gc(max_lifetime) получает возраст в секундах. Учебные часы now=1000 позволяют без ожидания сравнить запись с updated100 и запись с updated990.

Путь конкретных данных
  1. TraceFiles подключён через session_set_save_handler
  2. PHP сохраняет cart=Кисть:2 в собственном каталоге
  3. Повторный 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) их не заменяет.

Наблюдать gc у родного файлового обработчикаСамостоятельный пример
<?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 текущему внутреннему обработчику.

PHP5.5.1+ · session

public create_sid(): string

Наблюдать create_sid у родного файлового обработчика. Это метод штатного адаптера SessionHandler. В TraceFiles он записывает имя события, затем parent::create_sid передаёт работу родному files. extends наследует реализацию, parent обозначает базовый класс; это обёртка над действующим backend, не новое файловое хранилище. Генератор создаёт ID, когда PHP открывает новое состояние.

Путь конкретных данных
  1. TraceFiles подключён через session_set_save_handler
  2. PHP сохраняет cart=Кисть:2 в собственном каталоге
  3. Повторный 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) их не заменяет.

Наблюдать create_sid у родного файлового обработчикаСамостоятельный пример
<?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)

Дальше: session_create_id; SessionIdInterface::create_sid.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.