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
This package is auto-updated.
Last update: 2026-04-24 22:57:37 UTC
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
