Last active
November 11, 2016 15:22
-
-
Save victorjonsson/c9a89574d0d6bcad1ea5d1b093d16d4b to your computer and use it in GitHub Desktop.
fsm.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
interface State { | |
public function getState(); | |
} | |
abstract class StateAbstract implements State { | |
const STATE_INIT = 'STATE_INIT'; | |
protected $currentState = self::STATE_INIT; | |
public function getState() | |
{ | |
return $this->currentState; | |
} | |
protected function transitionToNewState($newState) | |
{ | |
$this->guardAgainsInvalidStateTransition($newState); | |
call_user_func_array([$this, 'enterNewState'], func_get_args()); | |
} | |
private function guardAgainstInvalidStateTransition() | |
{ | |
$validTransition = null; | |
foreach (static::$stateTransitions as $transition) { | |
if ($transition['to'] == $newState) { | |
$validTransition = in_array($this->currentState, $transition['from']); | |
break; | |
} | |
} | |
if ($validTransition === null) { | |
throw new UnknownStateException('Unknown state'); | |
} elseif (!$validTransition) { | |
throw new InvalidStateTransationException(static::class, $this->currentState, $newState); | |
} | |
} | |
private function enterNewState($newState) | |
{ | |
$callback = $this->getStateTransitionCallback($newState); | |
if (is_callable($callback)) { | |
call_user_func_array($callback, func_get_args()); | |
$this->currentState = $newState; | |
} | |
} | |
/** | |
* Use this to execute a function before the new state is set | |
* @param string $newState | |
* @return callable|null | |
*/ | |
protected function getStateTransitionCallback($newState) | |
{ | |
return null; | |
} | |
} | |
trait StateTrait extends AbstractState {} | |
class SomeEntityHavingStateTransitions extends StateAbstract | |
{ | |
const STATE_A = ''; | |
const STATE_B = ''; | |
const STATE_C = ''; | |
protected static $stateTransitions = [ | |
['to' => self::STATE_B, 'from' => [self::STATE_A]], | |
['to' => self::STATE_C, 'from' => [self::STATE_B]], | |
]; | |
protected function getStateTransitionCallback($newState) { | |
switch ($newState) { | |
case self::STATE_A: | |
return function () { | |
}; | |
break; | |
// and so on... | |
} | |
} | |
public function setState($newState) { | |
$this->transitionToNewState($newState); | |
} | |
} | |
class SomeEntity extends StateAbstract { | |
const STATE_IN_PROGRESS = 'IN_PROGRESS'; | |
const STATE_SUCCESSFUL = 'SUCCESS'; | |
const STATE_FAILED = 'FAILED'; | |
protected static $stateTransitions = [ | |
['to' => self::STATE_IN_PROGRESS, 'from' => [self::STATE_FAILED, self::STATE_INIT]], | |
['to' => self::STATE_FAILED, 'from' => [self::STATE_IN_PROGRESS]] | |
['to' => self::STATE_SUCCESSFUL, 'from' => [self::STATE_IN_PROGRESS]], | |
]; | |
public function transitionToInProgressState(DateTime $date, $data) | |
{ | |
$this->transitionToNewState(self::STATE_IN_PROGRESS); | |
$this->startedDate = $date; | |
$this->data = $data; | |
} | |
public function transitionToFailedState(DateTime $date) | |
{ | |
$this->transitionToNewState(self::STATE_FAILED); | |
$this->failedDate = $date; | |
} | |
public function transitionToSuccessfulState(DateTime $date, $data) | |
{ | |
$this->transitionToNewState(self::STATE_SUCCESSFUL); | |
$this->sucessfullDate = $date; | |
} | |
} | |
class DataProcessor { | |
function proccess(SomeEntity $data) | |
{ | |
switch ($data->getState()) { | |
case SomeEntity::INIT_STATE: | |
$data->transitionToInProgressState(new DateTime(), $this->fetchData()); | |
$this->doProcess($data); | |
break; | |
case SomeEntity::STATE_FAILED: | |
$data->transitionToInProgressState(new DateTime(), $data->getData()); | |
$this->doProcess($data); | |
break; | |
default: | |
throw new UnexpectedValueException( | |
'Can not process data when in state "'.$data->getState().'"' | |
); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment