From 16796864d17f6f01c9e859fe058c05fe87cf55b1 Mon Sep 17 00:00:00 2001 From: Christopher CHEN Date: Fri, 11 Oct 2019 14:58:22 +0800 Subject: [PATCH 1/2] feat: ability to use callable as action --- src/StateMachine.php | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/StateMachine.php b/src/StateMachine.php index 972e5e6..d827254 100644 --- a/src/StateMachine.php +++ b/src/StateMachine.php @@ -24,7 +24,8 @@ class StateMachine * StateMachine constructor. * @param array $transitions * - * $transitions example: + * $transitions example for a deterministic finite state machine: + * https://en.wikipedia.org/wiki/Deterministic_finite_automaton * [ * 'State1' => [ * 'action1' => 'State1', @@ -38,20 +39,22 @@ class StateMachine * ], * ] * - * You may use Closure to do real actions in the target state: + * $transitions example for a nondeterministic finite state machine: + * https://en.wikipedia.org/wiki/Nondeterministic_finite_automaton + * You may use a `Closure` or a `callable array` to perform checks before transitioning, e.g.: * [ - * 'State1' => [ - * 'action1' => 'State1', - * 'action2' => 'State1', + * 'locked' => [ + * 'push' => 'locked', + * // use callable array: + * 'coin' => [Turnstile::class, 'insertCoin'], * ], - * 'State2' => [ - * 'action3' => function ($stateMachine) { - * // Do something here - * return 'State3'; + * 'unlocked' => [ + * // use closure: + * 'push' => function ($stateMachine) { + * Turnstile::useCoin(); + * return Turnstile::hasCoin() ? 'unlocked' : 'locked'; * }, - * ], - * 'State3' => [ - * 'action4' => 'State1', + * 'coin' => [$turnstile, 'insertCoin'], * ], * ] * @param array|null $history @@ -98,8 +101,17 @@ public function doAction($action, &$payload = null) Exception::INVALID_ACTION ); } - if (($state = $this->transitions[$this->currentState][$action]) instanceof Closure) { - $state = $state($this, $payload); + $state = $this->transitions[$this->currentState][$action]; + if (!is_string($state)) { + if (is_callalbe($state)) { + $state = call_user_func($state, $this, $payload); + } + } + if (!is_string($state) || !isset($this->transitions[$state])) { + throw new Exception( + sprintf('Invalid action "%s" for current state "%s"', $action, $this->currentState), + Exception::INVALID_ACTION + ); } $this->previousState = $this->currentState; $this->currentState = $state; From 544a7db94fb2eb1ee03652ef576524bad9f9d25d Mon Sep 17 00:00:00 2001 From: Christopher CHEN Date: Fri, 11 Oct 2019 15:04:36 +0800 Subject: [PATCH 2/2] feat: ability to use callable as action --- src/StateMachine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StateMachine.php b/src/StateMachine.php index d827254..09f076e 100644 --- a/src/StateMachine.php +++ b/src/StateMachine.php @@ -103,7 +103,7 @@ public function doAction($action, &$payload = null) } $state = $this->transitions[$this->currentState][$action]; if (!is_string($state)) { - if (is_callalbe($state)) { + if (is_callable($state)) { $state = call_user_func($state, $this, $payload); } }