pimbay-php/sequence

Contracts, results and exceptions for the PimBay Sequence Stack. Shared foundation for number and random sequence implementations.

Maintainers

Package info

codeberg.org/pimbay-php/sequence

Homepage

Issues

Documentation

pkg:composer/pimbay-php/sequence

Transparency log

Statistics

Installs: 19

Dependents: 5

Suggesters: 0

v2.3.0 2026-05-23 21:25 UTC

This package is auto-updated.

Last update: 2026-06-23 21:41:10 UTC


README

Latest Version on Packagist PHP Version License Code Coverage

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', 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\RandomSequenceManagerInterface;
use PimBay\Sequence\Random\CodeGeneratorInterface;

RandomSequenceManagerInterface — sequence setup:

// Create sequence — returns RandomSequenceResult immediately, no extra getCurrent() needed
$result = $manager->createSequence('vouchers', 'christmas_2026', $generator);
// $result->group, $result->name, $result->metadata, $result->createdAt, $result->generatorConfig

RandomSequenceInterface — business logic:

// Generate unique code — retries internally on collision
$code = $sequence->nextCode('vouchers', 'christmas_2026', ['country' => 'SK'], metadata: ['batch' => 'christmas_2026']);

// Insert a fixed/manual code — reserves it, prevents random generation of the same code
$sequence->insertCode('vouchers', 'christmas_2026', 'VIANOCE2026', ['source' => 'import_2026_q1']);

// Read current sequence state — throws SequenceNotFoundException if missing
$state = $sequence->getCurrent('vouchers', 'christmas_2026');
// $state->metadata, $state->createdAt, $state->generatorConfig

// Check if code exists within the group
$exists = $sequence->exists('vouchers', 'VIANOCE2026');

CodeGeneratorInterface — implemented by pimbay-php/sequence-formatter:

/** @param array<string, int|string> $context */
public function generate(array $context = []): string;
/** @return array<string, int|string|bool|array<int|string|bool>> */
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:

/** @param array<string, int|string|bool|array<int|string|bool>> $data */
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
}
ExceptionThrown by
SequenceNotFoundExceptionNumberSequenceInterface::getCurrent() or RandomSequenceInterface::getCurrent() on non-existent sequence
SequenceAlreadyExistsExceptionRandomSequenceManagerInterface::createSequence() on existing sequence
SequenceCollisionExceptionnextCode() after max attempts exceeded
CodeAlreadyExistsExceptioninsertCode() with duplicate code
InvalidCodeExceptioninsertCode() with a code containing characters not in the alphabet

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 — implements both interfaces
$randomSequence = new InMemoryRandomSequence($clock);
$randomSequence->createSequence('vouchers', 'promo', $generator);                   // RandomSequenceManagerInterface
$code = $randomSequence->nextCode('vouchers', 'promo', metadata: ['batch' => 'q1']); // RandomSequenceInterface
$randomSequence->insertCode('vouchers', 'promo', 'PROMO25', ['source' => 'import']); // reserves fixed code
$exists = $randomSequence->exists('vouchers', 'PROMO25');                            // true
$state = $randomSequence->getCurrent('vouchers', 'promo');
// $state->metadata, $state->createdAt, $state->generatorConfig

All test support classes are marked @internal — not intended for production use.

Packages in the stack

PackageDescription
pimbay/sequence-number-sqlSQL snippets for number sequence adapters
pimbay/sequence-random-sqlSQL snippets for random sequence adapters
pimbay-php/sequenceThis package — interfaces, results, exceptions
pimbay-php/sequence-formatterPattern formatter, code generator, alphabet
pimbay-php/sequence-number-pdoPDO adapter — MySQL, MariaDB, PostgreSQL, SQLite
pimbay-php/sequence-number-pdo-mssqlPDO adapter — MSSQL
pimbay-php/sequence-random-pdoPDO adapter for random sequences

Test matrix

PHPStatus
8.3
8.4
8.5

License

Public domain — Unlicense

Created by Jan Sarmir · No conditions · No copyright