DEV Community

manuel
manuel

Posted on • Edited on

State Pattern example in PHP

The State pattern is excellent for making the behavior of an object dependent on its state. In my example, it is an elevator that can have the following states:

  • Open (Open)
  • Closed (Close)
  • In Motion (Move)
  • Stand (Stop)

From this, the following interface can be derived

<?php

namespace Elevator;

interface ElevatorStateInterface
{
    public function open();
    public function close();
    public function move();
    public function stop();
}
Enter fullscreen mode Exit fullscreen mode

Now we need a class ElevatorState that implements the interface

<?php

namespace Elevator;

class ElevatorState implements ElevatorStateInterface
{
    public function close()
    {
        throw new \Elevator\Exception\IllegalStateTransitionException();
    }

    public function move()
    {
        throw new \Elevator\Exception\IllegalStateTransitionException();
    }

    public function open()
    {
        throw new \Elevator\Exception\IllegalStateTransitionException();
    }

    public function stop()
    {
        throw new \Elevator\Exception\IllegalStateTransitionException();
    }
}
Enter fullscreen mode Exit fullscreen mode

By default, all methods throw an exception. In my case it is an IllegalStateTransitionException which inherits from LogicException.

Now we can implement the individual states. In this example, the Move state.

<?php

namespace Elevator\State;

class Move extends \Elevator\ElevatorState
{
    public function move()
    {
        return new Move();
    }

    public function stop()
    {
        return new Stop();
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, not all methods are implemented from ElevatorState.
Exactly this, which are not allowed for the current state.

The class Elevator

<?php

namespace Elevator;

class Elevator
{
    private $state;

    function getState()
    {
        return $this->state;
    }

    function setState(ElevatorStateInterface $state)
    {
        $this->state = $state;
        print "set state to : " . get_class($state) . PHP_EOL;
    }

    public function __construct()
    {
        $this->setState(new \Elevator\State\Stop());
    }

    public function isOpen()
    {
        return $this->state instanceof \Elevator\State\Open;
    }

    public function open()
    {
        $this->setState($this->state->open());
    }

    public function close()
    {
        $this->setState($this->state->close());
    }

    public function move()
    {
        $this->setState($this->state->move());
    }

    public function stop()
    {
        $this->setState($this->state->stop());
    }
}
Enter fullscreen mode Exit fullscreen mode

If you call the class with

<?php
$elevator = new Elevator\Elevator();
Enter fullscreen mode Exit fullscreen mode

it gets the state Stop by the constructor. This means we can switch from this state to the Open state.

<?php
$elevator->open();
Enter fullscreen mode Exit fullscreen mode

Now, I try this

<?php
$elevator->move();
Enter fullscreen mode Exit fullscreen mode

An error will happen

PHP Fatal error: Uncaught Elevator\Exception\IllegalStateTransitionException
Enter fullscreen mode Exit fullscreen mode

because the door must first be closed.

<?php
$elevator->close();
Enter fullscreen mode Exit fullscreen mode

The complete source of this example is available on GitHub.

Top comments (4)

Collapse
 
ben profile image
Ben Halpern

Thanks for this

Collapse
 
tecnomoz profile image
Tecno-Moz

How great I liked logic

Collapse
 
fippu82 profile image
Philippe • Edited

I don't get it. Where did you implement the logic which says that the door must be closed before the move state can be set?

Collapse
 
mnlwldr profile image
manuel

„Open“ doesn’t implement the method „move“ (github.com/mnlwldr/State-Pattern/b...).
That means when you call the method „move“ it throws an exception because in this state, move is not allowed.