Public API reference
This reference describes Solo Tokens 1.0: PHP 8.3+, psr/clock 1.0 and the Solo\Tokens namespace. Getting started walks through the setup, and Behavior in detail explains the rules below.
The public API consists of six types. The stores and Token are final; behavior is extended by implementing TokenStore, not by inheritance. Every parameter that carries a secret is marked #[\SensitiveParameter]. Exception messages are not part of the compatibility promise.
| Type | Role |
|---|---|
TokenStore | Tokens of one kind: issue, find, consume and revoke |
RedisTokenStore | The store every process shares, on Redis through phpredis |
InMemoryTokenStore | The same contract in process memory, for tests and single-process tools |
Token | What a store knows about a live token |
Secret | Makes and hashes secrets, for tokens kept in your own tables |
Exception\TokenStoreException | Thrown when a store fails or a stored record is damaged |
TokenStore
issue()
/** @param array<mixed> $data */
public function issue(string $subject, int $ttlSeconds, array $data = []): stringStores a token for $subject that lives $ttlSeconds, and returns its secret: 64 lowercase hex characters, 32 random bytes. Only the secret's SHA-256 is stored, with the subject, the data and the moment the token ends.
Throws InvalidArgumentException, before storing anything, when the subject is empty, the lifetime is outside 1 to 2_147_483_647 seconds, or the data does not encode as JSON within 512 levels. Throws TokenStoreException when the store fails.
find()
public function find(string $secret): ?TokenThe live token behind $secret, which stays usable, or null when there is none or the value cannot be a secret. Throws TokenStoreException when the store fails or the token's record is damaged; a damaged record stays in place.
consume()
public function consume(string $secret): ?TokenThe live token behind $secret, removed in the same step, or null. Of concurrent calls with the same secret, exactly one gets the token. A damaged record is removed as well, then reported with TokenStoreException.
revoke()
public function revoke(string $secret): voidEnds the token behind $secret, a damaged one included. A missing token or a value that cannot be a secret is ignored. Throws TokenStoreException when the store fails.
revokeAll()
public function revokeAll(string $subject): voidEnds every token issued for $subject. A subject without tokens is ignored. Throws InvalidArgumentException for an empty subject and TokenStoreException when the store fails.
RedisTokenStore
public function __construct(\Redis $redis, string $prefix)Keeps tokens in Redis through phpredis. Each operation is one Lua script, run on the Redis clock. The connection must be in its normal, immediate mode, without OPT_SERIALIZER or OPT_COMPRESSION; a client OPT_PREFIX comes before $prefix. Requires Redis 5.0 or later, on a single server with or without replicas, not Redis Cluster. See Storage on Redis.
InMemoryTokenStore
public function __construct(ClockInterface $clock = new SystemClock())
public function purgeExpired(): intKeeps tokens per instance. The clock decides when a token ends; omit it to use system time. issue() drops expired tokens periodically, and purgeExpired() drops them at once and returns how many it removed.
Token
final readonly class Token
{
/** @param array<mixed> $data */
public function __construct(
public string $subject,
public array $data,
public \DateTimeImmutable $expiresAt,
)
}expiresAt is the moment the token ends. On Redis it comes from the Redis clock, in PHP's default time zone; in memory it comes from the store's clock, in that clock's time zone.
Secret
public const int MIN_BYTES = 32;
public const int MAX_BYTES = 64;
public static function generate(int $bytes = self::MIN_BYTES): string
public static function hash(string $secret): ?stringgenerate() returns $bytes random bytes in lowercase hex, so 64 to 128 characters; another length throws InvalidArgumentException. hash() returns the SHA-256 in hex, or null when the value is not a secret generate() could have made: a wrong length, an odd number of characters, upper case or anything but hex. The stores use both.
TokenStoreException
Solo\Tokens\Exception\TokenStoreException extends RuntimeException. It is thrown when Redis fails, when the connection is in a mode the store refuses, when a script replies with something unexpected, and when a stored record is damaged. If phpredis threw RedisException, it is available via getPrevious(). The secret never appears in its message.