Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cuid2 Laravel Package

visus/cuid2

A PHP implementation of CUID2 for generating secure, URL-safe, collision-resistant identifiers across multiple machines without coordination. Includes instance-based and static APIs plus built-in validation.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. CUID2 is a pure value-generation library: three classes (Cuid2, Fingerprint, Utils), no service provider, no config file, no facade, no framework coupling. That is a strength for us — it drops into any PHP 8.3 codebase and imposes no container, HTTP, or storage opinions. The design is deliberately stateless at the process level (timestamp + monotonic counter + host/PID/environment fingerprint + CSPRNG bytes → SHA3-512 → base36), so horizontally scaled app servers can mint IDs without coordination, a requirement for our multi-node/queue-worker topology. Two behaviors are architecturally material: (1) since v6 the identifier is generated in the constructor and cached, so a Cuid2 instance is a single immutable ID, not a factory — resolving it as a container singleton would return the same ID forever; use Cuid2::generate() or construct per-ID; (2) the class deliberately blocks __clone/__wakeup, so the object cannot be serialized into queue payloads or the cache — only (string) casts may cross boundaries. Neither is a blocker; both must be encoded in our wrapper or they will be violated in review.

Integration feasibility. High. Composer install, MIT licensed, no transitive dependencies since 6.0.0 (the markrogoyski/math-php and symfony/polyfill-php83 deps were removed), GMP optional with an identical-output pure-PHP fallback. The work is not adoption of a package, it is writing the ~100 lines of Laravel glue that the package intentionally omits: an IdGenerator contract, a Cuid2 adapter, a provider binding, and an Eloquent trait/model convention ($incrementing = false, $keyType = 'string'). That is roughly 1–2 engineer-days plus tests and is largely copy-adaptable from existing ULID/UUID trait patterns.

Technical risk.

  • Sortability claim is weaker than it reads. The README lists "sortability" as a feature, but the millisecond timestamp is an input to the hash, not a prefix. Lexicographic order does not track creation order. Any team that assumes CUID2 ≈ ULID will build broken cursor pagination and time-range queries. Treat this as the #1 documentation hazard.
  • Random primary keys fight InnoDB. Because output is effectively unordered, using CUID2 as a clustered primary key reintroduces the page-split/write-amplification profile of UUIDv4. For high-insert tables, keep the auto-increment PK and put CUID2 in a unique external-ID column.
  • Platform floor. 7.0.0 requires PHP 8.3; 6.0.0 required 8.2; 5.0.0 made GMP mandatory before 5.1.0 reversed it. Three consecutive majors raised the floor or flipped hard requirements. Pin exactly and read every major's changelog before bumping.
  • Adoption signal. 44 stars, 0 dependents, effectively single-maintainer. Code quality indicators are strong (SonarCloud gate, 100% coverage claim, CI, Conventional Commits, Renovate), but the low score reflects ecosystem/maintenance heuristics, not correctness. This is a "small dependency with a small blast radius" — vendor-or-wrap accordingly.
  • No failure at boot, only at first use. Missing SHA3-512 support throws from the generator; GMP absence silently costs 60–300× on base conversion. Both surface on a write path, not at deploy.
  • isValid() is format-only. It must never be used as an authenticity or authorization check.

Key questions. Is PHP 8.3 the fleet baseline (i.e., are we on Laravel 11/12-era runtimes)? Is ext-gmp present in every prod image, and do we care enough to enforce it? Do we need time-ordered IDs (→ ULID/UUIDv7) or merely opaque, non-enumerable ones (→ CUID2)? Is CUID2 a primary key or an external identifier? Do any non-PHP services need to generate or validate these values, and has cross-language format parity been verified against the reference implementation? Who owns the wrapper and the length constant? (Also flag: the metadata's last-release date of 2026-09-21 is future-dated — verify the actual tag on Packagist before pinning.)

Integration Approach

Stack fit. Good for Laravel 11/12 on PHP 8.3. No framework conflict: it does not overlap with symfony/uid, ramsey/uuid, or Laravel's built-in HasUuids/HasUlids traits, so it can coexist per-model. Its output is lowercase base36, URL-safe, and case-insensitive-collation-safe — no escaping or encoding concerns in routes, JSON, or S3 keys.

Migration path. Do not rewrite existing primary keys. Introduce CUID2 additively: new tables get a cuid column (or an external-ID pattern beside the existing auto-increment PK); existing tables get a nullable column, backfilled in batches, then a unique index. Dual-write during transition, with reads keyed on the legacy column until the new column is populated everywhere. Only if a table is small and low-traffic should PK replacement even be considered; the index-rebuild and FK-cascade cost dominates otherwise. Rollback is clean because every step is additive — stop generating and drop the column; nothing depends on it until the cutover is complete.

Compatibility. Lock one length (recommend 24, the default) in a single application constant and reuse it for isValid($value, expectedLength: X); changing the length later invalidates stored IDs against validation. Avoid lengths below ~10 for anything externally exposed — the 4-character minimum is far too little entropy. Enforce (string) casting at every boundary (queue payloads, cache, logs, session) so the __wakeup/__clone guards are never tripped. If Octane/RoadRunner is in play, confirm nothing holds a Cuid2 instance as a singleton.

Sequencing.

  1. Spike (½ day): confirm SHA3-512 and GMP on a representative prod image; measure ~10k generations with and without GMP (expect ~microseconds with GMP vs ~250 µs/ID without — the difference between negligible and seconds per bulk import).
  2. Build the seam (1–2 days): App\Contracts\IdGenerator, Cuid2Generator adapter, provider binding, HasCuid trait, php artisan id:healthcheck that generates one ID and asserts format/length at deploy time.
  3. Pilot one new, non-critical table behind the trait; validate route-model binding, JSON serialization, and index behavior under load.
  4. Standardize in the engineering guide; add a static-analysis or review rule banning direct new Cuid2() outside the adapter.
  5. Only then evaluate backfill of legacy tables, table by table, with an explicit cost/benefit per table.

Operational Impact

Maintenance. Low code surface, zero runtime dependencies, MIT. The real maintenance load is version drift: three majors in recent history raised the PHP floor or flipped GMP from optional to required to optional again. Pin a minor (composer require visus/cuid2:^7.0 with a committed lockfile), gate upgrades behind a regression test that asserts output shape and length, and keep the adapter thin enough that replacing the library with symfony/uid is a one-file change. Given 0 dependents and a single maintainer, treat this as a vendorable dependency: the source is small enough to fork or pin indefinitely if upstream stalls.

Support. No commercial support; community-only. Internal support burden concentrates in the adapter owner. Two things to publish internally: (a) CUID2 is not time-sortable despite the README's "sortability" bullet — use created_at for ordering; (b) isValid() is format-checking only and must not gate access to a record. Both are cheap doc fixes that prevent expensive misuse.

Scaling. Generation scales horizontally with no coordination — the fingerprint plus per-process counter handles concurrency within a node, and the CSPRNG handles the rest. Throughput is bounded by hashing plus base conversion, i.e., fast with GMP and roughly 4k IDs/sec/node without it. That is fine for request-path generation and marginal for bulk imports or fixtures — plan to enable GMP in CI and prod, and benchmark before any large seed/backfill job. The scaling pressure point is not the generator, it is the database: random, non-ordered keys on a clustered index cause fragmentation at high insert rates. The external-ID-column pattern sidesteps this entirely and should be the default recommendation.

Failure modes and mitigations.

  • Silent slowness — GMP missing, pure-PHP fallback, 60–300× base-conversion cost. Mitigate: assert extension_loaded('gmp') in the deploy health check.
  • Hard failure on first write — SHA3-512 unavailable throws from the generator. Mitigate: same health check, run pre-traffic.
  • Serialization exception — __wakeup/__clone guards fire if the object is queued or cached. Mitigate: adapter returns string, never the object; enforce with a return type.
  • Stale-ID singleton — resolving Cuid2 from the container yields one ID reused for every model. Mitigate: bind the contract to a stateless factory, not the class.
  • Entropy erosion — a short configured length silently weakens collision resistance and enumeration resistance. Mitigate: single constant, floor enforced in the adapter.
  • Length drift — a changed default breaks isValid() against stored IDs. Mitigate: version the constant or validate without expectedLength on read paths.

Ramp-up. Effectively zero for a mid-level Laravel developer: two constructor forms, one static, one validator. Budget one short internal note covering the four gotchas (not time-sortable, format-only validation, object-vs-string, length is a schema decision) and the adapter enforces the rest structurally. The heavier lift is the organizational conversation about identifier strategy — PK vs external ID, and CUID2 vs ULID/UUIDv7 — which should happen once, up front, not per team.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin