-
Notifications
You must be signed in to change notification settings - Fork 0
FAQ
Quick answers to common pitfalls and clarifications. If your question is not here, please open an issue — documentation fixes are reviewed eagerly.
Almost always because send() ran after output. Like native
setcookie(), send() writes an HTTP header, which only works before any body
has been emitted (no echo, no HTML, no whitespace/BOM outside the PHP tags).
Call send() at the end of request handling, before you render.
$cookie->set('a', '1');
$cookie->send(); // BEFORE any output
echo $html; // output starts hereDo not rely on the destructor: it often runs during shutdown, after the body has flushed, and the default writer suppresses the "headers already sent" warning — so a too-late write fails silently. See Sending & Lifecycle.
The usual cause is a per-key TTL that has elapsed. The $ttl argument of
set()/setArray()/push() is seconds from now; once it passes, the entry is
removed on the next read and never re-sent. Check:
- You did not pass a short
$ttlyou have since forgotten about. - The per-key TTL is not longer than the transport
ttloption — if it is, the browser drops the whole cookie when the transport TTL elapses. - You actually called
send()after the mutation.
See TTL & Expiry.
No, that is by design. The salt is the HMAC key. When it changes, every previously issued cookie fails the signature check and is silently replaced with an empty (freshly signed) cookie. Users lose their stored values. Rotate the salt only when you intend exactly that — it doubles as a global "invalidate every cookie" switch. See the Security Model.
Yes. The payload is signed, not encrypted — it is base64 of a plain PHP serialization, which anyone can decode. The signature proves the data was issued by you and detects tampering; it does not hide the values. Do not store secrets, password hashes, or sensitive PII. Store an opaque reference and keep the real data server-side. See the Security Model and the Remember Me recipe.
Only string, bool, int, float and numeric strings are accepted;
anything else throws CookieInvalidArgumentException. To store
structured data, encode it into a string yourself and decode on read:
$cookie->set('prefs', json_encode(['theme' => 'dark', 'lang' => 'en']));
// ...later...
$prefs = json_decode((string) $cookie->get('prefs', '{}'), true);Keep the total payload small (see the next entry).
Yes. Browsers cap a single cookie at roughly 4 KB (4096 bytes) for the whole name=value pair, and the value here also includes the base64 overhead plus a 64-character HMAC signature. Because every value you set shares one cookie, a large value or many values can blow past the limit, at which point the browser silently drops the cookie. Keep the combined payload well under 4 KB; store bulk data server-side.
null is not a storable value type, so set('k', null) throws. But get()
returns null as its default when a key is absent. To remove a value, call
remove('k') rather than setting it to null. To tell "absent" from a stored
false/0/'', use has(). See Basic Usage.
-
flush()clears the values but keeps the cookie: the nextsend()writes an empty, still-signed cookie that stays in the browser. -
destroy()writes a deletion cookie immediately, telling the browser to remove it, and makes the nextsend()a no-op.
Use flush() to empty the contents, destroy() to delete the cookie (e.g.
logout). See Reading & Removing.
A negative $ttl is normalized with abs(), so -100 behaves like 100
seconds from now — the value stays valid. This prevents an accidental sign flip
from silently dropping data. To actually delete a value, call remove(), not
set() with a negative TTL. See
TTL & Expiry.
Cookie is final and its properties are private to keep the
security-sensitive surface (signing, verification, the deserialization guard)
closed to accidental override. A subclass that touched encoding, the salt, or
the verification flow could quietly defeat the tamper protection. Extend by
composition: depend on CookieInterface
and wrap a Cookie instance in your own class, as the
Flash Messages and Testing pages do.
It reads $_COOKIE by default (you can inject $source instead) and never
writes to it. Writes go through the injectable $writer, which defaults to
native setcookie(). This makes the class fully testable without superglobal
or header juggling — see Testing.
PHP 7.4 through 8.4. The 2.0 line requires 7.4+ (1.x advertised 7.2 but already
relied on the PHP 7.3 setcookie() options-array signature). See
Migration (v1 → v2).
See SECURITY.md
in the InitPHP org profile. Please do not file public issues for
vulnerabilities.
- Quick Start — the five-minute tour.
- Security Model — the threat model and wire format.
-
Sending & Lifecycle — why
send()must run before output. - TTL & Expiry — the two lifetimes and their rules.
initphp/cookies · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Contributing · Security Policy
Getting Started
Core Usage
Reference
Practical Guides
Migration & Help