Tiger_Uuid

@api

UUID generation for Tiger primary keys.

WHY UUIDs (not auto-increment integers) for the substrate PKs:

  • Multi-tenant / distributed friendliness: IDs can be minted anywhere (app, CLI, an import job, another service) with no DB round-trip and no collision risk. Auto-increment forces a round-trip and leaks row counts.
  • Safe to expose in URLs/APIs: reveals nothing (no "org #5" enumeration).
  • Stable across environments: copy a row between dev/staging/prod and its identity is preserved.

WHY v7 IS THE DEFAULT (Tiger_Uuid::generate() -> v7):

  • Write performance / index locality. v4 is fully random, so every insert lands at a random spot in the PK B-tree -> page splits, fragmentation, poor cache behavior. v7's leading 48 bits are a millisecond timestamp, so inserts append near the right edge of the index (like auto-increment). You get UUID's benefits WITHOUT v4's write penalty.
  • Natural time-ordering. Rows sort chronologically by PK (ORDER BY id ~= ORDER BY created_at), and you can read a row's creation time off its ID (see timeOf()) with no join and no extra column.

WHEN TO USE v4 INSTEAD:

  • v7 embeds (leaks) its creation time in those first 48 bits. For anything where timing should be OPAQUE — session/invite/reset tokens, API keys — use v4. Rule of thumb: entities -> v7, secrets/tokens -> v4.

STORAGE: we store UUIDs as CHAR(36) canonical text (lowercase, hyphenated), not BINARY(16). Text is portable across every MySQL/MariaDB version and is human-readable in the console/logs (a big win when an AI or human is debugging). bin2hex() below already emits lowercase — the RFC-4122 canonical form — so IDs are lowercase by construction.

Methods

generate()

generate()

The default generator. Returns a v7 (time-ordered) UUID. Use this for entity primary keys unless you specifically need opaque timing (then v4()).

Returns string — 36-char lowercase canonical UUID

v7()

v7()

RFC-9562 version-7 (time-ordered) UUID.

Layout (128 bits): bits 0..47 : Unix time in milliseconds, big-endian (6 bytes) bits 48..51 : version = 0111 (7) bits 52..63 : rand_a (random) bits 64..65 : variant = 10 bits 66..127 : rand_b (random)

We fill the sub-millisecond bits with randomness (RFC "method 1"), which is sufficient for index locality and to-the-millisecond ordering. We do NOT guarantee strict monotonic ordering for two IDs minted within the same millisecond — that would need a counter and isn't worth the complexity here.

NOTE: assumes 64-bit PHP (universal on 8.1+); the ms timestamp fits in 48 bits until the year ~10889.

Returns string — 36-char lowercase canonical UUID

v4()

v4()

RFC-4122 version-4 (random) UUID. Use for tokens / IDs whose creation time must not be inferable. Uses random_bytes() (CSPRNG) so values are unguessable.

Returns string — 36-char lowercase canonical UUID

timeOf()

timeOf($uuid)

Extract the creation time embedded in a v7 UUID. Only meaningful for v7 (v4 has no timestamp — you'll get garbage). Demonstrates v7's payoff: the "when" is free, no created_at join required.

  • $uuid string — a v7 UUID

Returns float — Unix timestamp in seconds (millisecond precision)

isValid()

isValid($value)

Loosely validate a canonical UUID string (any version). Handy for guarding route params / API input before hitting the DB.

  • $value mixed — the value to test

Returns bool