kphoen/doctrine-state-machine-bundle
Symfony bundle that integrates the Doctrine finite state machine library, letting you define state machines and transitions in configuration and expose them as container services for clean, testable workflow logic in your PHP applications.
Architecture fit — poor for a Laravel codebase. Despite sitting in a PHP ecosystem, this is not a Laravel package: it is a Symfony bundle (K-Phoen/DoctrineStateMachineBundle) that attaches finite-state-machine metadata to Doctrine ORM entities and drives transitions from Doctrine lifecycle events. It assumes Symfony's dependency-injection container, bundle registration, symfony/event-dispatcher, and the Doctrine annotation driver. Laravel gives you Eloquent, service providers, and its own event dispatcher — none of which are drop-in surfaces for this bundle. Fit is viable only in the narrow case of an application already running Doctrine ORM 2.x under Laravel (e.g., via laravel-doctrine/orm) and willing to hand-wire the bundle's services.
Integration feasibility — conditional and non-trivial. Prerequisites a TPM must budget for:
| Requirement | Available in a typical Laravel app? |
|---|---|
| Doctrine ORM entities | No (Eloquent is default) |
| Symfony DI container / bundle kernel | No |
| Symfony EventDispatcher for the FSM listener | Partial (Laravel events ≠ Symfony contracts) |
| Doctrine annotation mapping (docblock) | No on modern stacks; attributes only |
| Service provider, config publish, Artisan commands | None shipped |
| PHP 8.1+ compatibility | Unverified, likely broken |
The realistic integration is a custom bridge: a service provider that registers the metadata listener and transition subscriber on Doctrine's EventManager, plus a symfony/event-dispatcher shim. That bridge is more code than the business logic it enables.
Technical risk — high, concentrated in abandonment. Description explicitly marks the package [DEPRECATED]; last release 2016-08-17; 45 stars; 0 dependents on the registry. That combination means no upstream maintainer, no security response path, no PHP 8.x compatibility work, and no community to ask when it breaks. Secondary risks:
composer why-not walls quickly.Key questions for the sponsor:
TPM read on the scores: the composite 2.21 is a "do not adopt" signal; the opportunity score of 7.76 should be read as high value in replacing/porting the concept, not as evidence of package quality. The idea — declarative states and transitions enforced at persistence time — is sound and worth 2–3 days of design extraction; the artifact is not.
Stack fit. This package fits a Symfony 2/3 + Doctrine 2 application circa 2015. In a Laravel context the only coherent placement is a Doctrine-backed bounded context (a reporting, billing, or legacy-integration service that already speaks Doctrine). Do not introduce Doctrine into an Eloquent application to consume this bundle — the ORM migration alone dwarfs any FSM benefit.
Migration path — three options, ranked:
can() check, single transitionTo() mutator that writes state + emits a domain event in the same transaction. Implement against Eloquent with casts, a HasStateMachine trait, and observers. Cost: small. Risk: low. Gain: identical semantics, PHP 8.1+ native, testable, greppable.symfony/workflow operated directly, without the Symfony full stack) and wire it in a Laravel service provider. Note symfony/workflow expects Symfony's event dispatcher and property-access components; verify the standalone footprint before committing.Compatibility gates before any code is written. Run composer why-not kphoen/doctrine-state-machine-bundle against the target app; confirm (a) Doctrine ORM major, (b) Symfony component versions, (c) PHP constraint, (d) whether the annotation reader is still installed. A single failure here should terminate the spike — no exceptions.
Sequencing.
Maintenance. If adopted, this becomes a permanent fork: abandoned packages are flagged by Composer's audit tooling and by SBOM/security scanners, which will surface as a recurring compliance finding in every release review. Budget for a named owner, a pinned lockfile, and a documented exit plan. The 2016 vintage means any PHP or framework upgrade re-opens the fork work; expect this to recur annually.
Support. No upstream, no maintainer, no issue triage. Answers exist only in 2014–2016 Stack Overflow and the README. Internal onboarding must rely on the extracted spec, not the code — another argument for writing the spec regardless of the decision.
Scaling. The FSM evaluation itself is cheap (in-memory state graph, negligible CPU per entity). Two real concerns:
onFlush/postLoad, so cost scales with the unit of work, not the request — batch imports become hard to reason about.The dominant scaling risk is organizational, not computational: one under-maintained dependency in the persistence hot path.
Failure modes to instrument for:
| Failure | Symptom | Detection |
|---|---|---|
| Listener not registered | Transitions silently no-op; state never changes | Assert state delta on every write in tests |
| Listener ordering | Guard evaluates against stale state; illegal transition persists | Integration test with multi-entity flush |
| Concurrency / lost update | Two writers both pass the guard; last write wins | Optimistic-lock version column; assert on conflict |
| Transaction rollback | State changed, side-effect event already dispatched | Emit domain events after commit, outbox pattern |
| Annotation drift | Typo in docblock silently widens the graph | Lint the state/transition matrix in CI |
Ramp-up. A Laravel team will burn 1–3 days each on Symfony bundle conventions, Doctrine event lifecycle, and annotation-based metadata — knowledge that has no reuse elsewhere in the codebase. A native Eloquent implementation is learnable in hours by any mid-level Laravel developer and is reviewable in a pull request. That asymmetry, combined with zero dependents and an explicit deprecation, is the whole case: recommend rejecting the dependency and adopting only its model.
How can I help you explore Laravel packages today?