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

Polyfill Uuid Laravel Package

symfony/polyfill-uuid

Symfony polyfill for the PHP uuid extension. It provides the uuid_* functions on PHP installations without ext-uuid, so applications and libraries can use UUID helpers without requiring the extension. Lightweight, MIT-licensed, and maintained by Symfony.

View on GitHub
Deep Wiki
Context7

Getting Started

First, check whether you already have it. Most Laravel apps do — it typically arrives transitively (via symfony/uid, ramsey/uuid, or another Symfony component). Don't add it blindly:

composer why symfony/polyfill-uuid
php -m | grep -i uuid          # is ext-uuid installed natively?
php -r "var_dump(function_exists('uuid_generate_v4'));"

If composer why returns nothing and you have code that calls uuid_*, that's your install signal:

composer require symfony/polyfill-uuid

There is nothing to configure. No service provider, no config/*.php to publish, no facade, no container binding. It registers a Composer files autoload that defines uuid_create(), uuid_generate_v1/3/4/5/6/7, uuid_is_valid(), uuid_compare(), uuid_type(), uuid_variant(), uuid_time() and the UUID_TYPE_* / UUID_VARIANT_* constants only if they don't already exist. If ext-uuid is loaded, the polyfill is a no-op.

Where to look first:

  1. The main Polyfill README — the per-package README is a stub.
  2. The bootstrap.php / Resources/stubs/ files in vendor/symfony/polyfill-uuid/ — that is the documentation.
  3. php.net's uuid extension docs — the polyfill mirrors the extension's API, so the extension docs are the real reference.

Your first use case is almost certainly not "call uuid_generate_v4() directly." In a Laravel app you should be using the framework:

use Illuminate\Support\Str;

Str::uuid();          // Ramsey\Uuid\UuidInterface (v4)
Str::orderedUuid();   // time-ordered, index-friendly
Str::ulid();          // ULID

The polyfill's job is to keep third-party code and legacy code that calls uuid_* from fataling. Treat it as a compatibility shim, not a UUID library.

Implementation Patterns

Eloquent model with UUID primary key (the common case). Use the framework trait; the polyfill is invisible here but keeps the underlying functions available everywhere:

use Illuminate\Database\Eloquent\Concerns\HasUuids;

class Order extends Model
{
    use HasUuids; // Laravel 9.x+
}

Older Laravel / manual assignment:

protected static function booted(): void
{
    static::creating(function ($model) {
        $model->uuid ??= (string) Str::uuid();
    });
}

Note the (string) cast — Str::uuid() returns a UuidInterface, and while Eloquent string casts usually handle it, explicit casting avoids surprises in ->toArray(), JSON responses, and log contexts.

Migration side — the trap and the fix:

// WRONG: this evaluates uuid_generate_v4() once, at migration time.
// Every row gets the identical UUID.
$table->uuid('id')->default(uuid_generate_v4());

// RIGHT: no PHP default; let the app or the DB assign it.
$table->uuid('id')->primary();

// Or push it to the database:
$table->uuid('id')->default(new Expression('gen_random_uuid()'));   // Postgres 13+
$table->uuid('id')->default(new Expression('(UUID())'));            // MySQL 8+

Deterministic UUIDs (v3/v5) for idempotent work. Genuinely useful for seeding, backfills, and dedupe keys where re-running a job must produce the same identifier:

$ns = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // UUID_TYPE_DNS namespace
$id = uuid_generate_v5($ns, "tenant:{$tenantId}:user:{$email}");

// or with ramsey, which is already installed:
$id = (string) Uuid::uuid5(Uuid::NAMESPACE_URL, $key);

Time-sortable keys (v7) instead of v4 for high-write tables. Since v1.43.0 the polyfill supports uuid_generate_v7(). B-tree index locality beats random v4 substantially on large InnoDB tables:

$id = uuid_generate_v7();   // polyfill or ext-uuid
// equivalent framework path:
$id = (string) Str::orderedUuid();

Comparing UUIDs — never strcmp. UUIDs can be formatted with/without hyphens and are compared per RFC 4122 octet order, not lexically:

if (uuid_compare($a, $b) === 0) { /* same UUID */ }

Validation in non-HTTP contexts. In a FormRequest, use Laravel's rule — it's better and doesn't depend on the polyfill:

'uuid' => ['required', 'uuid'],            // preferred

Reach for uuid_is_valid($value) only in jobs, console commands, or importers where the validator isn't wired up.

Testing determinism. Freeze UUID generation instead of asserting on random values:

Str::createUuidsUsing(fn () => Uuid::fromString('00000000-0000-4000-8000-000000000000'));
// ... assertions ...
Str::createUuidsNormally();

Docker/CI: the polyfill calls random_bytes(), which needs an entropy source. If you see Random\RandomException / Exception: Could not gather sufficient random data in a container, add --cap-add=SYS_RANDOM or ensure /dev/urandom is exposed. This is the single most common "the polyfill is broken" false alarm.

Gotchas and Tips

It never overrides the native extension — so behavior divergence is your real risk. The polyfill is if (!function_exists(...)). That means the same code path can take the extension's implementation in one environment and the polyfill's in another. A few points of divergence have historically existed and been fixed (uuid_compare, uuid_create, uuid_type alignment in v1.43.0; duplicate v1 UUIDs within the same microsecond in v1.43.0; APCu timestamp sharing). Pin the version and run your UUID-touching tests against both code paths at least once, especially if you use v1.

Don't install this to "get UUIDs." If you need UUIDs, you already have ramsey/uuid and Str::uuid(). The polyfill is only justified when something calls the procedural uuid_* API. Adding it as a "UUID library" and then reaching for uuid_generate_v4() everywhere is the anti-pattern — you lose the framework's casts, testing hooks (Str::createUuidsUsing), and ordered/ULID variants.

It supports more than v4. The evaluation material floating around claiming "only basic v4" is wrong: uuid_generate_v1/3/4/5 are long-standing, and v6/v7/v8 (UUID_TYPE_V6, UUID_TYPE_V7, UUID_TYPE_VENDOR) landed in v1.43.0. What it does not give you is a nice builder API, parsing/casting objects, or Uuid::isValid() ergonomics — that's still ramsey.

Random v4 primary keys are an index-locality problem, not a polyfill problem. If you're generating thousands of rows per second into a clustered index, v4 causes page splits regardless of which library produces it. Use v7, Str::orderedUuid(), or ULIDs — and note HasUuids lets you switch with uniqueIds() returning Str::orderedUuid().

composer.json config.platform.php can lie to you. If you've pinned platform.php below your real runtime, Composer resolves dependencies as if ext-uuid were absent even when it's present. Harmless here, but it's a classic source of "why is this polyfill installed?" confusion.

Debugging: is the function native or polyfilled?

php -r "echo (new ReflectionFunction('uuid_generate_v4'))->getFileName() ?: 'native extension';"

If it prints a path under vendor/symfony/polyfill-uuid/, the polyfill is active. If it errors on getFileName(), the extension is loaded.

Don't composer remove it to slim your lock file. It'll come straight back via whatever transitive dependency needs it. The footprint is a handful of stubs; there is no meaningful win.

There are no extension points — and that's fine. It's a pure function shim with no container hooks. If you need cross-cutting behavior (e.g., logging every generated UUID, swapping in a custom generator for a tenant scheme), write a small UuidGenerator service in your app and inject that; don't try to wrap or fork the polyfill.

Version 1 in multi-process environments: upgrade to ≥1.43.0. Earlier versions had a documented duplicate-on-same-microsecond bug, and the clock/node state is now shared via APCu. If you're on v1 UUIDs with queue workers on the same host, this matters.

Security framing, honestly: v4 UUIDs from random_bytes() are cryptographically strong and RFC 4122-compliant provided your OS entropy source is sound. Don't use them as unguessable bearer tokens in URLs without additional controls, and never assert on their values in tests.

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.
thomas-0816/doctrine-dbal-duckdb
moyisoft/laravel-admin
oxygen/mod-pages
oxygen/mod-import-export
oxygen/mod-media
ksoft/laravel-contentbuilderjs
ourgoldpackages/exchange
mikkimike/laravel-exchange1c
wandesnet/mercadopago-laravel
tsfcorp/email
larafony/core
laravel-enso/currencies
tartan/laravel-online-payment
gam/laravel-sat-catalogs
cloudcreativity/laravel-stripe
abdulelahragih/querybuilder
wedevbr/celcoin-laravel
surazdott/nepali-date
devsquad-cockpit/laravel
coding-socks/laravel-upload-handler