digitaltunnel/secure-code
Composer 安装命令:
composer require digitaltunnel/secure-code
包简介
Cryptographically secure random code generator with a fluent API for Laravel.
README 文档
README
Secure Code
Cryptographically secure random code generator with a fluent API for Laravel.
Generate PINs, voucher codes, serial keys, invite tokens, verification codes, sequential document IDs, and more -- all powered by PHP's random_int() CSPRNG under the hood.
Table of Contents
- Requirements
- Installation
- Quick Start
- Configuration
- Usage
- Basic Generation
- Code Length
- Character Sets
- Custom Character Pool
- Prefix & Suffix
- Separators
- Case Forcing
- Exclude Similar Characters
- Batch Generation
- Uniqueness Checking
- Database Uniqueness
- Max Attempts
- Presets
- Pattern-Based Generation
- Checksum (Luhn & Mod-97)
- Code Masking
- Entropy Calculator
- Code Vault (TTL & Verification)
- HashId Encoding
- Export (JSON, CSV, Text)
- Events
- Validation Rule
- Sequential Document IDs
- Artisan Command
- Blade Directive
- Facade
- Combining Options
- API Reference
- Real-World Examples
- Architecture
- Testing
- Security
- License
Requirements
| Dependency | Version |
|---|---|
| PHP | ^8.2 |
| Laravel | 11.x, 12.x, 13.x |
| ext-bcmath | * |
Installation
composer require digitaltunnel/secure-code
The package auto-discovers its service provider and facade. No manual registration needed.
Publish Configuration (optional)
php artisan vendor:publish --tag=secure-code-config
This publishes config/secure-code.php where you can set application-wide defaults.
Publish Migrations (required for Sequential IDs)
php artisan vendor:publish --tag=secure-code-migrations php artisan migrate
This creates the secure_code_sequences table used by the sequential document ID generator. Only needed if you use the sequence() feature.
Quick Start
use DigitalTunnel\SecureCode\SecureCode; use DigitalTunnel\SecureCode\Enums\Charset; // Generate with defaults (8-char alphanumeric) $code = SecureCode::generate(); // "a9Kf3mX2" // 6-digit numeric PIN $pin = SecureCode::length(6)->charset(Charset::Numeric)->generate(); // "847293" // Use a preset $voucher = SecureCode::preset('voucher')->generate(); // "A8K3-M7F2-B9X1-P4J6" // Pattern-based $code = SecureCode::pattern('AAA-999-AAA')->generate(); // "KFM-847-XBP" // Sequential document ID (requires migration) $id = SecureCode::sequence('invoice') ->prefix('INV-') ->format('{prefix}{sequence}-{Y}{m}{d}') ->padSequence(5) ->resetEvery('yearly') ->next(); // "INV-00001-20260404"
Configuration
After publishing, edit config/secure-code.php:
return [ 'length' => 8, 'charset' => 'Alphanumeric', 'exclude_similar' => false, 'max_attempts' => 1000, 'vault' => [ 'ttl' => 300, // seconds 'max_attempts' => 5, 'cache_prefix' => 'secure_code_vault:', ], 'hashid' => [ 'salt' => '', 'min_length' => 6, ], 'sequences' => [ 'connection' => null, // null = default DB connection 'table' => 'secure_code_sequences', 'pad' => 5, // zero-pad width for sequence numbers 'format' => '{prefix}{sequence}{suffix}', 'reset' => 'never', // 'never', 'daily', 'monthly', 'yearly' 'start_at' => 1, ], ];
All options can be overridden per-call via the fluent API.
Usage
Basic Generation
use DigitalTunnel\SecureCode\SecureCode; $code = SecureCode::generate(); // "a9Kf3mX2" (8-char alphanumeric by default)
Code Length
SecureCode::length(4)->generate(); // "aK3m" SecureCode::length(32)->generate(); // "a9Kf3mX2bP7nR4jL8wQ5vT6yU1cE0dG"
Character Sets
The Charset enum provides 13 predefined character pools:
use DigitalTunnel\SecureCode\Enums\Charset; SecureCode::charset(Charset::Numeric)->generate(); // "84729361" SecureCode::charset(Charset::Alpha)->generate(); // "aKfmXbPn" SecureCode::charset(Charset::AlphaUpper)->generate(); // "AKFMXBPN" SecureCode::charset(Charset::AlphaLower)->generate(); // "akfmxbpn" SecureCode::charset(Charset::Alphanumeric)->generate(); // "a9Kf3mX2" SecureCode::charset(Charset::AlphanumericUpper)->generate(); // "A9KF3MX2" SecureCode::charset(Charset::AlphanumericLower)->generate(); // "a9kf3mx2" SecureCode::charset(Charset::Hex)->generate(); // "3a7f1b9e" SecureCode::charset(Charset::HexUpper)->generate(); // "3A7F1B9E" SecureCode::charset(Charset::Binary)->generate(); // "10110010" SecureCode::charset(Charset::Base32)->generate(); // "JBSWY3DP" SecureCode::charset(Charset::Base58)->generate(); // "4K7nR2jL" SecureCode::charset(Charset::Base64Safe)->generate(); // "aK3m-X_2b"
Available Charsets:
| Charset | Characters | Pool Size |
|---|---|---|
Numeric |
0-9 |
10 |
Alpha |
A-Z a-z |
52 |
AlphaUpper |
A-Z |
26 |
AlphaLower |
a-z |
26 |
Alphanumeric |
0-9 A-Z a-z |
62 |
AlphanumericUpper |
0-9 A-Z |
36 |
AlphanumericLower |
0-9 a-z |
36 |
Hex |
0-9 a-f |
16 |
HexUpper |
0-9 A-F |
16 |
Binary |
0 1 |
2 |
Base32 |
A-Z 2-7 |
32 |
Base58 |
1-9 A-H J-N P-Z a-k m-z |
58 |
Base64Safe |
A-Z a-z 0-9 - _ |
64 |
Custom Character Pool
SecureCode::pool('ABCDEF123456')->length(8)->generate(); // "B3A6D1F4"
Prefix & Suffix
SecureCode::length(8)->prefix('INV-')->generate(); // "INV-a9Kf3mX2" SecureCode::length(8)->suffix('-2026')->generate(); // "a9Kf3mX2-2026" SecureCode::length(6)->prefix('ORD-')->suffix('-US')->generate(); // "ORD-847293-US"
Prefix and suffix are not counted toward
length.
Separators
SecureCode::length(12)->separator('-', 4)->generate(); // "A8K3-M7F2-B9X1" SecureCode::length(9)->separator(' ', 3)->generate(); // "847 293 615"
Separators are inserted after generation and don't affect the random character count.
Case Forcing
SecureCode::length(10)->charset(Charset::Alpha)->uppercase()->generate(); // "AKFMXBPNRJ" SecureCode::length(10)->charset(Charset::Alphanumeric)->lowercase()->generate(); // "a9kf3mx2bp"
Exclude Similar Characters
Remove visually ambiguous characters (0, O, 1, I, l) from the pool:
SecureCode::charset(Charset::Alphanumeric)->excludeSimilar()->generate();
Batch Generation
$codes = SecureCode::length(8)->count(10)->generate(); // ['a9Kf3mX2', 'bP7nR4jL', ... ] (array of 10 strings)
When count is 1, a single string is returned (not an array). Codes within a batch are always unique to each other.
Uniqueness Checking
Using a Closure:
$codes = SecureCode::length(10) ->count(50) ->unique(fn (string $code) => ! DB::table('vouchers')->where('code', $code)->exists()) ->generate();
Using the UniquenessChecker Interface:
use DigitalTunnel\SecureCode\Contracts\UniquenessChecker; class VoucherUniquenessChecker implements UniquenessChecker { public function isUnique(string $code): bool { return ! Voucher::where('code', $code)->exists(); } } $codes = SecureCode::length(10) ->count(100) ->unique(new VoucherUniquenessChecker()) ->generate();
Database Uniqueness
Built-in shorthand for database uniqueness -- no manual closure needed:
$code = SecureCode::length(10) ->uniqueInTable('vouchers', 'code') ->generate(); // With a specific database connection $code = SecureCode::length(10) ->uniqueInTable('vouchers', 'code', 'mysql') ->generate(); // Batch of unique codes $codes = SecureCode::length(10) ->count(500) ->uniqueInTable('promo_codes', 'code') ->generate();
Max Attempts
SecureCode::length(4) ->charset(Charset::Numeric) ->unique(fn ($code) => ! in_array($code, $existing)) ->maxAttempts(5000) ->generate();
Default: 1000 (configurable in config/secure-code.php).
Presets
Preconfigured templates for common use cases:
use DigitalTunnel\SecureCode\Enums\Preset; SecureCode::preset('pin')->generate(); // "847293" (6 numeric digits) SecureCode::preset('otp')->generate(); // "529184" (6 numeric digits) SecureCode::preset('voucher')->generate(); // "A8K3-M7F2-B9X1-P4J6" (16 upper, no similar, dashed) SecureCode::preset('serial')->generate(); // "3A7F-1B9E-4C2D-8F5A-6E0B" (20 hex, dashed) SecureCode::preset('api-key')->generate(); // "sk_aK3mX2bP7n..." (40 base64-safe, sk_ prefix) SecureCode::preset('token')->generate(); // 64 alphanumeric chars SecureCode::preset('invite')->generate(); // 12 base58 chars
You can also use the enum directly and override options:
SecureCode::preset(Preset::Pin)->length(8)->generate(); // "84729361" (8-digit PIN)
Available Presets:
| Preset | Length | Charset | Extras |
|---|---|---|---|
pin |
6 | Numeric | -- |
otp |
6 | Numeric | -- |
voucher |
16 | AlphanumericUpper | excludeSimilar, separator - every 4 |
serial |
20 | HexUpper | separator - every 4 |
api-key |
40 | Base64Safe | prefix sk_ |
token |
64 | Alphanumeric | -- |
invite |
12 | Base58 | -- |
Pattern-Based Generation
Define the exact shape of your code using placeholder characters:
| Placeholder | Produces |
|---|---|
A |
Uppercase letter (A-Z) |
a |
Lowercase letter (a-z) |
9 |
Digit (0-9) |
X |
Hex character (0-9, A-F) |
* |
Any alphanumeric |
| Anything else | Kept as literal |
SecureCode::pattern('AAA-999-AAA')->generate(); // "KFM-847-XBP" SecureCode::pattern('99-AAAA-99')->generate(); // "84-KFMX-72" SecureCode::pattern('INV-999-AA')->generate(); // "INV-847-KF" SecureCode::pattern('XXXX-XXXX')->generate(); // "3A7F-1B9E" SecureCode::pattern('***-***')->generate(); // "a9K-f3m" // Batch from pattern $codes = SecureCode::pattern('AAA-999')->count(100)->generate();
Checksum (Luhn & Mod-97)
Append self-validating check digits to generated codes:
Luhn (for numeric codes):
use DigitalTunnel\SecureCode\Support\Checksum; $code = SecureCode::length(7) ->charset(Charset::Numeric) ->withChecksum('luhn') ->generate(); // "84729355" (7 digits + 1 Luhn check digit = 8 chars) // Verify SecureCode::verifyChecksum($code, 'luhn'); // true Checksum::verifyLuhn($code); // true
Mod-97 (for alphanumeric codes):
$code = SecureCode::length(6) ->charset(Charset::AlphanumericUpper) ->withChecksum('mod97') ->generate(); // "A8K3MF42" (6 chars + 2 check digits = 8 chars) SecureCode::verifyChecksum($code, 'mod97'); // true
Use directly:
Checksum::appendLuhn('123456'); // "1234566" Checksum::verifyLuhn('1234566'); // true Checksum::appendMod97('ABC123'); // "ABC12374" Checksum::verifyMod97('ABC12374'); // true
Code Masking
Hide parts of a code for secure display in UIs and logs:
SecureCode::mask('ABCD-EFGH-IJKL'); // "****-****-IJKL" (last 4 visible, preserves dashes) SecureCode::mask('ABCD-EFGH-IJKL', visibleEnd: 4, preserve: '-'); // "****-****-IJKL" SecureCode::mask('ABCDEFGH', visibleStart: 3); // "ABC*****" SecureCode::mask('ABCDEFGHIJ', visibleStart: 2, visibleEnd: 2); // "AB******IJ" SecureCode::mask('sk_live_abc123def456', character: '#', visibleEnd: 6); // "##############ef456"
Entropy Calculator
Evaluate the security strength of your code configuration before generating:
$info = SecureCode::length(8)->charset(Charset::Alphanumeric)->entropy(); // [ // 'bits' => 47.63, // 'strength' => 'moderate', // 'pool_size' => 62, // 'length' => 8, // 'combinations' => '218340105584896', // ]
Strength levels:
| Bits | Strength |
|---|---|
| < 28 | very weak |
| 28-47 | weak |
| 48-79 | moderate |
| 80-127 | strong |
| 128+ | very strong |
Code Vault (TTL & Verification)
Issue short-lived codes with automatic expiry and brute-force protection -- ideal for email verification, 2FA, and OTP flows:
$vault = SecureCode::vault( length: 6, // code length charset: Charset::Numeric, ttl: 300, // expires in 5 minutes maxAttempts: 5, // lock after 5 wrong guesses ); // Issue a code $code = $vault->issue('user@example.com'); // "847293" -- stored in cache, expires automatically // Verify (returns true and auto-revokes on success) $vault->verify('user@example.com', '847293'); // true $vault->verify('user@example.com', '847293'); // false (already used) // Check if a code is pending $vault->pending('user@example.com'); // bool // Remaining attempts before lockout $vault->remainingAttempts('user@example.com'); // int // Manually revoke $vault->revoke('user@example.com');
The vault uses timing-safe comparison (hash_equals) and automatically deletes the code after max failed attempts to prevent brute-force attacks.
HashId Encoding
Encode integer IDs into short, obfuscated strings (reversible):
$hashid = SecureCode::hashid(salt: 'my-secret-salt', minLength: 8); $encoded = $hashid->encode(12345); // "X8kN3mBp" $decoded = $hashid->decode($encoded); // 12345 // Different salts produce different encodings SecureCode::hashid(salt: 'salt-a')->encode(42); // "aBcDeFgH" SecureCode::hashid(salt: 'salt-b')->encode(42); // "xYzWvUtS"
Export (JSON, CSV, Text)
Generate codes and export them directly:
// JSON $json = SecureCode::length(8)->count(100)->toJson(); // '["A8K3M7F2","B9X1P4J6",...]' $json = SecureCode::length(8)->count(100)->toJson(pretty: true); // Pretty-printed JSON // CSV $csv = SecureCode::preset('voucher')->count(50)->toCsv(); // "code\nA8K3-M7F2-B9X1-P4J6\n..." $csv = SecureCode::length(8)->count(50)->toCsv('voucher_code'); // Custom header // Plain text (one per line) $text = SecureCode::length(8)->count(50)->toText(); // "A8K3M7F2\nB9X1P4J6\n..."
Events
Opt-in event dispatching for audit logging:
use DigitalTunnel\SecureCode\Events\CodeGenerated; use DigitalTunnel\SecureCode\Events\CodeBatchGenerated; // Enable events $code = SecureCode::length(8)->withEvents()->generate(); // Dispatches CodeGenerated with $event->code $codes = SecureCode::length(8)->count(10)->withEvents()->generate(); // Dispatches CodeBatchGenerated with $event->codes and $event->count
Listen for events in your EventServiceProvider or with closures:
Event::listen(CodeGenerated::class, function (CodeGenerated $event) { Log::info('Code generated', ['code' => $event->code]); }); Event::listen(CodeBatchGenerated::class, function (CodeBatchGenerated $event) { Log::info('Batch generated', ['count' => $event->count]); });
Events are not dispatched by default -- you must call withEvents() to opt in.
Validation Rule
Validate incoming codes against format, length, charset, pattern, or checksum:
use DigitalTunnel\SecureCode\Rules\SecureCodeFormat; use DigitalTunnel\SecureCode\Enums\Charset; // Validate length $request->validate([ 'code' => ['required', new SecureCodeFormat(length: 6)], ]); // Validate charset $request->validate([ 'code' => ['required', new SecureCodeFormat(charset: Charset::Numeric)], ]); // Validate length + charset $request->validate([ 'code' => ['required', new SecureCodeFormat(length: 8, charset: Charset::AlphanumericUpper)], ]); // Validate against a pattern $request->validate([ 'code' => ['required', new SecureCodeFormat(pattern: 'AAA-999-AAA')], ]); // Validate with Luhn checksum $request->validate([ 'code' => ['required', new SecureCodeFormat(verifyChecksum: true, checksumType: 'luhn')], ]); // Validate with mod-97 checksum $request->validate([ 'code' => ['required', new SecureCodeFormat(verifyChecksum: true, checksumType: 'mod97')], ]);
Sequential Document IDs
Generate gap-free, duplicate-free sequential document numbers (invoices, orders, receipts, etc.) backed by database-level locking. Safe for heavy concurrent transactions.
Requires migration: Run
php artisan vendor:publish --tag=secure-code-migrations && php artisan migratefirst.
Basic usage:
$id = SecureCode::sequence('invoice') ->prefix('INV-') ->padSequence(5) ->next(); // "INV-00001" // Next call: "INV-00002", "INV-00003", ...
Full format with date tokens:
$id = SecureCode::sequence('invoice') ->prefix('INV-') ->suffix('-EG') ->format('{prefix}{sequence}{separator}{Y}{m}{d}{suffix}') ->padSequence(5) ->resetEvery('yearly') ->next(); // "INV-00001-20260404-EG"
Batch allocation (atomic, contiguous):
$ids = SecureCode::sequence('order') ->prefix('ORD-') ->format('{prefix}{sequence}{separator}{Y}{m}{d}') ->padSequence(5) ->next(3); // ["ORD-00001-20260404", "ORD-00002-20260404", "ORD-00003-20260404"]
Inspect and preview:
$builder = SecureCode::sequence('invoice')->prefix('INV-')->padSequence(5); // Current value (last allocated), null if never used $builder->current(); // null $builder->next(); // "INV-00001" // Current value after allocation $builder->current(); // 1 // Preview next without allocating $builder->preview(); // "INV-00002"
Period-based reset:
// Resets to 1 every year SecureCode::sequence('invoice')->resetEvery('yearly')->next(); // Resets to 1 every month SecureCode::sequence('receipt')->resetEvery('monthly')->next(); // Resets to 1 every day SecureCode::sequence('ticket')->resetEvery('daily')->next(); // Never resets (default) SecureCode::sequence('order')->resetEvery('never')->next();
Custom start value and DB connection:
$id = SecureCode::sequence('invoice') ->startAt(1000) // first ever allocation starts at 1000 ->connection('mysql') // use a specific DB connection ->next(); // "01000"
Format tokens:
| Token | Example | Description |
|---|---|---|
{prefix} |
INV- |
Configured prefix |
{suffix} |
-EG |
Configured suffix |
{sequence} |
00001 |
Zero-padded sequence number |
{separator} |
- |
Configured separator |
{Y} |
2026 |
4-digit year |
{y} |
26 |
2-digit year |
{m} |
04 |
2-digit month |
{d} |
04 |
2-digit day |
{timestamp} |
1775433600 |
Unix timestamp |
How it works under the hood:
- Each call to
next()runs an autonomous database transaction withSELECT ... FOR UPDATE - This acquires an exclusive row lock, preventing concurrent access
- The sequence number is committed immediately, independent of any outer transaction
- No gaps: numbers are contiguous (1, 2, 3, ...)
- No duplicates: row-level locking serializes access
- Deadlock-free: each operation locks exactly one row
- Different sequence keys (
'invoice'vs'order') are fully independent
Tip: For strict isolation from your application's transactions, configure a dedicated database connection in
config/secure-code.phpundersequences.connection.
Artisan Command
Generate codes from the command line:
# Default (8-char alphanumeric) php artisan secure-code:generate # Custom options php artisan secure-code:generate --length=16 --charset=Numeric # Batch php artisan secure-code:generate --count=50 # With preset php artisan secure-code:generate --preset=voucher # With pattern php artisan secure-code:generate --pattern="AAA-999-AAA" # Formatted output php artisan secure-code:generate --count=10 --json php artisan secure-code:generate --count=10 --csv # All options php artisan secure-code:generate \ --length=12 \ --charset=AlphanumericUpper \ --prefix=INV- \ --suffix=-2026 \ --separator=- \ --every=4 \ --upper \ --exclude-similar \ --checksum \ --count=20
Blade Directive
Quick inline generation in Blade templates:
<p>Your code: @securecode</p>
Facade
The package registers a facade automatically:
use DigitalTunnel\SecureCode\Facades\SecureCode; SecureCode::length(8)->generate();
Combining Options
All methods are chainable and can be freely combined:
$vouchers = SecureCode::length(16) ->charset(Charset::AlphanumericUpper) ->excludeSimilar() ->separator('-', 4) ->prefix('GIFT-') ->suffix('-2026') ->withChecksum('mod97') ->withEvents() ->count(500) ->uniqueInTable('vouchers', 'code') ->maxAttempts(2000) ->generate();
API Reference
SecureCode (Static Entry Point)
| Method | Returns | Description |
|---|---|---|
generate() |
string|array |
Generate with default config |
length(int) |
CodeBuilder |
Set code length |
charset(Charset) |
CodeBuilder |
Set character set |
pool(string) |
CodeBuilder |
Set custom character pool |
prefix(string) |
CodeBuilder |
Set prefix |
suffix(string) |
CodeBuilder |
Set suffix |
separator(string, int) |
CodeBuilder |
Set separator and interval |
uppercase() |
CodeBuilder |
Force uppercase |
lowercase() |
CodeBuilder |
Force lowercase |
excludeSimilar(bool) |
CodeBuilder |
Exclude ambiguous characters |
count(int) |
CodeBuilder |
Set batch size |
unique(Closure|UniquenessChecker) |
CodeBuilder |
Set uniqueness checker |
uniqueInTable(string, string, ?string) |
CodeBuilder |
Database uniqueness |
maxAttempts(int) |
CodeBuilder |
Set max retry attempts |
preset(string|Preset) |
CodeBuilder |
Apply a preset |
pattern(string) |
CodeBuilder |
Set generation pattern |
withChecksum(string) |
CodeBuilder |
Append checksum digit |
withEvents(bool) |
CodeBuilder |
Enable event dispatching |
mask(string, ...) |
string |
Mask a code for display |
verifyChecksum(string, string) |
bool |
Verify a checksum |
vault(int, Charset, int, int) |
CodeVault |
Create a code vault |
hashid(string, int) |
HashId |
Create a HashId encoder |
sequence(string) |
SequenceBuilder |
Create a sequential ID builder |
CodeBuilder (Fluent Builder)
Immutable -- every method returns a new instance:
$template = SecureCode::length(12)->charset(Charset::AlphanumericUpper)->separator('-', 4); $code1 = $template->generate(); // uses template $code2 = $template->prefix('VIP-')->generate(); // extends template safely
Additional methods on CodeBuilder:
| Method | Returns | Description |
|---|---|---|
toJson(bool $pretty) |
string |
Generate + export as JSON |
toCsv(string $header) |
string |
Generate + export as CSV |
toText() |
string |
Generate + export as text |
entropy() |
array |
Calculate entropy info |
Support Classes
| Class | Description |
|---|---|
Checksum::appendLuhn(string) |
Append Luhn check digit |
Checksum::verifyLuhn(string) |
Verify Luhn checksum |
Checksum::appendMod97(string) |
Append mod-97 check digits |
Checksum::verifyMod97(string) |
Verify mod-97 checksum |
Mask::apply(string, ...) |
Mask a code string |
Entropy::calculate(int, int) |
Calculate entropy bits |
Entropy::strength(float) |
Get strength label |
Export::toJson(array, bool) |
Export as JSON |
Export::toCsv(array, string) |
Export as CSV |
Export::toText(array) |
Export as plain text |
PatternGenerator::generate(string) |
Generate from pattern |
PatternGenerator::toRegex(string) |
Convert pattern to regex |
HashId::encode(int) |
Encode integer |
HashId::decode(string) |
Decode to integer |
SequenceBuilder (Fluent Builder)
Immutable -- every method returns a new instance:
| Method | Returns | Description |
|---|---|---|
prefix(string) |
SequenceBuilder |
Set ID prefix |
suffix(string) |
SequenceBuilder |
Set ID suffix |
separator(string) |
SequenceBuilder |
Set separator character |
format(string) |
SequenceBuilder |
Set format template with tokens |
padSequence(int) |
SequenceBuilder |
Set zero-pad width |
resetEvery(string|SequenceResetPeriod) |
SequenceBuilder |
Set reset period |
startAt(int) |
SequenceBuilder |
Set initial sequence value |
connection(string) |
SequenceBuilder |
Set database connection |
date(DateTimeInterface) |
SequenceBuilder |
Set date for formatting and period key |
next(int $count = 1) |
string|array |
Allocate and return next ID(s) |
current() |
?int |
Get last allocated value |
preview() |
string |
Preview next ID without allocating |
Real-World Examples
Email Verification Flow
$vault = SecureCode::vault(ttl: 600); // 10 min expiry $code = $vault->issue($user->email); Mail::to($user)->send(new VerificationMail($code)); // Later, when user submits the code: if ($vault->verify($user->email, $request->code)) { $user->markEmailAsVerified(); }
Gift Card with Self-Validating Checksum
$card = SecureCode::preset('voucher') ->withChecksum('mod97') ->uniqueInTable('gift_cards', 'code') ->generate(); // When redeeming: if (! SecureCode::verifyChecksum($request->code, 'mod97')) { abort(422, 'Invalid gift card format.'); }
Obfuscated Order URLs
$hashid = SecureCode::hashid(salt: config('app.key')); // Generate URL $url = route('orders.show', $hashid->encode($order->id)); // /orders/X8kN3mBp // Resolve in controller $orderId = $hashid->decode($request->route('order'));
Batch Promo Codes Export
$csv = SecureCode::preset('voucher') ->count(10000) ->uniqueInTable('promo_codes', 'code') ->toCsv('promo_code'); Storage::put('exports/promo-codes.csv', $csv);
Security Audit with Entropy Check
$info = SecureCode::length(16)->charset(Charset::Base58)->entropy(); if ($info['bits'] < 80) { throw new \RuntimeException('Insufficient entropy for production tokens.'); }
Two-Factor Authentication
$vault = SecureCode::vault(length: 6, ttl: 120, maxAttempts: 3); $code = $vault->issue($user->id); // Display masked after generation SecureCode::mask($code, visibleStart: 1, visibleEnd: 1); // "8****3"
Invoice Numbers (Sequential)
// Sequential, gap-free invoice numbers that reset yearly $invoiceId = SecureCode::sequence('invoice') ->prefix('INV-') ->suffix('-EG') ->format('{prefix}{sequence}{separator}{Y}{m}{d}{suffix}') ->padSequence(6) ->resetEvery('yearly') ->next(); // "INV-000001-20260404-EG" // Next: "INV-000002-20260404-EG" // Bulk-generate for a batch of orders $ids = SecureCode::sequence('order') ->prefix('ORD-') ->format('{prefix}{Y}{m}{separator}{sequence}') ->padSequence(5) ->resetEvery('monthly') ->next(10); // ["ORD-202604-00001", "ORD-202604-00002", ..., "ORD-202604-00010"]
Architecture
src/
├── SecureCode.php # Static entry point
├── CodeBuilder.php # Immutable fluent builder
├── CodeGenerator.php # CSPRNG engine (random_int)
├── CodeVault.php # TTL-based issue/verify/revoke
├── Contracts/
│ └── UniquenessChecker.php # Interface for uniqueness logic
├── Console/
│ └── GenerateCommand.php # Artisan command
├── Enums/
│ ├── Charset.php # 13 predefined character pools
│ ├── Preset.php # 7 preconfigured presets
│ └── SequenceResetPeriod.php # Reset period enum
├── Events/
│ ├── CodeGenerated.php # Single code event
│ └── CodeBatchGenerated.php # Batch event
├── Facades/
│ └── SecureCode.php # Laravel Facade
├── Providers/
│ └── SecureCodeServiceProvider.php
├── Rules/
│ └── SecureCodeFormat.php # Validation rule
├── Sequence/
│ ├── SequenceBuilder.php # Fluent builder for sequential IDs
│ ├── SequenceFormatter.php # Token-based string formatting
│ └── SequenceGenerator.php # Atomic DB allocation engine
└── Support/
├── Checksum.php # Luhn & Mod-97
├── Entropy.php # Entropy calculator
├── Export.php # JSON, CSV, Text export
├── HashId.php # Integer encoding/decoding
├── Mask.php # Code masking
└── PatternGenerator.php # Pattern-based generation
Security: All randomness is produced by random_int(), which draws from the OS CSPRNG. The Code Vault uses hash_equals for timing-safe comparison.
Immutability: CodeBuilder clones itself on every fluent call. Safe to store and reuse as templates.
Testing
The package ships with 171 Pest tests covering every feature:
./vendor/bin/pest
Security
If you discover a security vulnerability, please send an email to hey@digitaltunnel.net instead of opening a public issue.
See SECURITY.md for full details on our security policy, supported versions, and best practices.
License
The MIT License (MIT). See LICENSE for details.
digitaltunnel/secure-code 适用场景与选型建议
digitaltunnel/secure-code 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 330 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 04 月 03 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「generator」 「code」 「random」 「laravel」 「otp」 「secure」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 digitaltunnel/secure-code 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 digitaltunnel/secure-code 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 digitaltunnel/secure-code 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Memio's PrettyPrinter, used to generate PHP code from given Model
Faker Japanese is a Faker provider that generates fake Japanese related data for you.
Gambling Algorithms for Certification.
Promotional Codes Generator for Laravel 5.1
Reusable utilities library for Lacus Solutions' packages (type description, HTML escaping, random sequences)
Random string generator
统计信息
- 总下载量: 330
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 33
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-04-03