From acc
Generates State pattern for PHP 8.4 including context, state interface, concrete states, factory, entity updates, exceptions, and unit tests for state-dependent object behavior like order workflows.
How this skill is triggered — by the user, by Claude, or both
Slash command
/acc:create-stateThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Creates State pattern infrastructure for objects that change behavior based on internal state.
Creates State pattern infrastructure for objects that change behavior based on internal state.
| Scenario | Example |
|---|---|
| Object behavior varies by state | Order (pending, paid, shipped) |
| Many conditionals based on state | Document workflow |
| State-specific transitions | Subscription lifecycle |
| Finite state machines | Payment processing |
Determine:
Path: src/Domain/{BoundedContext}/State/
{Name}StateInterface.php — State contract with all actionsAbstract{Name}State.php — Base class with default (invalid) implementations{StateName}State.php — Concrete state for each state (PendingState, PaidState, etc.){Name}StateFactory.php — Factory for reconstitution from storagePath: src/Domain/{BoundedContext}/Exception/
InvalidStateTransitionException.php — Exception for invalid transitionsPath: src/Domain/{BoundedContext}/Entity/
Update entity to use state pattern with delegation methods.
Path: tests/Unit/Domain/{BoundedContext}/State/
{StateName}StateTest.php — Test each concrete state{Entity}StateTransitionTest.php — Test full state machine| Component | Path |
|---|---|
| State Interface | src/Domain/{BoundedContext}/State/ |
| Concrete States | src/Domain/{BoundedContext}/State/ |
| State Factory | src/Domain/{BoundedContext}/State/ |
| Entity with State | src/Domain/{BoundedContext}/Entity/ |
| Exception | src/Domain/{BoundedContext}/Exception/ |
| Unit Tests | tests/Unit/Domain/{BoundedContext}/State/ |
| Component | Pattern | Example |
|---|---|---|
| Interface | {Name}StateInterface | OrderStateInterface |
| Abstract State | Abstract{Name}State | AbstractOrderState |
| Concrete State | {StateName}State | PendingState, PaidState |
| Factory | {Name}StateFactory | OrderStateFactory |
| Exception | InvalidStateTransitionException | InvalidStateTransitionException |
| Test | {ClassName}Test | PendingStateTest |
interface {Name}StateInterface
{
public function getName(): string;
public function {action1}({Context} $context): self;
public function {action2}({Context} $context): self;
public function canTransitionTo(self $state): bool;
/** @return array<string> */
public function allowedTransitions(): array;
}
abstract readonly class Abstract{Name}State implements {Name}StateInterface
{
public function {action1}({Context} $context): {Name}StateInterface
{
throw InvalidStateTransitionException::actionNotAllowed('{action1}', $this->getName());
}
public function canTransitionTo({Name}StateInterface $state): bool
{
return in_array($state->getName(), $this->allowedTransitions(), true);
}
}
final readonly class {StateName}State extends Abstract{Name}State
{
public function getName(): string
{
return '{state_name}';
}
public function {action1}({Context} $context): {Name}StateInterface
{
$context->recordEvent(new {Event}($context->id()));
return new {NextState}State();
}
public function allowedTransitions(): array
{
return ['{next_state_1}', '{next_state_2}'];
}
}
final class {Name}StateFactory
{
public static function fromName(string $name): {Name}StateInterface
{
return match ($name) {
'pending' => new PendingState(),
'confirmed' => new ConfirmedState(),
default => throw new \InvalidArgumentException("Unknown state: $name"),
};
}
}
// Entity with state
$order = new Order($id, $customerId, $items);
$order->confirm(); // Pending -> Confirmed
$order->pay(); // Confirmed -> Paid
$order->ship(); // Paid -> Shipped
$order->deliver(); // Shipped -> Delivered
// Check state
if ($order->isInState('delivered')) {
// ...
}
// Reconstitute from storage
$state = OrderStateFactory::fromName($row['state']);
$order = new Order($id, $customerId, $items, $state);
┌──────────┐ action1 ┌──────────┐ action2 ┌──────────┐
│ State1 │───────────▶│ State2 │───────────▶│ State3 │
└────┬─────┘ └────┬─────┘ └──────────┘
│ │
│ cancel │ cancel
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│Cancelled │ │Cancelled │
└──────────┘ └──────────┘
| Anti-pattern | Problem | Solution |
|---|---|---|
| Mutable States | Shared state pollution | Make states readonly |
| God State | One state handles all | Split into specific states |
| Missing Transitions | Silent failures | Throw on invalid action |
| State in Entity | Mixed concerns | Extract to State classes |
| No Factory | Hard to reconstitute | Add StateFactory |
For complete PHP templates and examples, see:
references/templates.md — State interface, abstract, concrete templatesreferences/examples.md — Order state machine example and testsnpx claudepluginhub dykyi-roman/awesome-claude-code --plugin accUse when an object's behavior changes based on its internal state and the logic for each state is complex — replacing large conditional chains with state objects where each state encapsulates its own behavior.
Generates DDD-compliant entities for PHP 8.4 with identity, behavior, state transitions, invariants, status enums, exceptions, and unit tests.
Replaces conditional logic with state objects that delegate behavior to the current state. Useful for state machines like order lifecycle, connection states, or traffic lights.