pimbay-php / sequence
Contracts, results and exceptions for the PimBay Sequence Stack. Shared foundation for number and random sequence implementations.
Requires
- php: >=8.3
- psr/clock: ^1.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^11.0
README
Foundational interfaces, results and exceptions for the PimBay Sequence Stack. Shared foundation for number and random sequence implementations.
Installation
composer require pimbay-php/sequence
What is in this package
This package defines what sequence implementations must do. Concrete adapters live in separate packages and are injected at runtime.
Number sequences
use PimBay\Sequence\Number\NumberSequenceInterface;
use PimBay\Sequence\Number\NumberSequenceResult;
use PimBay\Sequence\Exception\SequenceNotFoundException;
NumberSequenceInterface — two methods:
// Atomic increment — creates sequence if it does not exist
$next = $sequence->nextNumber('invoice', '2026', initialValue: 1000);
// Read current value without incrementing — throws SequenceNotFoundException if missing
$result = $sequence->getCurrent('invoice', '2026');
// $result->currentValue, $result->metadata, $result->createdAt, $result->updatedAt
Random sequences
use PimBay\Sequence\Random\RandomSequenceInterface;
use PimBay\Sequence\Random\CodeGeneratorInterface;
RandomSequenceInterface — five methods:
// Create sequence with a bound code generator (generator config persisted in DB)
$sequence->createSequence('vouchers', 'christmas_2026', $generator);
// Generate unique code — retries internally on collision
// Pass runtime context for pattern tokens like {country}
$code = $sequence->nextCode('vouchers', 'christmas_2026', ['country' => 'SK']);
// Insert a fixed/manual code — reserves it, prevents random generation of the same code
$sequence->insertCode('vouchers', 'christmas_2026', 'VIANOCE2026');
// Read current sequence state without modifying it — throws SequenceNotFoundException if missing
$state = $sequence->getCurrent('vouchers', 'christmas_2026');
// $state->metadata, $state->createdAt, $state->updatedAt
// Check if code exists within the group
$exists = $sequence->exists('vouchers', 'VIANOCE2026');
CodeGeneratorInterface — implemented by pimbay-php/sequence-formatter:
public function generate(array $context = []): string;
public function toArray(): array; // for JSON storage in DB
CodeGeneratorFactoryInterface — implemented by adapter packages or the application layer. Reconstructs a generator from its serialized toArray() configuration, with full access to the DI container for injecting dependencies such as NumberSequenceInterface or ClockInterface:
public function create(array $data): CodeGeneratorInterface;
Exceptions
All exceptions extend SequenceException implements SequenceExceptionInterface. To catch all sequence exceptions at once, use SequenceExceptionInterface:
use PimBay\Sequence\Exception\SequenceExceptionInterface;
try {
$sequence->nextCode('vouchers', 'promo');
} catch (SequenceExceptionInterface $e) {
// catches all sequence exceptions
}
| Exception | Thrown by |
|---|---|
SequenceNotFoundException | NumberSequenceInterface::getCurrent() or RandomSequenceInterface::getCurrent() on non-existent sequence |
SequenceAlreadyExistsException | createSequence() on existing sequence |
SequenceCollisionException | nextCode() after max attempts exceeded |
CodeAlreadyExistsException | insertCode() with duplicate code |
Testing
This package ships with in-memory test doubles and a controllable clock for unit testing:
use PimBay\Sequence\Test\Support\InMemoryNumberSequence;
use PimBay\Sequence\Test\Support\InMemoryRandomSequence;
use PimBay\Sequence\Test\Support\MockClock;
// Controllable clock
$clock = new MockClock(new \DateTimeImmutable('2026-01-01'));
$clock->advance('+1 hour');
// In-memory number sequence
$numberSequence = new InMemoryNumberSequence($clock);
$next = $numberSequence->nextNumber('invoice', '2026', 1000);
// In-memory random sequence
$randomSequence = new InMemoryRandomSequence($clock);
$randomSequence->createSequence('vouchers', 'promo', $generator);
$code = $randomSequence->nextCode('vouchers', 'promo');
$state = $randomSequence->getCurrent('vouchers', 'promo');
// $state->metadata, $state->createdAt, $state->updatedAt
All test support classes are marked @internal — not intended for production use.
Packages in the stack
| Package | Description |
|---|---|
pimbay-php/sequence | This package — interfaces, results, exceptions |
pimbay-php/sequence-formatter | Pattern formatter, code generator, alphabet |
pimbay/sequence-number-sql | SQL snippets for number sequence adapters |
pimbay-php/sequence-number-pdo | PDO adapter — MySQL, MariaDB, PostgreSQL, SQLite |
pimbay-php/sequence-number-pdo-mssql | PDO adapter — MSSQL |
pimbay-php/sequence-random-pdo | PDO adapter for random sequences |
Test matrix
| PHP | Status |
|---|---|
| 8.3 | ✅ |
| 8.4 | ✅ |
| 8.5 | ✅ |
License
Public domain — Unlicense
Created by Jan Sarmir · No conditions · No copyright