PHP / Intermediate: objects, persistence, and tests / Lesson 11
Sessions, cookies, and state ownership
Understand a session as a server-side lifecycle associated with a client identifier. Separate authentication, authorization, cookie delivery, locking, and isolated session-storage evidence.
On this page
Lesson goals
- Explain session identity without treating it as permission.
- Choose cookie and session security policies explicitly.
- Limit lock duration and verify state lifecycle.
A session identifier refers to state; it is not the whole policy
PHP sessions associate a session identifier with stored state through a configured save handler. A browser commonly carries the identifier in a cookie, but cookie transport and server storage are separate mechanisms. Possessing an identifier must not be confused with permission for every object or action. Authentication establishes an account context, while authorization checks the requested operation against current server rules. Keep sensitive decisions authoritative on the server and minimize stored state. Do not put session identifiers in public URLs, logs, screenshots, or teaching outputs; this chapter uses only owned synthetic local state.
Cookie flags and fixation defenses solve different risks
Use an explicit cookie policy appropriate to HTTPS, script access, and cross-site behavior: Secure, HttpOnly, and SameSite address different properties. SameSite is not a universal replacement for CSRF defenses, and HttpOnly does not make an application immune to injected script effects. Strict session mode helps reject uninitialized identifiers under supported handlers. Identifier regeneration at relevant privilege transitions must follow a safe lifecycle that accounts for concurrent requests and storage behavior; do not copy a destructive rotation one-liner without understanding lost sessions. Review the current PHP security documentation and application threat model before deployment.
Test storage without a browser cookie or application session
The CLI demonstration creates a private temporary directory, disables cookie use, and selects its own file save handler and storage path. It writes one synthetic counter, closes the session, reopens the same local identity, and reads it back. The directory and session file are removed afterward. No configured application storage or authentication data is loaded. This verifies persistence for this local handler only; it does not test HTTPS, browser cookie acceptance, distributed storage, or a production login flow. Keep such environment assumptions in the caption rather than presenting a CLI result as complete session security.
<?php
declare(strict_types=1);
if (PHP_SAPI !== "cli") { throw new RuntimeException("CLI only"); }
$path = sys_get_temp_dir() . "/php-session-demo-" . bin2hex(random_bytes(6));
mkdir($path, 0700);
ini_set("session.save_handler", "files");
ini_set("session.save_path", $path);
ini_set("session.use_cookies", "0");
session_cache_limiter("");
try {
session_start();
$_SESSION["visits"] = 1;
session_write_close();
session_start();
$visits = $_SESSION["visits"];
session_destroy();
echo $visits, PHP_EOL;
} finally {
if (session_status() === PHP_SESSION_ACTIVE) { session_write_close(); }
foreach (glob($path . "/*") ?: [] as $file) { unlink($file); }
rmdir($path);
}
1
The output proves the local counter survives close/reopen. Cookie delivery and authentication are intentionally outside this evidence.
Release the session when its state work is finished
Some session handlers lock state while a request owns it. A long request can therefore block another request for the same session, making an otherwise fast endpoint appear stuck. Read or update the required state, then close writing when the workflow no longer needs the lock. Do not continue assuming later $_SESSION edits will persist after session_write_close. Session state should not become a substitute database for durable business operations. The pure snapshot example illustrates an explicit local boundary: later mutations of a working copy do not change a saved snapshot in this model. Real locking behavior still needs handler-specific tests.
<?php
declare(strict_types=1);
$working = ["cartCount" => 1];
$savedSnapshot = $working;
$working["cartCount"] = 2;
echo $savedSnapshot["cartCount"], " ", $working["cartCount"], PHP_EOL;
1 2
The example distinguishes a saved value from a later working change. It does not simulate session locking or cookie transport.
Expiration and logout require explicit semantics
Define idle timeout, absolute lifetime, privilege changes, logout, and recovery according to product risk. Server-side state invalidation and expiring a browser cookie are related but different actions; removing one client cookie does not necessarily revoke every server-side session. Avoid storing secrets or full private records merely for convenience. Test a synthetic lifecycle with independent clients where appropriate and verify that rejected or expired operations have no business side effects. Record browser origin, cookie scope, handler, and concurrency assumptions when diagnosing persistence. Never weaken flags or reuse real account sessions as a shortcut to make a test pass.
Common errors and fixes
CLI persistence is called browser-session proof
- Why it happens
- Storage and cookie transport are conflated.
- How to fix it
- Verify cookie delivery and server storage separately.
- Prevent it next time
- Label the handler, host, and tested lifecycle.
A long request blocks same-session work
- Why it happens
- A handler lock is retained during unrelated work.
- How to fix it
- Close session writing after required state changes.
- Prevent it next time
- Test concurrent synthetic clients against the intended handler.
A session field is treated as permanent permission
- Why it happens
- Stored identity context replaces current authorization checks.
- How to fix it
- Authorize sensitive operations against current trusted rules.
- Prevent it next time
- Review privilege changes and session invalidation together.
Check your understanding
1. Does HttpOnly eliminate the need for XSS prevention?
Show worked solution
No. It limits script access to that cookie, but injected script may still perform actions in the userโs context or alter the page. Keep safe output handling, content policies where appropriate, CSRF strategy, and server authorization as separate defenses. Test the actual task rather than relying on one flag.
2. What does session_write_close change about later state edits?
Show worked solution
It writes and closes the current session; later ordinary changes to the in-memory $_SESSION array are not automatically saved through that closed session. Complete required state updates before closing, then avoid pretending later edits are durable. Verify behavior for the configured handler in an isolated test.
Key takeaways
- Cookie policy, storage, identity, and authorization are distinct boundaries.
- Own the session lifecycle and minimize its lock and retained state.