joby / tid
A lightweight time-ordered random ID system designed for human-scale applications.
Requires
- ext-ctype: *
Requires (Dev)
- phpunit/phpunit: ^12.3
This package is auto-updated.
Last update: 2025-08-04 17:06:50 UTC
README
A simple and lightweight time-ordered random ID library designed for human-scale applications.
What is php-tid?
php-tid is a simple, lightweight library for generating unique, time-ordered, human-readable IDs. Unlike UUIDs or auto-incrementing database IDs, Tids (Time-ordered IDs) are:
- Human-readable: Formatted with optional dashes for better readability (e.g., "abcd-efgh")
- Time-ordered: Can be represented as integers or strings, and both are naturally sortable by rough creation time
- Compact: Uses alphanumeric encoding for concise representation of the underlying integer (they are currently eight characters, but will expand slowly as time progresses)
- URL-safe: No special characters that are hard to type or need encoding in URLs
- Privacy-conscious: Drops timestamp precision to avoid leaking exact creation times
- Ergonomic: Strings can be easily converted from the concise format back into the underlying integer, using dashes or not for readability
Why php-tid?
Not every project needs globally unique IDs or distributed systems. For many smaller applications, simpler solutions are often better:
- Human scale: Designed for applications where IDs might be seen, shared, or even typed by humans
- Simplicity: No external dependencies or complex setup
- Lightweight: Minimal overhead and easy integration
- Chronological: Natural time-based ordering at a resolution of about 72 hours, without revealing exact timestamps
Installation
composer require joby/tid
Basic Usage
Creating a new Tid
use Joby\Tid\Tid; // Generate a new Tid $tid = new Tid(); echo $tid; // Outputs something like "abcd-efgh" // Get the compact representation (without dashes) echo $tid->compactString(); // Outputs something like "abcdefgh"
Creating a Tid from an existing string
use Joby\Tid\Tid; // Create a Tid from a string $tid = Tid::fromString("abcd-efgh"); // or $tid = Tid::fromString("abcdefgh"); // Dashes are optional when parsing
Working with timestamps
use Joby\Tid\Tid; $tid = new Tid(); // Get the approximate timestamp when this Tid was created $timestamp = $tid->time(); echo date('Y-m-d H:i:s', $timestamp); // Get the entropy bits (random portion) of the Tid $entropy = $tid->entropy();
Validation
use Joby\Tid\TidHelper; // Validate a Tid string if (TidHelper::validateString("abcd-efgh")) { echo "Valid Tid string!"; } // Validate a Tid integer $int = TidHelper::toInt("abcd-efgh"); if (TidHelper::validateInt($int)) { echo "Valid Tid integer!"; }
Advanced Usage
Using the underlying integer
use Joby\Tid\Tid; use Joby\Tid\TidHelper; // Create a Tid $tid = new Tid(); // Get the underlying integer $int = $tid->id; // Convert back to a Tid $sameTid = new Tid($int); // or $sameTid = Tid::fromInt($int);
Serialization
Tid objects can be serialized and unserialized:
use Joby\Tid\Tid; $tid = new Tid(); $serialized = serialize($tid); $unserialized = unserialize($serialized); echo $tid === $unserialized; // false (different objects) echo $tid->id === $unserialized->id; // true (same ID value)
Using with databases
Tids can be stored in your database as either strings or integers:
// Store as a string (more readable) $db->query("INSERT INTO users (id, name) VALUES (?, ?)", [(string)$tid, "John"]); // Store as an integer (more efficient) $db->query("INSERT INTO users (id, name) VALUES (?, ?)", [$tid->id, "John"]);
How It Works
Each Tid consists of a single integer value with two parts:
- A timestamp component (with 18 precision bits dropped for privacy and to make room for entropy)
- Random entropy bits to ensure uniqueness
The combination is encoded in base-36 (alphanumeric) and formatted with dashes for readability.
Limitations
- Not designed or suitable for distributed systems requiring guaranteed global uniqueness
- Time ordering is approximate due to the dropped precision bits
- No built-in collision detection (though collisions are extremely unlikely at human scale applications)
Collision probability
The time portion of a Tid resets every 2^18 seconds, which is roughly 72 hours. There are 32 bits of entropy. This leaves 2^14 Tids available per second. So the odds of any given Tid colliding can be calculated by the frequency they are being generated by in your app:
Frequency of generation | Odds of collison (1 in) | Odds of collision in 72h |
---|---|---|
1/second | 16,384 | ~100% |
1/minute | 983,040 | ~0.4% |
1/hour | ~59 million | ~0.00006% |
1/day | ~1.4 billion | ~0.0000001% |
1/week | ∞ | for intervals over 72h collisions are impossible |
A rough rule of thumb is that to preserve better than one in a million odds of collisions per 72 hour window, you shouldn't use TIDs for systems generating more than about 1.3 IDs per hour. So it's perfectly acceptable for things like blog posts on personal sites, or even microblogging on a small scale. If you implement collision detection in your app they're even potentially viable for higher-volume things. They would never really be appropriate for things like logging though, where there might be thousands of entries being generated per hour or more.