Under PSR-6 and PSR-16, which cache key characters and lengths must every implementation accept, and which characters are reserved?
answer
- A-Z, a-z, 0-9, underscore, period
- at least 64 characters, UTF-8
- reserved: {}()/\@:
- illegal key: InvalidArgumentException
- hash long or user-supplied parts
basics
~20 sBoth PSRs require implementations to accept keys of A-Z, a-z, 0-9, underscore and period, up to 64 characters in UTF-8. The characters {}()/@: are reserved and must not be supported; an illegal key throws the standard's InvalidArgumentException.
solid answer
~40 sPSR-16 copies PSR-6's key definition: a key is a string of at least one character, and every implementation must support keys made of `A-Z`, `a-z`, `0-9`, `_` and `.` in any order, in UTF-8, up to 64 characters. Implementations may accept more characters or longer keys, but portable code cannot rely on that. The characters `{}()/\@:` are reserved for future extensions and implementations must not support them. Passing an illegal key makes `getItem()`, `get()`, `set()` and friends throw `Psr\Cache\InvalidArgumentException` or `Psr\SimpleCache\InvalidArgumentException`. So a library should never build keys straight from URLs, e-mail addresses or namespaced class names: prefix a short fixed name and hash the variable part, for example `'rate.' . hash('xxh128', $url)`, which stays well under 64 characters.
code
php · 11 lines<?php
declare(strict_types=1);
function rateCacheKey(string $sourceUrl, string $pair): string
{
// 'rate.' + 32 hex chars + '.' + 6 letters = 44 characters, all legal
return 'rate.' . hash('xxh128', $sourceUrl) . '.' . preg_replace('/[^A-Z]/', '', strtoupper($pair));
}
echo rateCacheKey('https://rates.example/v1', 'eur/usd');
// rate.<32 hex characters>.EURUSDgo deeper
Remember the safe set, letters, digits, underscore and period, up to 64 characters, and that : / \ @ and brackets are off limits.
Explain which exception an illegal key raises in PSR-6 and PSR-16, and why implementations that accept more characters still make such keys non-portable.
Design a key builder for a reusable library that hashes variable parts, stays under 64 characters, and is covered by tests with hostile inputs.
Decide whether a codebase standardises one key-building helper for every package, trading a small shared dependency for consistent, portable keys.
## The shared key definition **PSR-6** defines a cache key, and **PSR-16** copies the definition word for word. A key is a string of at least one character that uniquely identifies a cached item, and: - implementations **must** support keys made of `A-Z`, `a-z`, `0-9`, `_` and `.`, in any order, in UTF-8, with a length of **up to 64 characters**; - implementations **may** support more characters, other encodings or longer keys, but at least that minimum; - implementations are responsible for escaping keys for their storage, but **must** be able to return the original, unmodified key (PSR-6's `CacheItemInterface::getKey()` does exactly that); - the characters `{}()/\@:` are **reserved** for future extensions and **must not** be supported. ## What happens with an illegal key Every method that takes a key declares that an illegal key **must** raise the standard's invalid-argument exception: | Standard | Methods | Exception interface | |---|---|---| | PSR-6 | `getItem()`, `getItems()`, `hasItem()`, `deleteItem()`, `deleteItems()` | `Psr\Cache\InvalidArgumentException` | | PSR-16 | `get()`, `set()`, `delete()`, `has()`, and the `*Multiple()` methods | `Psr\SimpleCache\InvalidArgumentException` | Both extend the standard's `CacheException` interface. This is the one exception a calling library should expect from normal use: PSR-6 asks implementations to trap storage errors rather than throw them, and PSR-16 reports failed writes and deletes as `false`, but an invalid key is the caller's bug, and it surfaces immediately. ## Keys that break in practice A library that builds keys from its inputs meets the reserved set quickly: 1. **URLs** contain `:` and `/`: `https://rates.example/v1` is illegal. 2. **E-mail addresses** contain `@`. 3. **Namespaced class names** contain `\`: `App\Rates\Provider` is illegal. 4. **Templates** such as `{pair}` use braces. 5. **Composite keys** such as `rate:EUR:USD` use colons, a separator many developers bring from key/value stores. Some implementations accept a few of these today, which is worse: the code works in development and throws when the application swaps in a stricter implementation. ## Building portable keys - Use a **short fixed prefix** per concern, with `.` or `_` as the separator: `rate.EURUSD`. - **Hash** any variable part that may contain other characters or grow long. `hash('xxh128', $url)` produces 32 hexadecimal characters, so `'rate.' . hash('xxh128', $url)` is 37 characters of legal ASCII. A 64-character `sha256` digest plus a prefix would exceed the guaranteed 64. - Keep the whole key within **64 characters**, even if your current backend allows more. - If you need to see the original input when debugging, log it next to the key; do not put it in the key. ## Why the reserved characters exist The specification only says the characters are reserved for future extensions. Reserving them means implementations cannot give them their own meanings, so a later standard could assign one without breaking conforming code. For a library author the consequence is simple: treat them as forbidden. ## Checklist for a framework-agnostic library - Validate or hash every externally influenced key part before it reaches the cache. - Write a unit test that feeds a URL and a namespaced class name through the key builder. - Do not catch `InvalidArgumentException` to hide it; fix the key builder instead. ## A note on the value side The key rules have a counterpart for values: both standards require implementations to return exactly what was stored, including its type, and to answer with a miss rather than corrupted data when that is impossible. Keys are strict so that values can be trusted.
- Why is 'rate.' . hash('sha256', $url) a risky PSR-16 key?A `sha256` hex digest is 64 characters, so with the prefix the key is 69. PSR-16 only guarantees support up to 64 characters, so an implementation that stops at the minimum may reject it with `Psr\SimpleCache\InvalidArgumentException`. A shorter digest such as `xxh128` (32 hex characters) keeps it portable.
- Should a library catch Psr\SimpleCache\InvalidArgumentException and skip caching?Not as a fix. That exception means the calling code built an illegal key, which is a bug in the library, not a storage outage. Catching it hides the bug and silently disables caching. Fix the key builder so every key is legal, and cover it with a test.
saying these in an interview costs you the question
- Uses colons in keys like rate:EUR:USD because key/value stores allow them.
- Assumes any string is a valid key if the current backend accepts it.
- Believes keys may be any length because storage handles it.
- Puts full URLs or class names straight into cache keys.
- Catches InvalidArgumentException from the cache and silently skips caching.