{"id":37012620,"url":"https://github.com/inanepain/id-forge","last_synced_at":"2026-01-14T01:12:27.687Z","repository":{"id":321430323,"uuid":"1085806555","full_name":"inanepain/id-forge","owner":"inanepain","description":"A lightweight, versatile PHP library for generating and encoding unique identifiers. It supports base32, base58, base64, nanoid, snowflakeid, uuid, and ulid, providing fast, secure, and flexible solutions for ID generation and encoding in modern applications.","archived":false,"fork":false,"pushed_at":"2025-10-29T14:38:32.000Z","size":1223,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2025-10-29T16:54:16.480Z","etag":null,"topics":["base32","base58","base64","cryptography","encoding","id-forge","id-generation","inanepain","library","nanoid","php","snowflakeid","ulid","unique-identifier","uuid"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inanepain.png","metadata":{"files":{"readme":"README.adoc","changelog":"CHANGELOG.adoc","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-29T14:36:50.000Z","updated_at":"2025-10-29T14:44:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"141355bd-e867-4410-a54c-a7c3323ab4db","html_url":"https://github.com/inanepain/id-forge","commit_stats":null,"previous_names":["inanepain/id-forge"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/inanepain/id-forge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inanepain%2Fid-forge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inanepain%2Fid-forge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inanepain%2Fid-forge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inanepain%2Fid-forge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inanepain","download_url":"https://codeload.github.com/inanepain/id-forge/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inanepain%2Fid-forge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28407658,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T00:40:43.272Z","status":"ssl_error","status_checked_at":"2026-01-14T00:40:42.636Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["base32","base58","base64","cryptography","encoding","id-forge","id-generation","inanepain","library","nanoid","php","snowflakeid","ulid","unique-identifier","uuid"],"created_at":"2026-01-14T01:12:26.939Z","updated_at":"2026-01-14T01:12:27.678Z","avatar_url":"https://github.com/inanepain.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"= id-forge\n:author: Philip Michael Raab\n:email: \u003cphilip@cathedral.co.za\u003e\n:keywords: inanepain, library, unique identifier, id generation, base32, base58, base64, nanoid, snowflakeid, uuid, ulid, php, encoding, cryptography\n:description: A lightweight, versatile PHP library for generating and encoding unique identifiers. It supports base32, base58, base64, nanoid, snowflakeid, uuid, and ulid, providing fast, secure, and flexible solutions for ID generation and encoding in modern applications.\n:revnumber: 0.1.0\n:revdate: 2025-10-29\n:copyright: Unlicense\n:experimental:\n:doctype: book\n:hide-uri-scheme:\n:icons: font\n:source-highlighter: highlight.js\n:toc: left\n:sectanchors:\n:idprefix: topic-\n:idseparator: -\n:pkg-vendor: inanepain\n:pkg-name: id-forge\n:pkg-id: {pkg-vendor}/{pkg-name}\n\n== image:./icon.png[title={pkg-id},25] {pkg-id}\n\n{description}\n\n- Reversible encoders (`Base32`, `Base58`, `Base64`) via a common `EncoderInterface`\n- Several ID generators (`UUIDv4`, `ULID`, `Nanoid`, Snowflake-like IDs) via a common `IdGeneratorInterface`\n- Simple configuration of objects and factory helpers\n\nTIP: All examples target PHP 8.2+ (the codebase is PHP 8.4-ready). Namespaces are rooted under `Inane\\IdForge`.\n\n\u003c\u003c\u003c\n\n:leveloffset: +1\n\n= Install\n:author: Philip Michael Raab\n:email: \u003cphilip@cathedral.co.za\u003e\n:experimental:\n:hide-uri-scheme:\n:icons: font\n:source-highlighter: highlight.js\n:sectnums: |,all|\n:toc: auto\n:sectanchors:\n\n[source,shell]\n====\n$ composer require {pkg-id}\n====\n\n:leveloffset!:\n\n\u003c\u003c\u003c\n\n== Quickstart\n\n.Generate a few IDs\n[source,php]\n----\nuse Inane\\IdForge\\IdGeneratorFactory;\nuse Inane\\IdForge\\EncoderFactory;\n\n$uuid    = IdGeneratorFactory::createUUID()-\u003egenerate();\n$ulid    = IdGeneratorFactory::createULID()-\u003egenerate();\n$nanoid  = IdGeneratorFactory::createNanoid()-\u003egenerate();\n$snow    = IdGeneratorFactory::createSnowflake(1, 2)-\u003egenerate();\n\n$base58  = EncoderFactory::createBase58();\n$encoded = $base58-\u003eencode($ulid);\n$decoded = $base58-\u003edecode($encoded);\n----\n\n== Modules\n\n:leveloffset: +1\n\n= Encoders\n:author: Philip Michael Raab\n:email: \u003cphilip@cathedral.co.za\u003e\n:experimental:\n:hide-uri-scheme:\n:icons: font\n:source-highlighter: highlight.js\n:sectnums: |,all|\n:toc: auto\n:sectanchors:\n\nThis module provides reversible string encoders that share a simple contract, `EncoderInterface`. Implementations cover Base32, Base58, and Base64 (including a URL-safe variant).\n\n== Contracts and base classes\n\n=== `Inane\\IdForge\\Interface\\EncoderInterface`\n\nContract for reversible string encoders:\n\n- `encode(string $data): string` — converts binary-safe input to an encoded string\n- `decode(string $data): string` — converts an encoded string back to the original binary data\n\nImplementations must be deterministic and satisfy `decode(encode($x)) === $x` for valid input. Invalid input should raise `Inane\\Stdlib\\Exception\\InvalidArgumentException` (or a domain-specific exception).\n\n=== `Inane\\IdForge\\Encoder\\AbstractEncoder`\n\nA small base class that stores an `EncoderConfig` and exposes helpers:\n\n- `getAlphabet(): string`\n- `getAlphabetLength(): int`\n\nConcrete encoders (Base32/58/64) extend this class.\n\n== Implementations\n\n=== Base32\n\nNamespace: `Inane\\IdForge\\Encoder\\Base32Encoder`\n\n- Alphabet: configurable (RFC 4648 by default when using `EncoderFactory::createBase32()`)\n- Padding: not applied; trailing zero bits are used to complete the last 5-bit group\n- Errors: throws `InvalidArgumentException` if a character is not in the alphabet during `decode()`\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\EncoderFactory;\n\n$base32 = EncoderFactory::createBase32();\n$enc = $base32-\u003eencode(\"\\x00\\xFFHello\");\n$bin = $base32-\u003edecode($enc);\n----\n\n=== Base58\n\nNamespace: `Inane\\IdForge\\Encoder\\Base58Encoder`\n\n- Alphabet: configurable (Bitcoin alphabet via `EncoderFactory::createBase58()`)\n- Preserves leading zero bytes as leading first-alphabet characters\n- Errors: throws `InvalidArgumentException` for unknown characters during `decode()`\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\EncoderFactory;\n\n$base58 = EncoderFactory::createBase58();\n$enc = $base58-\u003eencode(\"\\0\\0payload\");\n$bin = $base58-\u003edecode($enc);\n----\n\n=== Base64\n\nNamespace: `Inane\\IdForge\\Encoder\\Base64Encoder`\n\n- Standard Base64 via `encode()`/`decode()`\n- URL-safe helpers: `urlEncode()` replaces `+`/`/` with `-`/`_` and strips padding; `urlDecode()` restores and decodes\n- Errors: `decode()` and `urlDecode()` throw `InvalidArgumentException` for invalid Base64 input\n\n.Usage (URL-safe)\n[source,php]\n----\nuse Inane\\IdForge\\EncoderFactory;\n\n$base64 = EncoderFactory::createBase64();\n$token  = $base64-\u003eurlEncode(random_bytes(16));\n$bytes  = $base64-\u003eurlDecode($token);\n----\n\n== Configuration\n\nEncoders accept `Inane\\IdForge\\Config\\EncoderConfig` which holds:\n\n- `alphabet: string`\n- `alphabetLength: int` (precomputed)\n\nUse `EncoderFactory` for sensible defaults or construct encoders manually with a custom alphabet.\n\n== Exceptions\n\n- `Inane\\Stdlib\\Exception\\InvalidArgumentException` — invalid input during `decode()`\n\n== See also\n\n- xref:config.adoc[Configuration]\n- xref:factories.adoc[Factories]\n\n:leveloffset!:\n\n\u003c\u003c\u003c\n\n:leveloffset: +1\n\n= Generators\n:author: Philip Michael Raab\n:email: \u003cphilip@cathedral.co.za\u003e\n:experimental:\n:hide-uri-scheme:\n:icons: font\n:source-highlighter: highlight.js\n:sectnums: |,all|\n:toc: auto\n:sectanchors:\n\nIdForge includes several generators that implement a common contract, `IdGeneratorInterface`. Each generator focuses on a different trade-off: interoperability (UUID), sortability (ULID, Snowflake), or brevity and URL-friendliness (Nanoid).\n\n== Contract and base class\n\n=== `Inane\\IdForge\\Interface\\IdGeneratorInterface`\n\n- `generate(): string` — create a new identifier as a string\n\nImplementations should be fast, low-collision, and safe to use concurrently.\n\n=== `Inane\\IdForge\\Generator\\AbstractIdGenerator`\n\nProvides helpers shared by all generators:\n\n- `getRandomBytes(int $length): string` — cryptographically secure random bytes (can throw `Random\\RandomException`)\n- `getTimestamp(): int` — current UNIX timestamp in milliseconds\n\n== Implementations\n\n=== UUIDv4\n\nNamespace: `Inane\\IdForge\\Generator\\UUIDGenerator`\n\n- RFC 4122 UUID version 4\n- Format: canonical 36-char string `8-4-4-4-12`\n- Helpers:\n  - `isValid(string $uuid): bool`\n  - `toBase64(string $uuid, Base64Encoder $base64): string` — URL-safe Base64 (no padding)\n  - `fromBase64(string $b64, Base64Encoder $base64): string` — back to canonical string\n- Errors: `toBase64()` throws `InvalidArgumentException` for invalid input\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\IdGeneratorFactory;\nuse Inane\\IdForge\\EncoderFactory;\n\n$uuid = IdGeneratorFactory::createUUID()-\u003egenerate();\n$base64 = EncoderFactory::createBase64();\n$b64 = IdGeneratorFactory::createUUID()-\u003etoBase64($uuid, $base64);\n$uuid2 = IdGeneratorFactory::createUUID()-\u003efromBase64($b64, $base64);\n----\n\n=== ULID\n\nNamespace: `Inane\\IdForge\\Generator\\ULIDGenerator`\n\n- 26-char Crockford Base32, lexicographically sortable\n- Structure: 48-bit timestamp + 80 bits randomness\n- Monotonic mode ensures strict ordering within the same millisecond\n- Key methods:\n  - `__construct(?EncoderConfig $config = null, bool $monotonic = false)`\n  - `generate(?int $timestamp = null): string`\n  - `decodeTimestamp(string $ulid): int`\n  - `decode(string $ulid): array{timestamp:int, random:string}`\n  - `toEncoded(EncoderInterface $encoder): string`\n- Errors: `InvalidArgumentException` for bad characters/length; `Random\\RandomException` for entropy issues\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\IdGeneratorFactory;\n\n$ulid = IdGeneratorFactory::createULID()-\u003egenerate();\n----\n\n.Monotonic ULID\n[source,php]\n----\nuse Inane\\IdForge\\Generator\\ULIDGenerator;\nuse Inane\\IdForge\\Config\\EncoderConfig;\n\n$mono = new ULIDGenerator(new EncoderConfig('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), true);\n$a = $mono-\u003egenerate();\n$b = $mono-\u003egenerate(); // guaranteed $a \u003c $b when in same ms\n----\n\n=== Nanoid\n\nNamespace: `Inane\\IdForge\\Generator\\NanoidGenerator`\n\n- Short, URL-friendly random IDs\n- Constructor: `__construct(string $alphabet = '0-9a-zA-Z', int $size = 21)`\n- `generate(): string`\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\IdGeneratorFactory;\n\n$id = IdGeneratorFactory::createNanoid()-\u003egenerate();\n----\n\n=== Snowflake-like\n\nNamespace: `Inane\\IdForge\\Generator\\SnowflakeIdGenerator`\n\n- 64-bit composed numeric ID (as a string): timestamp + datacenter + worker + sequence\n- Configured via `SnowflakeConfig` (epoch, bit allocations for worker/datacenter/sequence)\n- Methods:\n  - `__construct(int $workerId = 0, int $datacenterId = 0, ?SnowflakeConfig $config = null)`\n  - `generate(): string`\n  - `toEncoded(EncoderInterface $encoder): string`\n- Behavior: on sequence overflow within the same millisecond, waits for the next millisecond\n- Errors: `RuntimeException` if the clock moves backwards; `InvalidArgumentException` for out-of-range worker/datacenter\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\IdGeneratorFactory;\n\n$gen = IdGeneratorFactory::createSnowflake(workerId: 1, datacenterId: 2);\n$id  = $gen-\u003egenerate();\n----\n\n== See also\n\n- xref:config.adoc[Configuration]\n- xref:factories.adoc[Factories]\n- xref:encoders.adoc[Encoders]\n\n:leveloffset!:\n\n\u003c\u003c\u003c\n\n:leveloffset: +1\n\n= Configuration\n:author: Philip Michael Raab\n:email: \u003cphilip@cathedral.co.za\u003e\n:experimental:\n:hide-uri-scheme:\n:icons: font\n:source-highlighter: highlight.js\n:sectnums: |,all|\n:toc: auto\n:sectanchors:\n\nThis module documents the small configuration objects that accompany encoders and generators.\n\n== EncoderConfig\n\nNamespace: `Inane\\IdForge\\Config\\EncoderConfig`\n\nHolds the alphabet used by encoders and caches its length.\n\n.Fields\n\n- `alphabet: string` — characters used by the encoding\n- `alphabetLength: int` — cached length of the alphabet\n\n.API\n\n- `__construct(string $alphabet)` — set the alphabet and precompute its length\n- `getAlphabet(): string`\n- `getAlphabetLength(): int`\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\Config\\EncoderConfig;\nuse Inane\\IdForge\\Encoder\\Base32Encoder;\n\n$config  = new EncoderConfig('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567');\n$encoder = new Base32Encoder($config);\n----\n\n== SnowflakeConfig\n\nNamespace: `Inane\\IdForge\\Config\\SnowflakeConfig`\n\nControls the epoch and bit allocations for Snowflake-like IDs.\n\n.Fields\n\n- `epoch: int` — custom epoch in milliseconds (default: 1609459200000, 2021-01-01)\n- `workerIdBits: int` — bits for worker/node id (default: 5)\n- `datacenterIdBits: int` — bits for datacenter id (default: 5)\n- `sequenceBits: int` — bits for per-millisecond sequence (default: 12)\n\n.API\n\n- `__construct(int $epoch = 1609459200000, int $workerIdBits = 5, int $datacenterIdBits = 5, int $sequenceBits = 12)`\n- Getters:\n  - `getEpoch(): int`\n  - `getWorkerIdBits(): int`\n  - `getDatacenterIdBits(): int`\n  - `getSequenceBits(): int`\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\Generator\\SnowflakeIdGenerator;\nuse Inane\\IdForge\\Config\\SnowflakeConfig;\n\n$config = new SnowflakeConfig(epoch: 1700000000000, workerIdBits: 6, datacenterIdBits: 4, sequenceBits: 12);\n$gen    = new SnowflakeIdGenerator(workerId: 3, datacenterId: 1, config: $config);\n$id     = $gen-\u003egenerate();\n----\n\n== See also\n\n- xref:encoders.adoc[Encoders]\n- xref:generators.adoc[Generators]\n- xref:factories.adoc[Factories]\n\n:leveloffset!:\n\n\u003c\u003c\u003c\n\n:leveloffset: +1\n\n= Factories\n:author: Philip Michael Raab\n:email: \u003cphilip@cathedral.co.za\u003e\n:experimental:\n:hide-uri-scheme:\n:icons: font\n:source-highlighter: highlight.js\n:sectnums: |,all|\n:toc: auto\n:sectanchors:\n\nFactory helpers provide convenient, opinionated constructors for common encoders and generators.\n\n== EncoderFactory\n\nNamespace: `Inane\\IdForge\\EncoderFactory`\n\nCreates encoder instances with sensible default alphabets.\n\n.API\n\n- `createBase32(): Base32Encoder` — RFC 4648 alphabet `A-Z2-7`\n- `createBase58(): Base58Encoder` — Bitcoin alphabet (no `0`, `O`, `I`, `l`)\n- `createBase64(): Base64Encoder` — Standard Base64 alphabet\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\EncoderFactory;\n\n$base32 = EncoderFactory::createBase32();\n$base58 = EncoderFactory::createBase58();\n$base64 = EncoderFactory::createBase64();\n----\n\n== IdGeneratorFactory\n\nNamespace: `Inane\\IdForge\\IdGeneratorFactory`\n\nCreates generator instances with defaults and safe validation where applicable.\n\n.API\n\n- `createNanoid(string $alphabet = '0-9a-zA-Z', int $size = 21): NanoidGenerator`\n- `createSnowflake(int $workerId = 0, int $datacenterId = 0, ?SnowflakeConfig $config = null): SnowflakeIdGenerator`\n- `createUUID(): UUIDGenerator`\n- `createULID(?EncoderConfig $config = null): ULIDGenerator`\n\n.Usage\n[source,php]\n----\nuse Inane\\IdForge\\IdGeneratorFactory;\n\n$uuid   = IdGeneratorFactory::createUUID()-\u003egenerate();\n$ulid   = IdGeneratorFactory::createULID()-\u003egenerate();\n$nanoid = IdGeneratorFactory::createNanoid()-\u003egenerate();\n$snow   = IdGeneratorFactory::createSnowflake(workerId: 1, datacenterId: 2)-\u003egenerate();\n----\n\n== See also\n\n- xref:encoders.adoc[Encoders]\n- xref:generators.adoc[Generators]\n- xref:config.adoc[Configuration]\n\n:leveloffset!:\n\n\u003c\u003c\u003c\n\n:leveloffset: +1\n\n= Example\n:author: Philip Michael Raab\n:email: \u003cphilip@cathedral.co.za\u003e\n:experimental:\n:hide-uri-scheme:\n:icons: font\n:source-highlighter: highlight.js\n:sectnums: |,all|\n:toc: auto\n:sectanchors:\n\n.Some examples\n[source,php]\n----\nuse Inane\\IdForge\\Config\\EncoderConfig;\nuse Inane\\IdForge\\Config\\SnowflakeConfig;\nuse Inane\\IdForge\\Encoder\\AbstractEncoder;\nuse Inane\\IdForge\\EncoderFactory;\nuse Inane\\IdForge\\Generator\\AbstractIdGenerator;\nuse Inane\\IdForge\\IdGeneratorFactory;\n\n// Example usage\ntry {\n\t// Create encoders via factory\n\t$base32 = EncoderFactory::createBase32();\n\t$base58 = EncoderFactory::createBase58();\n\t$base64 = EncoderFactory::createBase64();\n\n\t// Create ID generators via factory\n\t$nanoid = IdGeneratorFactory::createNanoid();\n\t$snowflake = IdGeneratorFactory::createSnowflake(1, 1);\n\t$uuid = IdGeneratorFactory::createUUID();\n\t$ulid = IdGeneratorFactory::createULID();\n\n\t// Base32\n\t$base32Encoded = $base32-\u003eencode('Hello');\n\techo \"Base32 Encoded: $base32Encoded\\n\";\n\techo 'Base32 Decoded: ' . $base32-\u003edecode($base32Encoded) . \"\\n\";\n\techo PHP_EOL;\n\n\t// Base58\n\t$base58Encoded = $base58-\u003eencode('Hello');\n\techo \"Base58 Encoded: $base58Encoded\\n\";\n\techo 'Base58 Decoded: ' . $base58-\u003edecode($base58Encoded) . \"\\n\";\n\techo PHP_EOL;\n\n\t// Base64\n\t$base64Encoded = $base64-\u003eurlEncode('Hello');\n\techo \"Base64 URL Encoded: $base64Encoded\\n\";\n\techo 'Base64 URL Decoded: ' . $base64-\u003eurlDecode($base64Encoded) . \"\\n\";\n\techo PHP_EOL;\n\n\t// Nanoid\n\techo 'Nanoid: ' . $nanoid-\u003egenerate() . \"\\n\";\n\techo PHP_EOL;\n\n\t// Snowflake ID\n\t$snowflakeId = $snowflake-\u003egenerate();\n\techo \"Snowflake ID: $snowflakeId\\n\";\n\techo 'Snowflake ID (Base58): ' . $snowflake-\u003etoEncoded($base58) . \"\\n\";\n\techo PHP_EOL;\n\n\t// UUID\n\t$uuidValue = $uuid-\u003egenerate();\n\techo \"UUID: $uuidValue\\n\";\n\t$uuidBase64 = $uuid-\u003etoBase64($uuidValue, $base64);\n\techo \"UUID Base64: $uuidBase64\\n\";\n\techo 'UUID from Base64: ' . $uuid-\u003efromBase64($uuidBase64, $base64) . \"\\n\";\n\techo PHP_EOL;\n\n\t// ULID\n\t$ulidValue = $ulid-\u003egenerate();\n\techo \"ULID: $ulidValue\\n\";\n\techo 'ULID Timestamp: ' . $ulid-\u003edecodeTimestamp($ulidValue) . \"\\n\";\n\techo 'ULID Base32: ' . $ulid-\u003etoEncoded($base32) . \"\\n\";\n\techo PHP_EOL;\n\n\t// ULID\n\t$ulidValue = $ulid-\u003egenerate(1761168799791);\n\techo \"ULID 2: $ulidValue\\n\";\n\techo 'ULID 2 Timestamp: ' . $ulid-\u003edecodeTimestamp($ulidValue) . \"\\n\";\n\techo 'ULID 2 Base32: ' . $ulid-\u003etoEncoded($base32) . \"\\n\";\n} catch (Exception $e) {\n\techo 'Error: ' . $e-\u003egetMessage() . \"\\n\";\n}\n\n// Add a New Encoder:\nclass Base16Encoder extends AbstractEncoder {\n\tpublic function __construct() {\n\t\tparent::__construct(new EncoderConfig('0123456789ABCDEF'));\n\t}\n\n\tpublic function encode(string $data): string {\n\t\treturn strtoupper(bin2hex($data));\n\t}\n\n\tpublic function decode(string $data): string {\n\t\treturn hex2bin($data);\n\t}\n}\n\nclass Encoder2Factory {\n\tpublic static function createBase16(): Base16Encoder {\n\t\treturn new Base16Encoder();\n\t}\n}\n\n$base16 = Encoder2Factory::createBase16()-\u003eencode('Hello');\n$text = Encoder2Factory::createBase16()-\u003edecode($base16);\n$line(\"Base16:encoded: $base16\");\n$line(\"Base16:decoded: $text\");\n// EncoderFactory::createBase16 = fn() =\u003e new Base16Encoder();\n\n\n// Add a New ID Generator:\nclass CustomIdGenerator extends AbstractIdGenerator {\n\tpublic function generate(): string {\n\t\t$timestamp = $this-\u003egetTimestamp();\n\t\t$random = $this-\u003egetRandomBytes(8);\n\t\treturn bin2hex($timestamp . $random);\n\t}\n}\n\nclass IdGenerator2Factory {\n\tpublic static function createCustomId(): CustomIdGenerator {\n\t\treturn new CustomIdGenerator();\n\t}\n}\n\n$customId = IdGenerator2Factory::createCustomId()-\u003egenerate();\n$line(\"CustomID: $customId\");\n\n// IdGeneratorFactory::createCustomId = fn() =\u003e new CustomIdGenerator();\n\n// Customize Snowflake Configuration:\n$customConfig = new SnowflakeConfig(1640995200000, 4, 4, 10); // Custom epoch, fewer bits\n$snowflake = IdGeneratorFactory::createSnowflake(1, 1, $customConfig);\n\n// Custom Alphabet for Nanoid:\n$nanoid = IdGeneratorFactory::createNanoid('0123456789abcdef', 12); // Hex-only, shorter length\n----\n\n:leveloffset!:\n\n\u003c\u003c\u003c\n\n== Error handling\n\n- Most decoders will throw `Inane\\Stdlib\\Exception\\InvalidArgumentException` when the input contains invalid characters or cannot be parsed.\n- `SnowflakeIdGenerator::generate()` can throw `Inane\\Stdlib\\Exception\\RuntimeException` if it detects a system clock moving backwards.\n- Generators that rely on randomness may throw `Random\\RandomException` from PHP core when entropy is not available.\n\n== When to use which generator\n\n- UUIDv4: Standard interoperable identifiers, not sortable, 36 chars.\n- ULID: 26-char, lexicographically sortable, timestamp + randomness; good for DB keys and logs.\n- Nanoid: Short, URL-friendly IDs with controllable alphabet and size.\n- Snowflake: Numeric IDs composed of timestamp + worker/datacenter + sequence; good for distributed systems that need k-sortable numbers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finanepain%2Fid-forge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finanepain%2Fid-forge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finanepain%2Fid-forge/lists"}