Small · Sortable · Secure ID generator
This is the official PHP implementation. Fully compliant with v1.0.1.
Pikaid is a 26-character, lowercase Base36 identifier, composed of:
- 7-character timestamp (seconds since epoch)
- 19-character cryptographically secure randomness
It’s a modern, compact alternative to UUID and ULID:
- Lexicographically sortable
- Collision-resistant
- Compact binary form (
BINARY(17)
)
See the full technical specs and benchmarks at pikaid/pikaid-specs
- PHP 7.4+ (PHP 8 recommended)
ext-gmp
(recommended for best performance) orext-bcmath
(optional)- If neither extension is installed, a pure-PHP fallback is used.
Install via Composer:
composer require pikaid/pikaid-php
Include Composer's autoloader in your project:
require 'vendor/autoload.php';
<?php
use Pikaid\Pikaid;
// Generate a new Pikaid string
$id = Pikaid::generate();
echo "ID: $id\n"; // e.g. 0swct4q01ch83h91onl6l47vb6
// Validate
if (Pikaid::isValid($id)) {
echo "Valid ID!\n";
}
// Parse components
$data = Pikaid::parse($id);
echo "Timestamp: " . $data['timestamp']->format('Y-m-d H:i:s') . " UTC\n";
echo "Randomness (hex): {$data['randomness']}\n";
Generate a new 26-character string ID.
- Layout:
[7 chars timestamp][19 chars randomness]
- Sortable by second.
$id = Pikaid::generate();
Generate the binary form directly (BINARY(17)
).
- Layout:
[5 bytes timestamp (uint40, big-endian)][12 bytes entropy]
.
$bin = Pikaid::generateBinary();
Convert a string ID to its binary (17 bytes) representation.
Throws InvalidArgumentException
if the input is not a valid 26-character Pikaid or if the timestamp is out of range.
$bin = Pikaid::toBinary($id);
Convert binary (17 bytes) back into a 26-char string.
Throws InvalidArgumentException
if the binary length isn’t exactly 17 bytes.
$id = Pikaid::fromBinary($bin);
Check if the given string is a valid Pikaid.
- Must be 26 chars long
- Must match regex:
/^[0-9a-z]{26}$/
if (Pikaid::isValid($id)) {
echo "Valid format!";
}
Parse a string ID into its components:
timestamp
→DateTimeImmutable
(UTC)randomness
→ lowercase hex string (24 chars = 12 bytes)
Throws InvalidArgumentException
on invalid input.
$info = Pikaid::parse($id);
/*
[
'timestamp' => DateTimeImmutable(...),
'randomness' => 'a1b2c3d4e5f6a7b8c9d0e1f2'
]
*/
Generate a string ID for a specific timestamp (seconds precision).
$id = Pikaid::fromDateTime(new DateTimeImmutable('@1234567890'));
- String and binary representations sort lexicographically by second:
$id1 = Pikaid::generate();
sleep(1);
$id2 = Pikaid::generate();
assert($id1 < $id2); // always true
Pikaid is designed to be compact and index-friendly, with a predictable layout:
Representation | Size | Sortable | Recommended for |
---|---|---|---|
BINARY(17) | 17B | Yes | High-performance storage and indexing |
CHAR(26) | 26B | Yes | Readability in SQL, debugging, or logs |
Binary form stores exactly 17 bytes:
[5 bytes timestamp (uint40, big-endian)][12 bytes entropy]
CREATE TABLE pika_events (
id BINARY(17) NOT NULL, -- Primary key
payload JSON NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- Optional: Extract timestamp for range queries and indexing
ts_seconds BIGINT UNSIGNED
AS ( ORD(SUBSTR(id, 1, 1)) * 4294967296
+ CONV(HEX(SUBSTR(id, 2, 4)), 16, 10) ) STORED,
PRIMARY KEY (id),
KEY idx_ts_seconds (ts_seconds),
KEY idx_created_at (created_at)
) ENGINE=InnoDB;
use Pikaid\Pikaid;
// Generate binary ID and store it
$binId = Pikaid::generateBinary();
$stmt = $pdo->prepare('INSERT INTO pika_events (id, payload) VALUES (?, ?)');
$stmt->execute([$binId, json_encode(['event' => 'signup'])]);
$stmt = $pdo->query('SELECT id, ts_seconds FROM pika_events ORDER BY id ASC');
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$stringId = Pikaid::fromBinary($row['id']); // Convert back to string
echo $stringId . ' @ ' . gmdate('c', (int)$row['ts_seconds']) . PHP_EOL;
}
- Small index size = better performance
- Binary comparison matches chronological order
- Perfect for
PRIMARY KEY
or clustered indexes
If you need IDs to remain human-readable directly in SQL queries, store the string form.
CREATE TABLE pika_events_str (
id CHAR(26) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, -- Primary key
payload JSON NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- Optional: Extract timestamp for range queries and indexing
ts_seconds BIGINT UNSIGNED
AS (CONV(SUBSTR(id, 1, 7), 36, 10)) STORED,
PRIMARY KEY (id),
KEY idx_ts_seconds (ts_seconds),
KEY idx_created_at (created_at)
) ENGINE=InnoDB;
use Pikaid\Pikaid;
$id = Pikaid::generate();
$stmt = $pdo->prepare('INSERT INTO pika_events_str (id, payload) VALUES (?, ?)');
$stmt->execute([$id, json_encode(['event' => 'login'])]);
$stmt = $pdo->query('SELECT id, ts_seconds FROM pika_events_str ORDER BY id ASC');
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
echo $row['id'] . ' @ ' . gmdate('c', (int)$row['ts_seconds']) . PHP_EOL;
}
- Slightly larger storage and index compared to
BINARY(17)
- Ideal for debugging or manual inspection of IDs
Both BINARY(17)
and CHAR(26)
maintain natural chronological order:
$id1 = Pikaid::generate();
sleep(1);
$id2 = Pikaid::generate();
assert($id1 < $id2); // String order is chronological
assert(strcmp(Pikaid::toBinary($id1), Pikaid::toBinary($id2)) < 0); // Binary order too
Pikaid is released under the MIT License.