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

Defuse Generator Laravel Package

spomky-labs/defuse-generator

A small PHP utility from Spomky-Labs for generating secure cryptographic keys compatible with defuse/php-encryption. It offers a simple API for creating encryption keys to protect application data in Laravel and other PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require spomky-labs/defuse-generator

Before writing code, open vendor/spomky-labs/defuse-generator/src and read the two or three classes there. The public surface is intentionally tiny — typically a generator class plus an interface — and the exact namespace/method signature is the only thing you need to confirm. It wraps defuse/php-encryption's CSPRNG, so the important guarantee is "cryptographically secure bytes, mapped onto a charset," not a framework feature.

There is no Laravel service provider or config file. Register a binding yourself in AppServiceProvider:

use Defuse\Generator\StringGenerator; // confirm namespace in vendor/

public function register(): void
{
    $this->app->singleton(StringGenerator::class, fn () => new StringGenerator());
}

Your first real use case should be issuing an opaque API or password-reset token, because that is where CSPRNG quality actually matters:

final class TokenIssuer
{
    public function __construct(private StringGenerator $generator) {}

    /** @return array{0:string,1:string} plaintext, hash */
    public function issue(int $length = 48): array
    {
        $plain = $this->generator->generate(
            $length,
            'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
        );

        return [$plain, hash('sha256', $plain)];
    }
}

Return the plaintext to the user once, store only the hash, and compare with hash_equals on lookup. That pattern is the reason to reach for this package at all; everything else (random filenames, slugs) is better served by Laravel's Str::random().

Implementation Patterns

Bind once, inject everywhere. Never instantiate the generator at call sites. A singleton binding means one place to swap in a fake during tests and one place to audit entropy usage.

// AppServiceProvider::register()
$this->app->singleton(StringGenerator::class, fn () => new StringGenerator());

Drive alphabets from config, not literals. Different features need different character sets, and hard-coded strings drift.

// config/random.php
return [
    'token_length' => (int) env('RANDOM_TOKEN_LENGTH', 48),
    'alphabets' => [
        'alnum'   => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
        'url'     => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
        'numeric' => '0123456789',
        'human'   => 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789', // no I/O/0/1
    ],
];
$this->app->singleton(StringGenerator::class, fn () => new StringGenerator());
$this->app->bind('random.alphabet', fn () => config('random.alphabets.url'));

Read alphabets through config(), never env(), so php artisan config:cache behaves.

Wrap it in a domain abstraction. The package type is a low-level utility; your application should depend on an interface like TokenGenerator, implemented by a class that delegates to it. This keeps vendor types out of jobs, controllers, and tests, and makes it trivial to replace the package later with random_bytes().

interface TokenGenerator { public function generate(int $length): string; }

final class DefuseTokenGenerator implements TokenGenerator
{
    public function __construct(private StringGenerator $generator) {}

    public function generate(int $length): string
    {
        return $this->generator->generate($length, config('random.alphabets.url'));
    }
}

OTP / short codes need a length and expiry policy, not just randomness. Use the numeric alphabet, expire in minutes, and rate-limit verification. Six digits is ~20 bits of entropy — fine only because attempts are throttled.

$code = $this->generator->generate(6, config('random.alphabets.numeric'));
Cache::put("otp:{$user->id}", hash('sha256', $code), now()->addMinutes(10));

Idempotency keys for queued jobs are a clean second use case: generate once when dispatching, persist on the job payload, and use a unique index to reject duplicate work.

dispatch(new SendInvoice($invoice, $this->generator->generate(32, config('random.alphabets.url'))));

Uniqueness needs a database constraint and a retry loop, not just a long string. For slugs, short links, or referral codes, catch the unique-constraint violation and regenerate a bounded number of times.

foreach (range(1, 5) as $_) {
    $code = $this->generator->generate(8, config('random.alphabets.human'));
    try { return ReferralCode::create(['code' => $code, 'user_id' => $user->id]); }
    catch (UniqueConstraintViolationException) { continue; }
}
throw new RuntimeException('Could not allocate a referral code.');

Testing: bind a deterministic fake to your domain interface in TestCase::setUp(). Do not attempt to seed the CSPRNG, and do not assert on generated values in feature tests — assert on length, charset membership, and that the stored hash matches.

$this->app->bind(TokenGenerator::class, fn () => new class implements TokenGenerator {
    public function generate(int $length): string { return str_repeat('a', $length); }
});

Commands and one-offs: an artisan command that mints a token for a service account is the most common operational use. Inject the generator via the command constructor as usual.

Gotchas and Tips

  • You probably don't need it on modern Laravel. random_bytes() has existed since PHP 7.0 and Str::random() since Laravel 5.4. This package's historical value was PHP 5.x compatibility via Defuse. If you are on Laravel 9/10/11, justify the dependency explicitly or use the framework.
  • Entropy is length × log2(alphabetSize). A 32-character hex string is 128 bits; a 6-digit numeric code is ~20 bits. Short human codes must be paired with expiry and rate limiting, never used as API keys.
  • Check for modulo bias. A correct implementation uses rejection sampling; a naive $alphabet[random_int(0, len-1)] is fine, but ord(random_byte) % strlen($alphabet) skews the distribution. Read the source once and confirm.
  • Case-insensitive collations cause "random" collisions. MySQL's default utf8mb4_unicode_ci treats A and a as equal in a unique index. Use _bin collation or a binary/varbinary column for token columns, or restrict the alphabet to a case-insensitive-safe subset.
  • Avoid ambiguous characters for anything a human types (I, l, 1, O, 0). Use the human alphabet above and strtoupper before comparison if the channel is case-insensitive.
  • Never use this for password storage. Hash passwords with Laravel's Hash facade (bcrypt/argon2); random strings are for secrets you hand out, not secrets you verify.
  • Don't log, dump, or put plaintext tokens in URL query strings. Accept them in Authorization headers or POST bodies, and hash before persistence.
  • Multibyte alphabets are a trap. If you pass emoji or CJK characters, the package may split by byte, producing malformed multibyte strings. Stick to single-byte ASCII alphabets unless the source explicitly documents multibyte support.
  • Fail closed on entropy failure. If the underlying CSPRNG throws, do not catch it and fall back to mt_rand()/uniqid(). Let it surface as a 500; a weak token is worse than a failed request.
  • Config caching: env() calls outside config files return null after config:cache. Route every length and alphabet through config('random.*').
  • Octane/Long-running workers: the singleton is stateless and safe, but do not cache the last generated value on the generator or on a service around it; that turns a CSPRNG into a sequence.
  • Pin the version and review the dependency tree. The package depends on defuse/php-encryption, which pulls in OpenSSL/Sodium expectations. Confirm the extension exists in your Docker image and that the dependency is not abandoned before shipping it into a long-lived app.
  • Extension point: because the surface is an interface plus a class, you can implement the package's generator interface in a test double or a decorator that records generated values for observability — just never log the plaintext secret itself.
  • Migration path: keep your own TokenGenerator interface from day one. Swapping the Defuse-backed implementation for bin2hex(random_bytes(32)) or Str::random(48) later then touches exactly one class.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core