PHP: State

State

State is a behavioral design pattern that allows an object to change the behavior when its internal state changes.

The pattern extracts state-related behaviors into separate state classes and forces original object to delegate the work to an instance of these classes, instead of acting on its own.

More about State

Application of the pattern in PHP

Complexity:

Popularity:

Usage examples: The State pattern is occasionally used in PHP for turning large and cumbersome state machines based on switch operators into objects.

Example: Structure of the Pattern

This example illustrates the structure of the State design pattern and focuses on following questions:

  • What classes does it consists of?
  • What roles do these classes play?
  • In what way the elements of the pattern are related?

After learning about the pattern's structure it will be easier for you to grasp the following example, based on a real world PHP use case.

StateStructural.php: Structural Example

<?php

namespace RefactoringGuru\State\Structural;

/**
 * The Context defines the interface of interest to clients. It also maintains a
 * reference to an instance of a State subclass, which represents the current
 * state of the Context.
 */
class Context
{
    /**
     * @var State A reference to the current state of the Context.
     */
    private $state;

    /**
     * @param State $state
     */
    public function __construct(State $state)
    {
        $this->transitionTo($state);
    }

    /**
     * The Context allows changing the State object at runtime.
     */
    public function transitionTo(State $state)
    {
        print("Context: Transition to ".get_class($state).".\n");
        $this->state = $state;
        $this->state->setContext($this);
    }

    /**
     * The Context delegates part of its behavior to the current State object.
     */
    public function request1()
    {
        $this->state->handle1();
    }

    public function request2()
    {
        $this->state->handle2();
    }
}

/**
 * The base State class declares methods that all Concrete State should
 * implement and also provides a backreference to the Context object, associated
 * with the State. This backreference can be used by States to transition the
 * Context to another State.
 */
abstract class State
{
    /**
     * @var Context
     */
    protected $context;

    public function setContext(Context $context)
    {
        $this->context = $context;
    }

    public abstract function handle1();

    public abstract function handle2();
}

/**
 * Concrete States implement various behaviors, associated with a state of the
 * Context.
 */
class ConcreteStateA extends State
{
    public function handle1()
    {
        print("ConcreteStateA handles request1.\n");
        print("ConcreteStateA wants to change the state of the context.\n");
        $this->context->transitionTo(new ConcreteStateB());
    }

    public function handle2()
    {
        print("ConcreteStateA handles request2.\n");
    }
}

class ConcreteStateB extends State
{
    public function handle1()
    {
        print("ConcreteStateB handles request1.\n");
    }

    public function handle2()
    {
        print("ConcreteStateB handles request2.\n");
        print("ConcreteStateB wants to change the state of the context.\n");
        $this->context->transitionTo(new ConcreteStateA());
    }
}

/**
 * The client code.
 */
$context = new Context(new ConcreteStateA());
$context->request1();
$context->request2();

Output.txt: Output

Context: Transition to RefactoringGuru\State\Structural\ConcreteStateA.
ConcreteStateA handles request1.
ConcreteStateA wants to change the state of the context.
Context: Transition to RefactoringGuru\State\Structural\ConcreteStateB.
ConcreteStateB handles request2.
ConcreteStateB wants to change the state of the context.
Context: Transition to RefactoringGuru\State\Structural\ConcreteStateA.

Example: Real World Example

While the concept of state machines and the State pattern itself are very popular among developers, I can't think of any decent real world example of this pattern in a real PHP application.

However, if you had used the pattern in your project before, feel free to share your experience on the forum or via email support@refactoring.guru. Thanks!