/** * Add a state. without a transition. * Normally, you would not use this method directly but instead use * addTransition to add the transitions with the states in one go. * * This method makes sense if you would want to load the statemachine with states * and not transitions, and then add the transitions with regex states. * This saves you the hassle of adding transitions before you add the * regex transitions (just so the states are already known on the machine). * * in case a State is not used in a Transition, it will be orphaned and not * reachable via other states. * * @param State $state * @return boolean true if the state was not know to the machine or wasn't added, false otherwise. */ public function addState(State $state) { //no regex states if ($state->isRegex()) { return false; } //check for duplicates if (isset($this->states[$state->getName()])) { return false; } $this->states[$state->getName()] = $state; return true; }
/** * get all states that match a possible regex state from the set of states * provided * * @param State $regex * a possible regex state. * @param State[] $targets * all target State instances that we check the regex against. * @return State[] an array of State instances from the $targets State * instances that matched the (negated) regex, or the $regex State if it was * not a regex State after all. * @link https://php.net/manual/en/function.preg-match.php * @link http://regexr.com/ for trying out regular expressions */ public static function getAllRegexMatchingStates(State $regex, $targets) { $all = array(); if ($regex->isRegex()) { // lookup all from states that conform to this rgex foreach ($targets as $target) { if (!$target->isRegex() && self::matchesRegex($regex, $target)) { $all[] = $target; } } } else { $all[] = $regex; } return $all; }
/** * @test * @group regex */ public function shouldNotReturnRegexState() { $name = 'rege:.*'; $regex = new State($name); $this->assertFalse($regex->isRegex()); $this->assertFalse($regex->isNormalRegex()); $this->assertFalse($regex->isNegatedRegex()); }
/** * @test * @group regex */ public function shouldReturnArrayOfMatchedStates() { $a = new State('a'); $b = new State('ab'); $c = new State('ba'); $d = new State('abracadabra'); $e = new State('action-hero'); $f = new State('action-bad-guy'); $g = new State('ac'); $targets = array($a, $b, $c, $d, $e, $f, $g); $regex = new State('regex:/.*/'); $this->assertEquals($targets, Utils::getAllRegexMatchingStates($regex, $targets)); $regex = new State('regex:/^a.*/'); $this->assertEquals(array($a, $b, $d, $e, $f, $g), Utils::getAllRegexMatchingStates($regex, $targets)); $regex = new State('regex:/^a.+/'); $this->assertEquals(array($b, $d, $e, $f, $g), Utils::getAllRegexMatchingStates($regex, $targets)); $regex = new State('regex:/^a.*a.+$/'); $this->assertEquals(array($d, $f), Utils::getAllRegexMatchingStates($regex, $targets)); $regex = new State('regex:/^ac.*-.+$/'); $this->assertEquals(array($e, $f), Utils::getAllRegexMatchingStates($regex, $targets)); $regex = new State('ac'); $this->assertFalse($regex->isRegex()); $this->assertEquals(array($g), Utils::getAllRegexMatchingStates($regex, $targets), 'non regex state'); }
/** * * @param State $state_from * @param State $state_to * @param string $event * optional: an event name by which this transition can be * triggered * @param string $rule * optional: one or more fully qualified Rule (sub)class name(s) * to check to see if we are allowed to transition. * This can actually be a ',' seperated string of multiple rules * that will be applied as a chained 'and' rule. * @param string $command * optional: one or more fully qualified Command (sub)class * name(s) to execute for a transition. * This can actually be a ',' seperated string of multiple * commands that will be executed as a composite. * @param callable $callable_guard * optional: a php callable to call. eg: "function(){echo 'closure called';};" * @param callable $callable_transition * optional: a php callable to call. eg: "izzum\MyClass::myStaticMethod" */ public function __construct(State $state_from, State $state_to, $event = null, $rule = self::RULE_EMPTY, $command = self::COMMAND_EMPTY, $callable_guard = self::CALLABLE_NULL, $callable_transition = self::CALLABLE_NULL) { $this->state_from = $state_from; $this->state_to = $state_to; $this->setRuleName($rule); $this->setCommandName($command); $this->setGuardCallable($callable_guard); $this->setTransitionCallable($callable_transition); // setup bidirectional relationship with state this transition // originates from. only if it's not a regex or final state type if (!$state_from->isRegex() && !$state_from->isFinal()) { $state_from->addTransition($this); } // set and sanitize event name $this->setEvent($event); }