/** * {@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); }
/** * {@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} * Load the statemachine via a document in a mongodb collection. * * The document, originally loaded as a json string (see JSON::getJSONSchema) * is stored at the mongodb collection 'configuration' by default. * multiple machine definitions can be provided in a single document, or in multiple documents in the collection. * The first document containing the 'machines.name' key with the value matching * the name of the $statemachine is used. * * You could use the ReaderWriterDelegator to use another source to load the configuration from. */ public function load(StateMachine $statemachine) { //use the JSON loader to load the configuration (see the json schema we expect in JSON::getJSONSchema) //mongodb does not store JSON but documents (converts the json structure) and the mongodb //php library returns these documents as php objects. //therefore, we json_encode it again, so it can be json_decoded in the JSON class :-( //alternatively, we could write a PHP Loader, but the assumption is that the speed gain is not worth it. $loader = new JSON(json_encode($this->getClient()->izzum->configuration->findOne(array("machines.name" => $statemachine->getContext()->getMachine())))); $count = $loader->load($statemachine); return $count; }
/** * {@inheritDoc} * Load the statemachine with data from a JSON string. * the JSON string is stored at the redis key '<prefix:>configuration' by default. * you can alter the configuration key by using Redis::setPrefix() and Redis::setConfigurationKey() * * First, the key '<prefix>:configuration:<machine-name>' is checked for existence. * If it exists, take the configuration from that key, else take the configuration form * the '<prefix>:configuration' key. * * This method can be overriden in a subclass to use another loader when * the data is stored in redis in YAML or XML form for example. * You could use the ReaderWriterDelegator to use another source to load the configuration from. */ public function load(StateMachine $statemachine) { //use the JSON loader to load the configuration (see the json schema we expect in JSON::getJSONSchema) $key = $this->getConfigurationKey(); $redis = $this->getRedis(); $specific_key = sprintf(self::KEY_CONFIGURATION_SPECIFIC, $key, $statemachine->getContext()->getMachine()); if ($redis->exists($specific_key)) { $key = $specific_key; } $loader = new JSON($this->getRedis()->get($key)); $count = $loader->load($statemachine); return $count; }
/** * {@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) { //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); }
/** * creates plantuml state output for a statemachine * * @param string $machine * @return string plant uml code, this can be used to render an image * @link http://www.plantuml.com/plantuml/ * @link http://plantuml.sourceforge.net/state.html * @throws Exception */ public function createStateDiagram(StateMachine $machine) { $transitions = $machine->getTransitions(); // all states are aliased so the plantuml parser can handle the names $aliases = array(); $end_states = array(); $EOL = "\\n\\" . PHP_EOL; /* for multiline stuff in plantuml */ $NEWLINE = PHP_EOL; // start with declaration $uml = "@startuml" . PHP_EOL; // skins for colors etc. $uml .= $this->getPlantUmlSkins() . PHP_EOL; // the order in which transitions are executed $order = array(); // create the diagram by drawing all transitions foreach ($transitions as $t) { // get states and state aliases (plantuml cannot work with certain // characters, so therefore we create an alias for the state name) $from = $t->getStateFrom(); $from_alias = $this->plantUmlStateAlias($from->getName()); $to = $t->getStateTo(); $to_alias = $this->plantUmlStateAlias($to->getName()); // get some names to display $command = $t->getCommandName(); $rule = self::escape($t->getRuleName()); $name_transition = $t->getName(); $description = $t->getDescription() ? "description: '" . $t->getDescription() . "'" . $EOL : ''; $event = $t->getEvent() ? "event: '" . $t->getEvent() . "'" . $EOL : ''; $f_description = $from->getDescription(); $t_description = $to->getDescription(); $f_exit = $from->getExitCommandName(); $f_entry = $from->getEntryCommandName(); $t_exit = $to->getExitCommandName(); $t_entry = $to->getEntryCommandName(); // only write aliases if not done before if (!isset($aliases[$from_alias])) { $uml .= 'state "' . $from . '" as ' . $from_alias . PHP_EOL; $uml .= "{$from_alias}: description: '" . $f_description . "'" . PHP_EOL; $uml .= "{$from_alias}: entry / '" . $f_entry . "'" . PHP_EOL; $uml .= "{$from_alias}: exit / '" . $f_exit . "'" . PHP_EOL; $aliases[$from_alias] = $from_alias; } // store order in which transitions will be handled if (!isset($order[$from_alias])) { $order[$from_alias] = 1; } else { $order[$from_alias] = $order[$from_alias] + 1; } // get 'to' alias if (!isset($aliases[$to_alias])) { $uml .= 'state "' . $to . '" as ' . $to_alias . PHP_EOL; $aliases[$to_alias] = $to_alias; $uml .= "{$to_alias}: description: '" . $t_description . "'" . PHP_EOL; $uml .= "{$to_alias}: entry / '" . $t_entry . "'" . PHP_EOL; $uml .= "{$to_alias}: exit / '" . $t_exit . "'" . PHP_EOL; } // write transition information $uml .= $from_alias . ' --> ' . $to_alias; $uml .= " : <b><size:10>{$name_transition}</size></b>" . $EOL; $uml .= $event; $uml .= "transition order from '{$from}': <b>" . $order[$from_alias] . "</b>" . $EOL; $uml .= "rule/guard: '{$rule}'" . $EOL; $uml .= "command/action: '{$command}'" . $EOL; $uml .= $description; $uml .= PHP_EOL; // store possible end states aliases if ($t->getStateFrom()->isFinal()) { $end_states[$from_alias] = $from_alias; } if ($t->getStateTo()->isFinal()) { $end_states[$to_alias] = $to_alias; } } // only one begin state $initial = $machine->getInitialState(); $initial = $initial->getName(); $initial_alias = $this->plantUmlStateAlias($initial); if (!isset($aliases[$initial_alias])) { $uml .= 'state "' . $initial . '" as ' . $initial_alias . PHP_EOL; } $uml .= "[*] --> {$initial_alias}" . PHP_EOL; // note for initial alias with explanation $uml .= "note right of {$initial_alias} {$NEWLINE}"; $uml .= "state diagram for machine '" . $machine->getContext()->getMachine() . "'{$NEWLINE}"; $uml .= "created by izzum plantuml generator {$NEWLINE}"; $uml .= "@link http://plantuml.sourceforge.net/state.html\"" . $NEWLINE; $uml .= "end note" . $NEWLINE; // add end states to diagram foreach ($end_states as $end) { $uml .= "{$end} --> [*]" . PHP_EOL; } // close plantuml $uml .= "@enduml" . PHP_EOL; return $uml; }