Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

States Laravel Package

teknoo/states

Teknoo States provides a simple, extensible finite state machine implementation for PHP. Model objects with states and transitions, define allowed changes, and trigger callbacks on entry, exit, or transition events—useful for workflows, order processing, and status management.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • teknoo/states is a single-purpose, in-process implementation of the GoF State pattern. A proxy dispatches method calls to the currently enabled state object(s), letting an object's behavior change at runtime without monolithic switch/if ladders. It carries no persistence, orchestration, or eventing concerns, so the blast radius is small but the scope is narrow: it solves exactly one problem — removing conditional sprawl from rich domain objects.
  • The programming model is deliberately unconventional. "State methods" are builders that return Closure instances rebound to the stated class; inside the closure, $this/self/static refer to the proxy, not the state. This yields real PHP method semantics and visibility enforcement across states, but it is a frame-breaking idiom that defeats IDE navigation and is opaque to tooling unless the shipped PHPStan extension is loaded. Treat "team absorbs the idiom" as the dominant adoption cost.
  • Best fit is legacy domain models (orders, subscriptions, documents, tickets, approvals) already paying for conditional sprawl, where the alternative is a god-class refactor. Poor fit in greenfield apps where enum + match or symfony/workflow delivers most of the value without the idiom tax.

Integration feasibility

  • Mechanically low friction: one runtime dependency (teknoo/immutable), BSD-3-Clause, no daemon, no DB requirement, no Laravel service provider or config. Doctrine ORM/ODM support ships as an optional infrastructure layer (StandardTrait, AbstractStandardObject).
  • Hard gate: PHP ^8.4. Environments on 8.1–8.3 must upgrade first — usually a multi-quarter platform program, not a library decision. This is the single largest feasibility filter.
  • Second gate: the PHPStan extension depends on PHPStan internals outside its BC promise. Upstream patch releases frequently exist only to repair PHPStan-induced breaks, and the project's own phpstan.neon does not load the extension. Adopting the idiom at scale means pinning PHPStan hard or accepting periodic red CI.

Technical risk

  • Idiom risk (high): builder-returns-Closure is not guessable. Correctness depends on developers remembering : Closure return types and closure/arrow-function returns. Mistakes fail late — at call time on the affected code path.
  • Visibility via debug_backtrace() (medium): builder visibility is enforced by walking the call stack. This can behave differently behind framework proxies (Doctrine lazy proxies, DI decorators, mocking tools) and adds per-call overhead.
  • Reflection/attribute surface (medium): state declaration moved to #[StateClass] attributes; attribute scanning plus RuntimeCache implies warm-up costs and cache-coherence questions under long-running workers (RoadRunner/Swoole/FrankenPHP) — must be measured.
  • Vendor risk (medium): 10 stars, 0 dependents, effectively single-maintainer. The permissive license and small surface make a vendor-and-freeze posture credible, but no one upstream will be paged for us.
  • Deprecation churn (low-medium): a live deprecation path (statesListDeclaration() → #[StateClass]) and a PHPUnit config that fails on any deprecation/notice. Good hygiene, moving target on bumps.

Key questions

  1. Is PHP 8.4+ already our platform baseline, or would this package be the forcing function? If the latter, stop.
  2. Do we have a concrete, bounded conditional-sprawl problem worth the idiom tax — and what is the measurable before/after (cyclomatic complexity, defect rate in the affected module)?
  3. Who owns the PHPStan pin, and are we willing to run the extension unloaded by default?
  4. Are any candidate domain objects serialized (sessions, queues, caches)? If so, the reserved __serialize() builder and state serialization semantics need a spike.
  5. Would symfony/workflow or enum + match satisfy the requirement with less novelty? Answer in writing, not verbally.

Integration Approach

Stack fit

  • Natural home: a Laravel app with rich domain models and Doctrine in play (or willing to adopt the Doctrine traits). If the app is Eloquent-centric with anemic models plus service classes, the conditional logic lives in services, not entities — this library does not help there.
  • Poor fit: serialization-heavy queue pipelines, hot request paths where per-call debug_backtrace() is measurable, and codebases where an internals-coupled PHPStan extension is a CI liability.
  • Neutral: Laravel itself — no provider, config, or facade; it is composer require plus a namespace import.

Migration path

  1. Isolate behind an internal seam (our own StateMachine/HasStates contract) so domain classes are reachable only through our abstraction; a future swap to symfony/workflow becomes one adapter change.
  2. Refactor one bounded aggregate — the object with the worst conditional sprawl and the best test coverage — behind parity tests asserting the old switch path and the new state path produce identical observable behavior.
  3. Pin teknoo/states and phpstan/phpstan to exact versions and commit the application composer.lock (upstream's "lock is ignored" rule is library-authoring convention, not consumer guidance).
  4. Grep for statesListDeclaration() and convert to #[StateClass] in the same PR; the library emits E_USER_DEPRECATED and a strict PHPUnit config will fail the build.
  5. Write a one-page internal how-to covering the builder/closure rule, the StateDefault auto-enable convention, and child-overloads-parent-by-short-class-name. Do not rely on upstream docs as the sole reference.

Compatibility

  • Enforce declare(strict_types=1); and PSR-12 (120-char lines) — matches upstream, so no conflict.
  • Validate the Doctrine infrastructure traits against our actual ORM version and hydration mode before assuming persistence works.
  • Validate the PHPStan extension against our pinned PHPStan; if it breaks on a patch bump, drop it from phpstan.neon rather than block a security update.
  • Audit serialization: sessions, queues, caches touching stated objects need explicit round-trip tests.

Sequencing Platform upgrade to 8.4 → dependency pin + time-boxed spike (two sprints, one aggregate) → parity verification → docs and a review gate → a second aggregate with no new scope → decide on wider rollout at the quarter boundary. Never run two migrations of this kind concurrently; the idiom, not the tooling, is the bottleneck.

Operational Impact

Maintenance

  • Steady-state cost is low if state classes stay small and single-responsibility, which upstream doctrine mandates ("avoid monolithic state classes"). The predictable failure mode is the first state class that grows past a few hundred lines, recreating the god-object with extra indirection.
  • Budget explicitly for dependency churn: expect 2–4 bump-and-fix cycles per year chasing PHPStan internals.
  • Keep fork readiness: an internal patch branch and a verified local build. Note the default make target is clean depend — it rm -rf vendor and runs composer update; never call bare make in an automated job. Use make qa-offline where network is unavailable.

Support

  • Bus factor 1, 10 stars, 0 known dependents, no commercial support and no community to lean on. Mitigation is institutional: an internal owner, internal docs, and a forkable license — not hope.
  • Escalation path for defects is our fork, not upstream. Set the expectation with stakeholders that "we maintain this" is the real support model.

Scaling

  • The library is in-process and stateless at the framework level, so horizontal scaling is bounded by the host application, not the package. The scaling concerns are per-call cost (debug_backtrace() visibility resolution, reflection/attribute scanning, RuntimeCache behavior) and coherence of cached state metadata under long-running workers. Benchmark before putting stated objects on a hot path; on long-running workers verify cache warm-up and invalidation behavior explicitly.

Failure modes

  • Late-failing idiom errors: a missing : Closure return type surfaces at the call site on a specific state, not at deploy time — coverage of every state method on every state is therefore mandatory.
  • Visibility misresolution behind proxies: unexpected StateException-style failures when Doctrine or DI proxies mediate calls.
  • Static-analysis drift: a routine PHPStan bump breaks the extension and CI goes red for reasons unrelated to our code.
  • Serialization breakage: stated objects crossing queue/session boundaries lose or mis-restore state.
  • Monolith regression: state classes absorbing business logic until the pattern adds indirection without removing complexity.

Ramp-up

  • Realistic onboarding: one experienced PHP engineer reaches productivity in roughly a week with the internal how-to plus a paired refactor; a junior needs supervision until the builder/closure idiom and the #[StateClass]/StateDefault conventions are internalized. Budget two engineers for the initial spike and measure their onboarding time as a primary spike output.
  • Do not attempt organization-wide standardization on first adoption. Treat this as a narrow, deliberate bet on specific complex workflows, with a written before/after showing reduced conditional complexity and no regression in the affected module.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core