For teaching purpose, I would like to create a simple implementation of State Pattern using PHP 7.4. So, I've tried to create a simple "document state machine" starting with Draft, sending to review and, after three "votes", can be published:
<?php
namespace StatePatternPHP;
interface DocumentManagement {
public function review();
public function approve();
public function reject();
public function publish();
}
abstract class State implements DocumentManagement {
protected Document $document;
public function __construct(Document $document) {
$this->document = $document;
}
public function review() {
throw new Exception("Document cannot be reviewed in this current state (". get_class($this).")");
}
public function approve() {
throw new Exception("Document cannot be approved in this current state (". get_class($this).")");
}
public function publish() {
throw new Exception("Document cannot be published in this current state (". get_class($this).")");
}
public function reject() {
throw new Exception("Document cannot be rejected in this current state (". get_class($this).")");
}
}
class Document implements DocumentManagement {
private string $content;
private State $currentState;
private int $approvals = 0;
public function __construct(string $content) {
$this->content = $content;
$this->currentState = new Draft($this);
}
public function getContent(): string {
return $this->content;
}
public function setContent(string $content){
$this->content = $content;
$this->currentState = new Draft($this);
$this->approvals = 0;
}
public function setState(State $state){
$this->currentState = $state;
}
public function addApproval(){
$this->approvals++;
}
public function disapprove(){
$this->approvals--;
}
public function getApprovals(){
return $this->approvals;
}
public function review() {
$this->currentState->review();
}
public function approve() {
$this->currentState->approve();
}
public function publish() {
$this->currentState->publish();
}
public function reject() {
$this->currentState->reject();
}
}
class Draft extends State {
public function review() {
$this->document->setState(new InReview($this->document));
}
}
class InReview extends State {
public function approve() {
$this->document->addApproval();
}
public function publish() {
if($this->document->getApprovals() > 2){ //needs 3 votes at least
$this->document->setState(new Published($this->document));
}else{
parent::publish();
}
}
public function reject() {
$this->document->disapprove();
}
}
class Published extends State {
public function __construct(Document $document) {
parent::__construct($document);
print('document published !');
}
}
$document = new Document("hello world !");
$document->review();
$document->approve();
$document->approve();
$document->approve();
$document->publish();
- So, Could this be considered a valid State GoF Pattern implementation ?
- Is this a valid S.O.L.I.D too? My main fear is about how open is to new state...
- Is there any kind of improvement to do on it?
The sand-boxed version is here.