northrook / hashes
Crockford Base32 hashing utilities for short, URL-safe identifiers, checksums, and time-sortable prefixes
Requires
- php: >=8.5
- ext-hash: *
- ext-random: *
Requires (Dev)
- northrook/php-cs: dev-main
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13
This package is auto-updated.
Last update: 2026-08-12 05:44:11 UTC
README
Crockford Base32 hashing utilities for short, URL-safe identifiers, checksums, and time-sortable prefixes.
Dual package: PHP (northrook/hashes) and TypeScript (@northrook/hashes).
Checksums
checksum is the low 60 bits of xxHash64 as 12 Crockford Base32 characters — same digest bytes as value / native hash('xxh64', ...).
Strings are hashed as UTF-8 bytes. PHP strings are used as-is (UTF-8 for text; arbitrary bytes for binary). TypeScript encodes strings with TextEncoder; use Uint8Array for raw bytes.
No Unicode normalization is applied: NFC and NFD forms of the same character produce different digests.
Parity
Guaranteed cross-language parity is only for digests of shared input: PHP checksum / value (and native hash('xxh32'|'xxh64', …) in fixtures) ↔ TypeScript getChecksum / getHash / getXXH32 / getXXH64. Same UTF-8/bytes in, same Crockford or hex out. Locked by shared fixtures; not the TypeScript streaming XXHash32 / XXHash64 classes (PHP has no streaming hasher API — use hash()).
crypto / fast / time / ulid match style and API (Crockford alphabet, length bounds, method shapes, seed conventions) — not bit-identical outputs. RNG and clocks differ; JS also cannot mirror PHP’s int-vs-float time seed split exactly.
fixtures/digest-parity.json— golden vectors (seefixtures/README.md)- PHP:
vendor/bin/phpunit(ParityTest,DigestTest, …) - TypeScript:
npm run test:parity(plustest:xxhash/test:time/test:fast/test:crypto/test:ulid)
Regenerate goldens from PHP’s native xxHash after intentional digest-format changes:
php bin/generate-digest-parity.php
PHP
use Northrook\Hash; Hash::checksum('hello'); // 12-char Crockford (xxh64) Hash::value('hello', 6); // truncated Crockford Hash::time(); // time-sortable prefix (int ms / float seconds) Hash::ulid(); // time(10) + crypto(16) — 26-char ULID shape Hash::fast(); // mt_rand Crockford (best-effort session uniqueness) Hash::reset(); // clear fast() session map Hash::crypto(); // CSPRNG Crockford hash('xxh64', 'hello'); // raw hex — use native hash(), not Hash:: hash('xxh32', 'hello');
Requires ext-hash and ext-random, PHP >= 8.5.
fast uniqueness is best-effort (in-memory map + short retry budget), not absolute — tiny lengths can still warn-and-return a duplicate so runtime cannot loop. Call reset / resetFastHash from long-lived processes when the map should not grow unbounded.
TypeScript
Free functions plus a Hash facade mirroring the PHP method names:
import {getChecksum, Hash} from "@northrook/hashes"; getChecksum("hello"); Hash.checksum("hello"); Hash.xxh64("hello"); Hash.value("hello", 6); Hash.time(); // integer seed = ms; fractional = seconds Hash.ulid(); // time(10) + crypto(16) — 26-char ULID shape Hash.fast(); // Math.random Crockford (best-effort session uniqueness) Hash.reset(); // clear getFastHash session map
Browser / Electron oriented. Digests accept string (UTF-8) or Uint8Array.
XXHash32 / XXHash64 are TypeScript-only streaming hashers (used under getXXH32 / getXXH64). Prefer Uint8Array chunks when streaming: each string chunk is UTF-8-encoded independently, so splitting a surrogate pair across updates is not byte-equivalent to hashing the full string.
time / getTimeHash seed: integer → milliseconds; non-integer → seconds (floor(seed * 1000)), approximating PHP’s int/float split (style parity only — see Parity).