Using the State Pattern in PHP
The state pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. It appears as if the object changed its class.
The first statement makes sense, given that the state diagram is based on the idea of states and corresponding behavioral changes when the state machine transitions between various states.
The second statement is more apparent from a client’s perspective because when the object changes its behavior, it appears that it is an instance of some other class. The client doesn’t see all the state transitions that happen within, thus creating a perception that the object changes its class in the runtime.
Article Highlights
- Defines classes for all the states, encapsulating their behaviors. The client delegates actions to the state objects.
- Benefit – Creates a flexible and maintainable state machine.
- Cons – Adds as many classes as the number of states in a system.
State Design Pattern PHP Code Example
<?php
interface State {
public function pay();
public function getRefund();
public function pickFlavor();
public function dispenseTea();
}
class NotPaid implements State {
private $cubeTeaMachine;
public function __construct($cubeTeaMachine) {
$this->cubeTeaMachine = $cubeTeaMachine;
}
public function pay() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getPaidState());
echo "Payment has been received"."\n";
}
public function getRefund() {
echo "Invalid operation. No payment made yet"."\n";
}
public function pickFlavor() {
echo "Please pay before choosing a flavor"."\n";
}
public function dispenseTea() {
echo "Please wait for a while"."\n";
}
}
class Paid implements State {
private $cubeTeaMachine;
public function __construct($cubeTeaMachine) {
$this->cubeTeaMachine = $cubeTeaMachine;
}
public function pay() {
echo "Payment has already made"."\n";
}
public function getRefund() {
echo "Refunding amount in a moment"."\n";
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getNotPaidState());
}
public function pickFlavor() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getSoldState());
echo "Flavor selected. Tea will be ready in a moment"."\n";
}
public function dispenseTea() {
echo "Please select flavor before proceeding"."\n";
}
}
class Sold implements State {
private $cubeTeaMachine;
public function __construct($cubeTeaMachine) {
$this->cubeTeaMachine = $cubeTeaMachine;
}
public function pay() {
echo "Please wait, tea will be ready in a moment"."\n";
}
public function getRefund() {
echo "Invalid operation. Tea will be ready to pick in moment"."\n";
}
public function pickFlavor() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getSoldState());
echo "Flavor selected already. Tea will be ready to pick in moment"."\n";
}
public function dispenseTea() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getNotPaidState());
echo "Tea is ready to be picked"."\n";
}
}
class CubeTeaMachine {
private $NotPaidState;
private $PaidState;
private $SoldState;
private $currentState;
public function __construct() {
$this->NotPaidState = new NotPaid($this);
$this->PaidState = new Paid($this);
$this->SoldState = new Sold($this);
$this->setState($this->NotPaidState);
}
public function setState($state) {
$this->currentState = $state;
}
public function getNotPaidState() {
return $this->NotPaidState;
}
public function getPaidState() {
return $this->PaidState;
}
public function getSoldState() {
return $this->SoldState;
}
public function pay() {
$this->currentState->pay();
}
public function getRefund() {
$this->currentState->getRefund();
}
public function pickFlavor() {
$this->currentState->pickFlavor();
}
public function dispenseTea() {
$this->currentState->dispenseTea();
}
}
function main() {
$cubeTeaMachine = new CubeTeaMachine();
$cubeTeaMachine->pay();
$cubeTeaMachine->pickFlavor();
$cubeTeaMachine->dispenseTea();
}
/*
Payment has been received
Flavor selected. Tea will be ready in a moment
Tea is ready to be picked
*/
?>

Table of Contents
- What is State Pattern?
- Example of State Pattern
- Problem Scenario
- Solution
- Benefits of State Design Pattern
- Complete Architecture of State Pattern
- Pseudocode of State Pattern
- Pros & Cons of State Pattern
- Use cases of State Pattern
- Conclusion
What is the State Pattern
“The state pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. It appears as if the object changed its class.”

State Pattern Example
Remember CubeTea from the template method design pattern? They are a new venture in town serving iced tea via their ice tea machines. Previously they built a module for automatic tea-making. Now, there is another assignment, and it is about automating the payment and checkout.
The machine has a user interface where users can pay via cards or digital wallets, pick a flavor and then proceed to checkout. Customers can cancel before checkout and could get a refund.
Cool, isn’t it?
Developers love visualizing abstract ideas, so they made a state diagram for this system.

The circles represent the states of the system. A system can be in any of those states. Think of states as different configurations of the system resulting in different behaviors.

The arrows represent state transitions. These are actions, and when they occur, they change the system’s state.

Having gathered states and transitions, developers moved to code this state machine.
They created an instance variable state
that would represent the current state of the machine.
The initials state would be NOT PAID, where the customer is yet to pay. Only then the state transitions into PAID.
<?php
class CubeTeaMachine {
private $state;
public static $NOT_PAID = 0;
public static $PAID = 1;
public static $SOLD = 2;
public function __construct() {
$this->state = CubeTeaMachine::$NOT_PAID;
}
public function getState() {
echo $this->state."/n";
}
}
?>
Next, they create methods for state transitions. The following is the method for pay state transition.
<?php
public function pay() {
if($this->state == CubeTeaMachine::$NOT_PAID) {
$this->state = CubeTeaMachine::$PAID; //Changes state to PAID
echo "Payment Received"."\n";
}
elseif($this->state == CubeTeaMachine::$PAID) {
echo "Already Paid"."\n";
}
elseif($this->state == CubeTeaMachine::$SOLD) {
echo "Please wait, tea will be ready in a moment"."\n";
}
}
?>
We have three conditional, each for one state respectively. The output/behavior of the machine depends on the state. Notice the state transition from NOT_PAID to PAID.
The complete code features all the methods. They all use similar conditional statements for evaluating the state and the related behavior.
<?php
class CubeTeaMachine {
private $state;
public static $NOT_PAID = 0;
public static $PAID = 1;
public static $SOLD = 2;
public function __construct() {
$this->state = CubeTeaMachine::$NOT_PAID;
}
public function pay() {
if($this->state == CubeTeaMachine::$NOT_PAID) {
$this->state = CubeTeaMachine::$PAID; //Changes state to PAID
echo "Payment Received"."\n";
}
elseif($this->state == CubeTeaMachine::$PAID) {
echo "Already Paid"."\n";
}
elseif($this->state == CubeTeaMachine::$SOLD) {
echo "Please wait, tea will be ready in a moment"."\n";
}
}
public function getRefund() {
if($this->state == CubeTeaMachine::$NOT_PAID) {
echo "Invalid operation. No payment made yet"."\n";
}
elseif($this->state == CubeTeaMachine::$PAID) {
$this->state = CubeTeaMachine::$NOT_PAID; //Refunds money and reverts to NOTPAID state
echo "Refunding amount in a moment"."\n";
}
elseif($this->state == CubeTeaMachine::$SOLD) {
echo "Invalid operation. Tea will be ready in a moment"."\n";
}
}
public function pickFlavor() {
if($this->state == CubeTeaMachine::$NOT_PAID) {
echo "Please pay before choosing a flavor"."\n";
}
elseif($this->state == CubeTeaMachine::$PAID) {
$this->state = CubeTeaMachine::$SOLD; //Flavor has been chosen. Time to switch to SOLD state
echo "Flavor selected. Tea will be ready in a moment"."\n";
}
elseif($this->state == CubeTeaMachine::$SOLD) {
echo "Please select flavor before proceeding"."\n";
}
}
public function dispenseTea() {
if($this->state == CubeTeaMachine::$NOT_PAID) {
echo "Please wait for a while"."\n";
}
elseif($this->state == CubeTeaMachine::$PAID) {
echo "Invalid operation"."\n";
}
elseif($this->state == CubeTeaMachine::$SOLD) {
echo "Tea is ready to be picked"."\n";
$this->state = CubeTeaMachine::$NOT_PAID; //Back to initial state. One cycle completes.
}
}
public function getState() {
echo $this->state."\n";
}
}
?>
The developers are eager to test drive the application. Let’s see the output.
function main() {
$cubeTeaMachine = new CubeTeaMachine();
//Initial state
$cubeTeaMachine->getState();
//Pay
$cubeTeaMachine->pay();
$cubeTeaMachine->getState();
//Pick flavor and it will transition to SOLD.
$cubeTeaMachine->pickFlavor();
$cubeTeaMachine->getState();
//When one cycle complete, the state returns to initial NOT_PAID state
$cubeTeaMachine->dispenseTea();
$cubeTeaMachine->getState();
}
/*
0
Payment Received
1
Flavor selected. Tea will be ready in a moment
2
Tea is ready to be picked
0
*/
Pretty cool! It is working as seen in the state diagram. So, we are good to go. But wait! The game is not yet over because having a change request anytime soon is not a big deal in software development.
But why is that a problem? Let’s discuss that before reaching any conclusion.
Problem 🙁
Horrors of a change request
One change request, either in the form of a new state or transition, can have a ripple effect on the system. Consider CubeTea wants to add a new state, “Out of service” when the iced tea machine is out of order.

That’s many changes, and the worst is that similar changes can introduce bugs and logical fallacies leading to incorrect programs.
Open-closed principle
The open-closed principle states that “a class should be closed for modification but open for extension.” Consider adding a new state. Developers must modify the existing module because there is no other way.
A good software design has to open ways for extension in a way that doesn’t affect the existing code, which is clearly not the case here. A design must be defined where adding new states has no or minimal effect on the existing design.
Single responsibility principle
The single responsibility principle states that “a class should have one and only one responsibility.” In the current code, the class represents the machine entity and also takes care of multiple states and transitions. So, the class takes care of more than one thing and has multiple responsibilities.
Ideally, there should be specialized classes for representing the machine, states, and their behaviors.
Code duplication
Clearly, there is a lot of duplication in the code given that we are using conditional statements for the machine’s state. This problem may not be apparent at this scale, but if they want to add processing power to the machine for various objectives, including analytics and advanced customer care features, in the future.
All these will add hundreds, if not thousands of states and transitions. Think of the code with thousands of conditionals and states. It is a developer’s nightmare, a catastrophe, and a total mess when change and maintenance activities kick in.
Solution 🙂
Encapsulate what varies
Let’s recall the principle of “encapsulate what varies” meaning that try to confine the most dynamic elements of a program in a specialized set of modules. This way any change occurring will have a limited scope and won’t initiate a chain reaction or what we termed as the ripple effect above. Of course, there would be some minor changes in the existing code but that will be much limited in scope.
The dynamic element is “behaviors” associated with the states. So, defining classes for each state where each state implements its behavior is a nice solution. The machine can delegate behavior calls to the state object which would then execute the appropriate class method.
This design will open up the system for extension and localize the scope of a change.
Introducing state pattern
It is not much of a spoiler because the article title says already that state pattern is the savior here. Let’s learn it step-by-step. Also, forget about the last code because it is not useful now. We will be doing things from scratch. It’s a 100% refactor.
1. Define an interface State and include methods for every state transition action.

2. Implement a class for every state of the machine. They are supposed to provide an implementation for the State interface methods.

3. Use composition to delegate behavior calls to the state object.

State transitions in the new design
Let’s go through the basic flow. In this case, the Context class, which is the CubeTeaMachine, references all the state objects (composition).
1. The current state of the CubeTeaMachine instance is pointing at the NotPaid object, which represents the machine’s initial state. The system delegates the pay()
call to the NotPaid object, and as a result, it proceeds with payment and changes the state to Paid.

2. The current state now points to the Paid object. The system delegates the pickFlavor()
call to the Paid object. Consequently, it confirms the order changes the state to Sold.

3. Finally, the system delegates the dispenseTea()
to the current state object Sold. In return, the method does the dispensing function and sets the state back to NotPaid, which completes one cycle.

Nice! You can find the complete pseudocode of the state pattern in PHP below.
Benefits of the State Pattern
- Removes code duplication and problematic conditional statements making maintenance much easier.
- Clearly define states and corresponding behaviors, which is easier to read and understand.
- Makes the system flexible to accommodate new states easily.
Complete Architecture | State Pattern in PHP

State Pattern PHP Pseudocode Example
<?php
interface State {
public function pay();
public function getRefund();
public function pickFlavor();
public function dispenseTea();
}
class NotPaid implements State {
private $cubeTeaMachine;
public function __construct($cubeTeaMachine) {
$this->cubeTeaMachine = $cubeTeaMachine;
}
public function pay() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getPaidState());
echo "Payment has been received"."\n";
}
public function getRefund() {
echo "Invalid operation. No payment made yet"."\n";
}
public function pickFlavor() {
echo "Please pay before choosing a flavor"."\n";
}
public function dispenseTea() {
echo "Please wait for a while"."\n";
}
}
class Paid implements State {
private $cubeTeaMachine;
public function __construct($cubeTeaMachine) {
$this->cubeTeaMachine = $cubeTeaMachine;
}
public function pay() {
echo "Payment has already made"."\n";
}
public function getRefund() {
echo "Refunding amount in a moment"."\n";
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getNotPaidState());
}
public function pickFlavor() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getSoldState());
echo "Flavor selected. Tea will be ready in a moment"."\n";
}
public function dispenseTea() {
echo "Please select flavor before proceeding"."\n";
}
}
class Sold implements State {
private $cubeTeaMachine;
public function __construct($cubeTeaMachine) {
$this->cubeTeaMachine = $cubeTeaMachine;
}
public function pay() {
echo "Please wait, tea will be ready in a moment"."\n";
}
public function getRefund() {
echo "Invalid operation. Tea will be ready to pick in moment"."\n";
}
public function pickFlavor() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getSoldState());
echo "Flavor selected already. Tea will be ready to pick in moment"."\n";
}
public function dispenseTea() {
$this->cubeTeaMachine->setState($this->cubeTeaMachine->getNotPaidState());
echo "Tea is ready to be picked"."\n";
}
}
class CubeTeaMachine {
private $NotPaidState;
private $PaidState;
private $SoldState;
private $currentState;
public function __construct() {
$this->NotPaidState = new NotPaid($this);
$this->PaidState = new Paid($this);
$this->SoldState = new Sold($this);
$this->setState($this->NotPaidState);
}
public function setState($state) {
$this->currentState = $state;
}
public function getNotPaidState() {
return $this->NotPaidState;
}
public function getPaidState() {
return $this->PaidState;
}
public function getSoldState() {
return $this->SoldState;
}
public function pay() {
$this->currentState->pay();
}
public function getRefund() {
$this->currentState->getRefund();
}
public function pickFlavor() {
$this->currentState->pickFlavor();
}
public function dispenseTea() {
$this->currentState->dispenseTea();
}
}
function main() {
$cubeTeaMachine = new CubeTeaMachine();
$cubeTeaMachine->pay();
$cubeTeaMachine->pickFlavor();
$cubeTeaMachine->dispenseTea();
}
/*
Payment has been received
Flavor selected. Tea will be ready in a moment
Tea is ready to be picked
*/
?>
Pros and Cons of the State Pattern in PHP
Pros | Cons |
Single responsibility principle: State and machine have their own classes with a discrete set of responsibilities. | Introduces many new state classes and increases code complexity. |
Open-closed principle: Can introduce more states with minimal modification in the existing codebase. | |
Removed duplicate code and troublesome conditionals that were difficult to maintain. |
Where is the State Pattern Used?
Use state pattern when you have:
- A system with many different states and conditions. The behavior of the system depends on its state.
- A finite state machine that could otherwise have branch and conditional statements.
What is the State Pattern?
The state pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. It appears as if the object changed its class.
The first statement makes sense, given that the state diagram is based on the idea of states and corresponding behavioral changes when the state machine transitions between various states.
The second statement is more apparent from a client’s perspective because when the object changes its behavior, it appears that it is an instance of some other class. The client doesn’t see all the state transitions that happen within, thus creating a perception that the object changes its class in the runtime.
What is the Difference between Strategy and State Pattern?
The class diagram of both patterns looks quite alike, but both have different intents.
In the strategy pattern, the client initializes a strategy object for the context. Changing a strategy in the runtime is flexible, but a strategy remains appropriate for a context and changes rarely or less frequently than a state would. This pattern uses composition rather than inheritance to dynamically alter the context’s strategy.
In the state pattern, the behaviors are encapsulated in their own state classes, and the client is not completely aware of internal state changes. It is an alternative to conditions statements which are rather difficult to maintain. Thus with this pattern, the dynamic behaviors can be encapsulated and changed at runtime with the state objects.
Using the State Pattern in PHP Today
Phew! We have covered a lot of ground. Let’s recap what we have seen thus far. The state pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. It appears as if the object changed its class.
This article features the CubeTea example from the template method pattern which aims to build a state machine for payment and serving system. The developers devised a state diagram for this system and were able to come up with a solution using conditional statements in a monolithic class.
This solution worked. However, it had design flaws because it was difficult to maintain and any prospected change was predicted to create havoc. The system violated certain important and fundamental design principles – open/closed principle and single responsibility principle.
Given these problems, developers reevaluated their code and decide to encapsulate the varying behaviors that depend on the state. Thus, they defined state classes that contain corresponding behaviors. The client module now instead of using conditional, delegated function calls to state objects using composition.
This new design is more flexible, maintainable, and open to change. It can accommodate new states with quite minor adjustments to the existing codebase.
That’s all. See you in another design pattern article.
Books on Design Patterns
Want to learn more about Design Patterns? There are many great resources online. We recommend the following books for your collection as they both can teach you the theoretical and the application of using design patterns in your day-to-day programming. Feel free to use the following Amazon affiliate links if you’d like to purchase them and a way to support our efforts.
Design Patterns: Elements of Reusable Object-Oriented Software

This is the book that started it all. I believe that every programmer should have a referenced copy to this book at some point in their career. There have been many updates and excellent newer content through the years, but this is a classic. It still stands the test of time and is just as relevant for today as it was in the original printing in the 90s.
Learning PHP Design Patterns

This is an excellent book to go beyond the theory and apply it to writing good PHP code. Learning PHP Design Patterns is published by the popular O’Reilly media company. O’Reilly consistently publishes some of the most useful reference material related to software development. They are known to provide materials that thoroughly cover a topic in a way that is simple to understand. I recommend this book to every PHP developer.
Want to see our full review of books on design patterns? Read our huge review article on over 15 design pattern books.
Design Patterns in PHP Learning Series.
This article is part of our series of design patterns in PHP. We are going through all of the patterns and showing how they can help you build better applications. Browse through our full list of patterns below.
- What are Design Patterns in PHP?
- Books on Design Patterns
- Builder Design Pattern
- Strategy Design Pattern
- Factory Design Pattern
- Prototype Design Pattern
- Singleton Design Pattern
- Adapter Design Pattern
- Bridge Design Pattern
- Iterator Design Pattern
- Composite Design Pattern
- Decorator Design Pattern
- Facade Design Pattern
- Flyweight Design Pattern
- Proxy Design Pattern
- State Design Pattern
- Observer Design Pattern
- Momento Design Pattern
- Mediator Design Pattner
- Chain of Responsibility Design Pattern