skip to content

Cookies & Sessions

setcookie writes a cookie and $_COOKIE reads it back, while session_start ties $_SESSION to a cookie-carried ID. Interviewers probe the hardening flags, ID rotation and session file locking.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

6

In PHP, what does session_start() actually do, and how does $_SESSION data survive from one request to the next?

level: juniorimportance: must knowfreq 80%

answer

  1. ID from the PHPSESSID cookie
  2. files handler, sess_ file in save_path
  3. unserialized into $_SESSION at start
  4. serialized and written at script end
  5. before output, or a warning and false

basics

~20 s

session_start() takes the session ID from the PHPSESSID cookie or creates a new one, loads that ID's stored data through the save handler into $_SESSION, and PHP writes $_SESSION back to storage when the request ends.

solid answer

~40 s

`session_start()` looks for the cookie named by `session.name` (default `PHPSESSID`). If none arrives it creates a new ID and queues a `Set-Cookie` header, which is why it must run before any output. It then opens the save handler, `files` by default with one `sess_<id>` file in `session.save_path`, reads the stored string and unserializes it into `$_SESSION`. The PHP process itself keeps nothing: the data survives only because it is written back. When the script ends, or earlier on `session_write_close()`, PHP serializes `$_SESSION` and hands it to the handler; with `session.lazy_write` on (the default) an unchanged session only has its timestamp refreshed. `session_status()` tells you whether a session is active.

code

php · 9 lines
php
<?php
declare(strict_types=1);

session_start();                 // before any output: may send the PHPSESSID cookie

$_SESSION['views'] = ($_SESSION['views'] ?? 0) + 1;

echo 'Views this session: ' . $_SESSION['views'];
// No save call: PHP serializes $_SESSION and writes it when the script ends

go deeper

for a junior

Recall that session_start() goes before output, that the browser holds only the ID in PHPSESSID, and that $_SESSION is loaded at start and saved automatically at the end.

for a middle

Walk through the handler's open, read and write steps, the files handler's sess_ files, the serialize handler, lazy_write and what session_status() reports.

for a senior

Explain the production failures: objects stored before their class is autoloadable, closures in the session, notices from double starts in shared code, and data growing with every request.

for a principal

Decide what belongs in the session at all versus a database row keyed by user, and keep the session small so storage, serialization and locking stay cheap.

## What a PHP session is PHP runs each request in a fresh state: variables, objects and arrays are gone when the request ends. A **session** is PHP's built-in way to carry per-visitor data across requests anyway. It has three parts: - a **session ID**, a random string the browser sends back on every request, normally in a cookie; - a **save handler**, the storage that maps an ID to a serialized string (the `files` handler by default); - the **`$_SESSION` superglobal**, an ordinary PHP array that is filled from storage at the start and written back at the end. Nothing about the data lives in the PHP process between requests, and nothing but the ID lives in the browser. ## What session_start() does, step by step 1. **Checks state.** If a session is already active it raises an `E_NOTICE` ("Ignoring session_start() because a session is already active") and returns `true` without reloading. If cookies are in use and headers were already sent, it warns "Session cannot be started after headers have already been sent" and returns `false`. 2. **Finds the ID.** It reads the cookie named by `session.name`. If there is none, the handler creates a new ID and PHP marks a `Set-Cookie` header to be sent. With `session.use_strict_mode=1`, an ID the handler does not recognise is replaced the same way. 3. **Opens and reads.** The handler's open and read steps run. The `files` handler opens `sess_<id>` under `session.save_path` (the system temp directory when the setting is empty) and takes an exclusive lock on it. 4. **Unserializes.** The stored string is decoded with `session.serialize_handler` (default `php`) into `$_SESSION`. Objects are rebuilt here, so their classes must be loadable at this moment. 5. **Returns `true`.** From now on `$_SESSION` is a normal array you read and change. At the end of the request, or when you call `session_write_close()` (alias `session_commit()`), PHP serializes `$_SESSION` and passes it to the handler's write step, then closes the session and releases the lock. With `session.lazy_write=1` and data that did not change, the default handler only refreshes the file's timestamp instead of rewriting it. ## Where the data lives by default | Setting | Built-in default | What it controls | |---|---|---| | `session.name` | `PHPSESSID` | cookie name carrying the ID | | `session.save_handler` | `files` | storage backend | | `session.save_path` | empty (system temp directory) | directory for `sess_*` files | | `session.serialize_handler` | `php` | format of the stored string | | `session.auto_start` | `0` | whether every request starts a session automatically | | `session.lazy_write` | `1` | skip rewriting unchanged data | ## Rules that trip people up - **Call it before output.** It may need to send a cookie, and cookies are headers. - **No explicit save call is needed.** Changes to `$_SESSION` are written automatically at the end of the request. - **Only serializable values survive.** Storing a `Closure` fails at write time; an object whose class cannot be autoloaded during `session_start()` comes back as `__PHP_Incomplete_Class`. Register the Composer autoloader first, and prefer scalars and arrays. - **`session_destroy()` is narrower than it sounds.** It deletes the stored record for the current ID but leaves `$_SESSION` populated for the rest of the request and the browser's cookie in place. - **PHP 8.5 change:** with the default `php` serializer, a `$_SESSION` key containing `|` makes the write fail with a "Failed to write session data" warning; before 8.5 the same write failed silently. ## Checking the state `session_status()` returns one of three constants: - `PHP_SESSION_DISABLED` when the session extension is unavailable; - `PHP_SESSION_NONE` when sessions work but none is active; - `PHP_SESSION_ACTIVE` after a successful `session_start()` and before the session is closed. Library code that may run inside an existing session checks `session_status() === PHP_SESSION_ACTIVE` rather than calling `session_start()` blindly and relying on the notice. ## The ID and the name from code Two small functions expose the pieces `session_start()` works with: - `session_id()` returns the current ID, or an empty string when no session is active. Passed an argument before `session_start()`, it sets the ID to use instead of the one from the cookie, which is mostly useful in tests and command-line tools. - `session_name()` returns the cookie name, `PHPSESSID` unless `session.name` was changed; passing a new name must also happen before `session_start()`. Neither is needed in everyday web code, where the cookie and `session_start()` handle both. They matter when debugging ("which session file is this request using?") and when two applications on one domain must not share a cookie name, since both would otherwise read and overwrite the same `PHPSESSID`.

  • What does session_start() do if a session is already active?
    It raises an `E_NOTICE`, "Ignoring session_start() because a session is already active", and returns `true` without reading storage again. Code that may run in several places should check `session_status() === PHP_SESSION_ACTIVE` first instead of relying on the notice.
  • Why can an object stored in $_SESSION come back as __PHP_Incomplete_Class?
    The session string is unserialized inside `session_start()`. If the object's class is not loaded and no autoloader can load it at that moment, PHP builds an `__PHP_Incomplete_Class` placeholder instead. Register the Composer autoloader before `session_start()`, and prefer storing IDs, scalars and arrays over whole objects.

saying these in an interview costs you the question

  • PHP keeps $_SESSION in the worker's memory between requests
  • The session data itself travels inside the PHPSESSID cookie
  • You must call a save function or $_SESSION changes are lost
  • Calling session_start() twice reloads fresh data from storage
  • session_start() still works after HTML has been sent to the client
open as a page

In PHP, what do session_regenerate_id() and session.use_strict_mode each protect against, and what are their defaults?

level: middleimportance: must knowfreq 62%

basics

~10 s

Both defend against session fixation. session_regenerate_id() swaps the ID after a privilege change such as login, keeping the data; session.use_strict_mode, off by default, makes session_start() refuse IDs the server never issued.

open as a page

A PHP shopping-basket page fires four AJAX requests at once, yet they finish one after another instead of in parallel; why, and how do you fix it?

level: seniorimportance: should knowfreq 50%

basics

~20 s

The default files handler holds an exclusive lock on the session file from session_start() until the session is written, so requests sharing one session ID queue. Release it early with session_write_close(), or use read_and_close where nothing is written.

open as a page

In PHP, how do you implement and register a custom session save handler with SessionHandlerInterface, and what must it handle itself?

level: seniorimportance: nice to knowfreq 25%

basics

~10 s

Implement SessionHandlerInterface's open, close, read, write, destroy and gc, register the object with session_set_save_handler($handler, true) before session_start(), and handle locking, ID validation and timestamp refreshes yourself.

open as a page