fast-forward / enum
Ergonomic utilities for PHP enums, including names, values, lookups, and option maps.
Requires
- php: ^8.3
- ext-mbstring: *
Requires (Dev)
- fast-forward/dev-tools: dev-main
- llm/skills: ^1.15
Suggests
- llm/skills: Optionally synchronize the bundled consumer skill in your project.
Provides
None
Conflicts
None
Replaces
None
README
Ergonomic utilities and reusable catalogs for PHP enums, including names, values, lookups, maps, sorting helpers, and enum-driven workflows.
โจ Features
- ๐งฉ Traits for
values(),names(), options, maps, lookups, and enum comparisons - ๐งญ
Helper\EnumHelperfor generic operations overUnitEnumandBackedEnum - ๐ Reversible sort-oriented enums such as
SortDirection,NullsPosition, andComparisonResult - ๐ Reusable catalogs grouped by domain, including
Calendar,Logger,Runtime, andDateTime - ๐ฆ Enum-based workflow transitions through
StateMachine\HasTransitions - ๐ท Optional
LabeledEnumInterfaceand readable descriptions without framework lock-in - ๐งผ Small public API with explicit namespaces and no
Contractsbucket
๐ฆ Installation
composer require fast-forward/enum
Requirements:
- PHP
^8.3
New to the package? Start with the Quickstart, then use the Usage guide when you want more complete examples.
๐ ๏ธ Usage
Basic enum ergonomics:
<?php declare(strict_types=1); use FastForward\Enum\Helper\EnumHelper; use FastForward\Enum\Trait\Comparable; use FastForward\Enum\Trait\HasDescription; use FastForward\Enum\Trait\HasNameLookup; use FastForward\Enum\Trait\HasNames; use FastForward\Enum\Trait\HasOptions; use FastForward\Enum\Trait\HasValues; enum Status: string { use Comparable; use HasDescription; use HasNameLookup; use HasNames; use HasOptions; use HasValues; case Draft = 'draft'; case Published = 'published'; } Status::values(); // ['draft', 'published'] Status::names(); // ['Draft', 'Published'] Status::options(); // ['Draft' => 'draft', 'Published' => 'published'] Status::fromName('Draft'); // Status::Draft Status::Draft->is(Status::Published); // false Status::Draft->description(); // 'Draft' EnumHelper::valueMap(Status::class); // ['draft' => Status::Draft, 'published' => Status::Published]
Labels and label maps:
<?php declare(strict_types=1); use FastForward\Enum\Helper\EnumHelper; use FastForward\Enum\LabeledEnumInterface; enum Priority: int implements LabeledEnumInterface { case Low = 1; case High = 2; public function label(): string { return match ($this) { self::Low => 'Low priority', self::High => 'High priority', }; } } EnumHelper::labels(Priority::class); // ['Low priority', 'High priority'] EnumHelper::labelMap(Priority::class); // ['Low' => 'Low priority', 'High' => 'High priority']
Enum-driven workflows:
<?php declare(strict_types=1); use FastForward\Enum\StateMachine\HasTransitions; use FastForward\Enum\StateMachine\InvalidTransitionException; enum ArticleWorkflow: string { use HasTransitions; case Draft = 'draft'; case Reviewing = 'reviewing'; case Published = 'published'; case Archived = 'archived'; protected static function transitionMap(): array { return [ self::Draft->name => [self::Reviewing, self::Archived], self::Reviewing->name => [self::Published, self::Draft], self::Published->name => [self::Archived], self::Archived->name => [], ]; } protected static function initialStateCases(): array { return [self::Draft]; } } ArticleWorkflow::Draft->canTransitionTo(ArticleWorkflow::Reviewing); // true ArticleWorkflow::Archived->isTerminal(); // true ArticleWorkflow::initialStates(); // [ArticleWorkflow::Draft] try { ArticleWorkflow::Reviewing->assertCanTransitionTo(ArticleWorkflow::Archived); } catch (InvalidTransitionException $exception) { // Invalid transition }
Packaged enum catalogs:
<?php declare(strict_types=1); use FastForward\Enum\Calendar\Month; use FastForward\Enum\Calendar\Quarter; use FastForward\Enum\Calendar\Semester; use FastForward\Enum\Calendar\Weekday; use FastForward\Enum\Common\Priority; use FastForward\Enum\Common\Severity; use FastForward\Enum\Comparison\ComparisonOperator; use FastForward\Enum\Container\ServiceLifetime; use FastForward\Enum\DateTime\IntervalUnit; use FastForward\Enum\Event\DispatchMode; use FastForward\Enum\Http\Scheme; use FastForward\Enum\Logger\LogLevel; use FastForward\Enum\Outcome\Result; use FastForward\Enum\Pipeline\FailureMode; use FastForward\Enum\Process\SignalBehavior; use FastForward\Enum\Runtime\Environment; use FastForward\Enum\Sort\CaseSensitivity; use FastForward\Enum\Sort\ComparisonResult; use FastForward\Enum\Sort\NullsPosition; use FastForward\Enum\Sort\SortDirection; Environment::Production->isProduction(); // true Priority::Critical->isHigherThan(Priority::Normal); // true Severity::Error->isAtLeast(Severity::Warning); // true LogLevel::Critical->isAtLeast(LogLevel::Warning); // true Result::Partial->isSuccessful(); // true ComparisonOperator::In->compare('draft', ['draft', 'published']); // true IntervalUnit::Hour->seconds(2); // 7200 DispatchMode::Async->isAsync(); // true ServiceLifetime::Singleton->isReusable(); // true FailureMode::StopOnFailure->stopsOnFailure(); // true Scheme::Https->defaultPort(); // 443 SignalBehavior::Handle->isTerminalControl(); // true Weekday::Saturday->isWeekend(); // true Month::December->quarter(); // 4 Quarter::Q2->months(); // [Month::April, Month::May, Month::June] Semester::H2->quarters(); // [Quarter::Q3, Quarter::Q4] SortDirection::Descending->reverse(); // SortDirection::Ascending NullsPosition::Last->compareNullability(null, 'value'); // 1 CaseSensitivity::Insensitive->equals('Draft', 'draft'); // true ComparisonResult::fromComparisonResult(-1); // ComparisonResult::RightGreater
๐งฐ API Summary
| API | Description |
|---|---|
Helper\EnumHelper |
Static helpers for cases, names, values, labels, maps, and lookups |
Trait\HasValues |
Adds values() to backed enums |
Trait\HasNames |
Adds names() to any enum |
Trait\HasNameLookup |
Adds fromName(), tryFromName(), and hasName() |
Trait\HasOptions |
Builds option arrays keyed by case name |
Trait\HasNameMap / Trait\HasValueMap |
Builds lookup maps for names and backed values |
Trait\Comparable |
Adds is(), isNot(), in(), and notIn() |
Trait\HasDescription |
Generates readable descriptions from case names |
Trait\HasLabel |
Provides a technical fallback label() implementation |
LabeledEnumInterface |
Contract for enums that expose presentation labels |
DescribedEnumInterface |
Contract for enums that expose human-readable descriptions |
ReversibleInterface |
Common contract for enums exposing reverse() |
StateMachine\HasTransitions |
Adds transition, terminal, and initial-state behavior to workflow enums |
StateMachine\InvalidTransitionException |
Exception thrown by invalid workflow transitions |
๐ Integration
fast-forward/enum is framework-agnostic and works well in:
- form and UI option generation
- DTO, request, and serializer layers
- validation and name/value normalization
- internal workflow modeling with enum transitions
- logging, sorting, date/time, and runtime catalogs shared across Fast Forward packages
It does not require a container, framework bridge, or reflection-heavy metadata system.
๐ Directory Structure Example
src/
โโโ Calendar/
โโโ Common/
โโโ Comparison/
โโโ Container/
โโโ DateTime/
โโโ Event/
โโโ Helper/
โโโ Http/
โโโ Logger/
โโโ Outcome/
โโโ Pipeline/
โโโ Process/
โโโ Runtime/
โโโ Sort/
โโโ StateMachine/
โโโ Trait/
tests/
โโโ Common/
โโโ Helper/
โโโ Sort/
โโโ StateMachine/
โโโ Support/
โโโ Trait/
docs/
โโโ getting-started/
โโโ usage/
โโโ api/
โโโ advanced/
โ๏ธ Advanced & Customization
- Implement
LabeledEnumInterfacewhen you need explicit presentation labels. - Use your own domain enums when semantics are business-specific rather than generic.
- Combine
Comparable, lookup traits, andHasTransitionsto build compact workflow models. - Prefer the packaged catalogs only when the semantics are stable and cross-project.
๐ ๏ธ Versioning & Breaking Changes
The current development line tracks 1.x-dev. There is no published breaking-change history yet for
this package.
โ FAQ
Q: Why does this package expose traits instead of one giant helper class?
Traits let enums opt into only the ergonomics they need while keeping the public surface explicit.
Q: Why is there no Contracts namespace?
Public interfaces stay in the root namespace so the package does not hide core API behind a generic
bucket.
Q: Is ComparisonResult a PHP polyfill?
No. It is a Fast Forward enum for comparator-style semantics, not a promise of native compatibility.
๐ก License
MIT ยฉ 2026 Felipe Sayรฃo Lobato Abreu
๐ค Contributing
Issues, pull requests, and documentation improvements are welcome.
- Read AGENTS.md for repository-specific guidance
- Run
composer dump-autoload - Run
./vendor/bin/dev-tools tests - Update the README and relevant docs when changing public API
๐ Links
- Repository
- Packagist
- Documentation site
- Sphinx docs source
- Wiki
- Issue tracker
- Coverage report
- Tests workflow
- MIT License
- RFC 2119
Consumer agent skill
The package includes fast-forward-enum for application integration.
It supports fast-forward/enum:^0.2; inspect the installed version.
See skill installation and lifecycle for
optional project synchronization with llm/skills ^1.15, guarded manual
project/global copies and a pinned Skills CLI alternative. Shipping files does
not install tooling or activate the skill in an agent session.
