State pattern implements a system where an object is able to change its behavior based on state(one or more properties) of that object. The behavior can change completely or partially.
This article demonstrates State pattern implementations in PHP. Check the following examples.
Implementation
Follow the steps below for State pattern implementation-
- Create abstract class (or interface if that suites the requirement) for the states.
- Create state classes for each of the steps/states and extend the abstract state class.
- Create class for context handling. Implement processing for handling different states.
Here is a simple example of state pattern implementation-
<?php
// State pattern implementation in PHP
// State abstract class
abstract class State {
protected Context $context;
public function __construct(Context $context) {
$this->context = $context;
$this->context->setState($this);
}
abstract function actionOne(): void;
abstract function actionTwo(): void;
}
// Context class
class Context {
private ?State $state;
public function setState(State $state): void {
$this->state = $state;
}
public function getState(): ?State {
return $this->state;
}
public function performActionOne(): void {
$this->state?->actionOne();
}
public function performActionTwo(): void {
$this->state?->actionTwo();
}
}
// First state class
class ConcreteStateOne extends State {
public function __construct(Context $context) {
parent::__construct($context);
}
public function actionOne(): void {
echo "Calling 'actionOne' of - 'ConcreteStateOne'n";
}
public function actionTwo(): void {
echo "Calling 'actionTwo' of - 'ConcreteStateOne'n";
}
}
// Second state class
class ConcreteStateTwo extends State {
public function __construct(Context $context) {
parent::__construct($context);
}
public function actionOne(): void {
echo "Calling 'actionOne' of - 'ConcreteStateTwo'n";
}
public function actionTwo(): void {
echo "Calling 'actionTwo' of - 'ConcreteStateTwo'n";
}
}
// Demo
$context = new Context();
new ConcreteStateOne($context);
$context->performActionOne();
// Change state class for the context
new ConcreteStateTwo($context);
$context->performActionOne();
$context->performActionTwo();
Output of this will be as below-
Calling 'actionOne' of - 'ConcreteStateOne'
Calling 'actionOne' of - 'ConcreteStateTwo'
Calling 'actionTwo' of - 'ConcreteStateTwo'
Examples
Here are a few examples of state pattern implementation in PHP-
Example #1: Order State Change
Here we are implementing an order processing system. This will move the order to next state after each processing.
As the state is changed after each step of processing, so the behavior of the object also changes and calling the same processing method will perform the processing of the next step.
Order State
- Create file “OrderState.php”.
- Create abstract class “OrderState”.
- Define a protected property “$context” of type “OrderContext” (this class is defined later). Accept and set “$context” in the constructor.
- In the constructor call the “setState” of the context and pass the current object($this) to it.
- Declare an abstract function named “process”.
<?php
// OrderState.php
namespace BigBoxCodeDesignPatternStateOrder;
abstract class OrderState {
protected OrderContext $context;
public function __construct(OrderContext $context) {
$this->context = $context;
$this->context->setState($this);
}
abstract function process(): void;
}
Order Check State Class
- Create file “OrderCheckState.php”.
- Define class “OrderCheckState”.
- Extend “OrderState” for the class.
- In the “process” method implement execute the process/steps for the initial checking of order info, then set the state of the context to the next state- In Progress state.
<?php
// OrderCheckState.php
namespace BigBoxCodeDesignPatternStateOrder;
class OrderCheckState extends OrderState {
public function __construct(OrderContext $context) {
parent::__construct($context);
}
public function process(): void {
// Write code to process the order
echo "Checking the order validity and other informationn";
$this->context->setState($this->context->getOrderInProgressState());
}
}
Order In-Progress State Class
- Create file “OrderInProgressState.php”.
- Define class “OrderInProgressState” and extend abstract class “OrderState”.
- In the “process” method implement execute the process/steps for the initial checking of order info, then set the state of the context to the next state- In Deliver state.
<?php
// OrderInProgressState.php
namespace BigBoxCodeDesignPatternStateOrder;
class OrderInProgressState extends OrderState {
public function __construct(OrderContext $context) {
parent::__construct($context);
}
public function process(): void {
// Write code to process the order
echo "Processing the ordern";
$this->context->setState($this->context->getOrderDeliverState());
}
}
Order Deliver State Class
- Create file “OrderDeliverState.php”.
- Define class “OrderDeliverState” and extend abstract class “OrderState”.
- In the “process” method after performing deliver state processing, Change the order to next state.
<?php
// OrderDeliverState.php
namespace BigBoxCodeDesignPatternStateOrder;
class OrderDeliverState extends OrderState {
public function __construct(OrderContext $context) {
parent::__construct($context);
}
public function process(): void {
// Write code to process the order
echo "Delivering the ordern";
$this->context->setState($this->context->getOrderReceiveState());
}
}
Order Receive State Class
- Create file “OrderReceiveState.php”.
- Define class “OrderReceiveState” and extend abstract class “OrderState”.
- In the “process” method after performing deliver state processing, Change the order to next state.
<?php
// OrderReceiveState.php
namespace BigBoxCodeDesignPatternStateOrder;
class OrderReceiveState extends OrderState {
public function __construct(OrderContext $context) {
parent::__construct($context);
}
public function process(): void {
// Write code to process the order
echo "Order receivedn";
$this->context->setState(null);
}
}
Order Context Class
- Create file “OrderContext.php”.
- Define class “OrderContext”.
- Define properties “$state” (for storing the current state) and other properties for each state. All these properties will be of “OrderState”.
- In the constructor initialize all the properties, by creating and assigning new objects of those state classes.
- In the constructor set the state(initial state) to check the state.
- Define method for getting and setting “$state”.
- Define methods for getting the objects for different states.
- Define method “runNextProcess”. If the current state is set and available then call the “process” from the state, else return a message that the order processing is complete.
<?php
// OrderContext.php
namespace BigBoxCodeDesignPatternStateOrder;
class OrderContext {
private ?OrderState $state;
private OrderState $orderCheckState;
private OrderState $orderInProgressState;
private OrderState $orderDeliverState;
private OrderState $orderReceiveState;
public function __construct() {
$this->orderCheckState = new OrderCheckState($this);
$this->orderInProgressState = new OrderInProgressState($this);
$this->orderDeliverState = new OrderDeliverState($this);
$this->orderReceiveState = new OrderReceiveState($this);
// Set the placed state as default
$this->state = $this->orderCheckState;
}
public function setState(?OrderState $state): void {
$this->state = $state;
}
public function getState(): OrderState|null {
return $this->state;
}
public function getOrderCheckState(): OrderState {
return $this->orderCheckState;
}
public function getOrderInProgressState(): OrderState {
return $this->orderInProgressState;
}
public function getOrderDeliverState(): OrderState {
return $this->orderDeliverState;
}
public function getOrderReceiveState(): OrderState {
return $this->orderReceiveState;
}
public function runNextProcess(): void {
if ($this->state != null) {
$this->state->process();
} else {
echo "Order processing completen";
}
}
}
Demo
In the client, create an object of “OrderContext”. Then call “runNextProcess” method, which will execute the current processing steps and move the order to the next state.
Once all the steps are complete, then calling the “runNextProcess” method will not do anything.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternStateOrderOrderContext;
$order = new OrderContext();
$order->runNextProcess();
$order->runNextProcess();
$order->runNextProcess();
$order->runNextProcess();
// Trying to process after all steps are complete
$order->runNextProcess();
Output
Following output will be generated-
Checking the order validity and other information
Processing the order
Delivering the order
Order received
Order processing complete
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Order State Change | GitHub |
Other Code Implementations
Use the following links to check State pattern implementation in other programming languages.