The LoaderArray should always be the object that is used by other Loader implementations. This means other LoaderInterface implementations should delegate the loading to this class. You could implement the Loader interface and use object composition for the LoaderArray. A transition will be unique per StateMachine and is uniquely defined by the tuple of a starting state name and a destination state name. The data of the rule, command or the identity (===) of the states does not matter when a Transition is added to a StateMachine. The machine will react on a first come first serve basis. In short, just make sure your configuration data is ok. TRICKY: if multiple transitions share the same State object (for example as their origin/from state or their destination/to state) then at least make sure that those states share the exact same data. Ideally, they should point to the same State instance. Otherwise, transitions and states are actually stored on the statemachine on a first wins basis (the later transition/state instance is not stored). Transitions will be sorted before they are added to the machine based on if they contain a regex or not. All regex transitions will be added to the machine after the non-regex transitions have been added.
See also: Transitions
See also: State
See also: PDO
Author: Rolf Vreijdenberger
Inheritance: implements izzum\statemachine\loader\Loader
Example #1
0
 /**
  * {@inheritDoc}
  */
 public function load(StateMachine $stateMachine)
 {
     //decode the json in a php object structure
     $decoded = yaml_parse($this->getYaml(), false);
     //yaml decoding returns a php array.
     $name = $stateMachine->getContext()->getMachine();
     $found = false;
     $data = null;
     if (is_array(@$decoded['machines'])) {
         foreach ($decoded['machines'] as $data) {
             if ($data['name'] === $name) {
                 $found = true;
                 break;
             }
         }
     }
     if (!$found) {
         //no name match found
         throw new Exception(sprintf('no machine data found for "%s" in yaml. seems like a wrong configuration.', $name), Exception::BAD_LOADERDATA);
     }
     //accessing an array with an @ error suppresion operator ('shut the f**k up' operator),
     //allows you to get properties, even if they do not exist, without notices.
     //this lets us be a little lazy in mapping the array properties to the state and transition properties
     $states = array();
     foreach ($data['states'] as $state) {
         $tmp = new State($state['name'], $state['type'], @$state['entry_command'], @$state['exit_command'], @$state['entry_callable'], @$state['exit_callable']);
         $tmp->setDescription(@$state['description']);
         $states[$tmp->getName()] = $tmp;
     }
     $transitions = array();
     foreach ($data['transitions'] as $transition) {
         $tmp = new Transition($states[$transition['state_from']], $states[$transition['state_to']], @$transition['event'], @$transition['rule'], @$transition['command'], @$transition['guard_callable'], @$transition['transition_callable']);
         $tmp->setDescription(@$transition['description']);
         $transitions[] = $tmp;
     }
     //delegate to loader
     $loader = new LoaderArray($transitions);
     return $loader->load($stateMachine);
 }
 /**
  * {@inheritDoc}
  * This is an implemented method from the Loader interface.
  * All other methods are actually implemented methods from the Adapter
  * class.
  */
 public function load(StateMachine $statemachine)
 {
     $data = $this->getLoaderData($statemachine->getContext()->getMachine());
     // delegate to LoaderArray
     $loader = new LoaderArray($data);
     $loader->load($statemachine);
 }
 /**
  * {@inheritDoc}
  */
 public function load(StateMachine $stateMachine)
 {
     //load the xml in a php object structure. suppres warning with @ operator since we explicitely check the return value
     $xml = @simplexml_load_string($this->getXML());
     if ($xml === false) {
         //could not load
         throw new Exception(sprintf('could not load xml data. check the xml format'), Exception::BAD_LOADERDATA);
     }
     $name = $stateMachine->getContext()->getMachine();
     $found = false;
     $data = null;
     foreach ($xml->machine as $data) {
         if ((string) @$data->name === $name) {
             $found = true;
             break;
         }
     }
     if (!$found) {
         //no name match found
         throw new Exception(sprintf('no machine data found for %s in xml. seems like a wrong configuration.', $name), Exception::BAD_LOADERDATA);
     }
     //accessing xml as an object with the @ error suppresion operator ('shut the f**k up' operator)
     //allows you to get properties, even if they do not exist, without notices.
     //this let's us be a littlebit lazy since we know some nonessential properties could not be there
     $states = array();
     foreach ($data->states->state as $state) {
         $tmp = new State((string) $state->name, (string) $state->type, (string) @$state->entry_command, (string) @$state->exit_command, (string) @$state->entry_callable, (string) @$state->exit_callable);
         $tmp->setDescription((string) @$state->description);
         $states[$tmp->getName()] = $tmp;
     }
     $transitions = array();
     foreach ($data->transitions->transition as $transition) {
         $tmp = new Transition($states[(string) @$transition->state_from], $states[(string) @$transition->state_to], (string) @$transition->event, (string) @$transition->rule, (string) @$transition->command, (string) @$transition->guard_callable, (string) @$transition->transition_callable);
         $tmp->setDescription((string) @$transition->description);
         $transitions[] = $tmp;
     }
     //delegate to loader
     $loader = new LoaderArray($transitions);
     return $loader->load($stateMachine);
 }
Example #4
0
 /**
  * {@inheritDoc}
  */
 public function load(StateMachine $stateMachine)
 {
     //decode the json in a php object structure
     $decoded = json_decode($this->getJSON(), false);
     if (!$decoded) {
         //could not decode (make sure that fully qualified names are escaped with
         //2 backslashes: \\izzum\\commands\\Null and that only double quotes are used.
         throw new Exception(sprintf('could not decode json data. did you only use double quotes? check the json format against %s', 'http://jsonlint.com/'), Exception::BAD_LOADERDATA);
     }
     $name = $stateMachine->getContext()->getMachine();
     $found = false;
     if (is_array(@$decoded->machines)) {
         foreach ($decoded->machines as $data) {
             if ($data->name === $name) {
                 $found = true;
                 break;
             }
         }
     }
     if (!$found) {
         //no name match found
         throw new Exception(sprintf('no machine data found for %s in json. seems like a wrong configuration.', $name), Exception::BAD_LOADERDATA);
     }
     //accessing json as an object with an @ error suppresion operator ('shut the f**k up' operator),
     //allows you to get properties, even if they do not exist, without notices.
     //this lets us be a little lazy in mapping the json properties to the state and transition properties
     $states = array();
     foreach ($data->states as $state) {
         $tmp = new State($state->name, $state->type, @$state->entry_command, @$state->exit_command, @$state->entry_callable, @$state->exit_callable);
         $tmp->setDescription(@$state->description);
         $states[$tmp->getName()] = $tmp;
     }
     $transitions = array();
     foreach ($data->transitions as $transition) {
         $tmp = new Transition($states[$transition->state_from], $states[$transition->state_to], @$transition->event, @$transition->rule, @$transition->command, @$transition->guard_callable, @$transition->transition_callable);
         $tmp->setDescription(@$transition->description);
         $transitions[] = $tmp;
     }
     //delegate to loader
     $loader = new LoaderArray($transitions);
     return $loader->load($stateMachine);
 }
 /**
  * @test
  */
 public function shouldAddRegexesLoaderOnlyWhenStatesAreSet()
 {
     $context = new Context(new Identifier(Identifier::NULL_ENTITY_ID, Identifier::NULL_STATEMACHINE));
     $machine = new StateMachine($context);
     $transitions = array();
     $s1 = new State("1");
     $s2 = new State("2");
     $s3 = new State("3");
     //many to many
     $transitions[] = new Transition(new State('regex:/.*/'), new State('regex:/.*/'));
     $loader = new LoaderArray($transitions);
     $this->assertEquals(count($transitions), $loader->count());
     $this->assertEquals(1, $loader->count());
     $this->assertEquals(0, count($machine->getStates()));
     $this->assertEquals(0, count($machine->getTransitions()));
     $count = $loader->load($machine);
     $this->assertEquals(0, $count, 'nothing because there are no known states');
     $this->assertTrue($machine->addState($s1));
     $this->assertTrue($machine->addState($s2));
     $this->assertTrue($machine->addState($s3));
     $this->assertEquals(3, count($machine->getStates()));
     $this->assertFalse($machine->addState($s1));
     $this->assertFalse($machine->addState($s2));
     $this->assertFalse($machine->addState($s3));
     $this->assertEquals(3, count($machine->getStates()));
     $count = $loader->load($machine);
     $this->assertEquals(6, count($machine->getTransitions()));
     $this->assertEquals(6, $count, 'regexes have matched all states and created a mesh');
     $count = $loader->load($machine);
     $this->assertEquals(0, $count, 'transitions are not added since they have already been added');
     $this->assertEquals(3, count($machine->getStates()));
     $this->assertEquals(6, count($machine->getTransitions()));
 }
 /**
  * https://github.com/rolfvreijdenberger/izzum-statemachine/issues/7
  * @test
  */
 public function shouldFailConfigurationCheckForMachineWithBadCallables()
 {
     $transitions = array();
     $s1 = new State("1");
     $s1->setEntryCallable('foobar');
     $s2 = new State("2");
     $s2->setEntryCallable('foobar');
     $s3 = new State("3");
     $s3->setEntryCallable('foobar');
     $transitions[] = new Transition($s1, $s2);
     $transitions[] = new Transition($s2, $s3);
     //3 states, 3 bad callables
     //2 transitions, 4 bad callables
     //total of 7
     foreach ($transitions as $transition) {
         $transition->setGuardCallable('foobar');
         $transition->setTransitionCallable('foobar');
     }
     $loader = new LoaderArray($transitions);
     $context = new Context(new Identifier(Identifier::NULL_ENTITY_ID, Identifier::NULL_STATEMACHINE));
     $machine = new StateMachine($context);
     $loader->load($machine);
     $exceptions = Utils::checkConfiguration($machine);
     $this->assertEquals(7, count($exceptions));
     $this->assertTrue(true, 'basic machine with bad callables will be configured incorrectly');
 }