3.10. State
3.10.1. Purpose
Encapsulate varying behavior for the same routine based on an object’s state. This can be a cleaner way for an object to change its behavior at runtime without resorting to large monolithic conditional statements.
3.10.2. UML Diagram
![Alt State UML Diagram](../../../_images/uml9.png)
3.10.3. Code
You can also find this code on GitHub
OrderContext.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class OrderContext
8{
9 private State $state;
10
11 public static function create(): OrderContext
12 {
13 $order = new self();
14 $order->state = new StateCreated();
15
16 return $order;
17 }
18
19 public function setState(State $state)
20 {
21 $this->state = $state;
22 }
23
24 public function proceedToNext()
25 {
26 $this->state->proceedToNext($this);
27 }
28
29 public function toString()
30 {
31 return $this->state->toString();
32 }
33}
State.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7interface State
8{
9 public function proceedToNext(OrderContext $context);
10
11 public function toString(): string;
12}
StateCreated.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class StateCreated implements State
8{
9 public function proceedToNext(OrderContext $context)
10 {
11 $context->setState(new StateShipped());
12 }
13
14 public function toString(): string
15 {
16 return 'created';
17 }
18}
StateShipped.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class StateShipped implements State
8{
9 public function proceedToNext(OrderContext $context)
10 {
11 $context->setState(new StateDone());
12 }
13
14 public function toString(): string
15 {
16 return 'shipped';
17 }
18}
StateDone.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class StateDone implements State
8{
9 public function proceedToNext(OrderContext $context)
10 {
11 // there is nothing more to do
12 }
13
14 public function toString(): string
15 {
16 return 'done';
17 }
18}
3.10.4. Test
Tests/StateTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State\Tests;
6
7use DesignPatterns\Behavioral\State\OrderContext;
8use PHPUnit\Framework\TestCase;
9
10class StateTest extends TestCase
11{
12 public function testIsCreatedWithStateCreated()
13 {
14 $orderContext = OrderContext::create();
15
16 $this->assertSame('created', $orderContext->toString());
17 }
18
19 public function testCanProceedToStateShipped()
20 {
21 $contextOrder = OrderContext::create();
22 $contextOrder->proceedToNext();
23
24 $this->assertSame('shipped', $contextOrder->toString());
25 }
26
27 public function testCanProceedToStateDone()
28 {
29 $contextOrder = OrderContext::create();
30 $contextOrder->proceedToNext();
31 $contextOrder->proceedToNext();
32
33 $this->assertSame('done', $contextOrder->toString());
34 }
35
36 public function testStateDoneIsTheLastPossibleState()
37 {
38 $contextOrder = OrderContext::create();
39 $contextOrder->proceedToNext();
40 $contextOrder->proceedToNext();
41 $contextOrder->proceedToNext();
42
43 $this->assertSame('done', $contextOrder->toString());
44 }
45}