|
@@ -2,22 +2,20 @@
|
|
|
|
|
|
* Finite state machine writer.
|
|
|
*
|
|
|
- * Keeping the FSM writer (generation) functions out of the root class minimize
|
|
|
+ * Keeping the FSM writer (generation) functions out of the root class minimizes
|
|
|
* the code volume for FSM readers, which should represent the majority of FSM
|
|
|
* users.
|
|
|
*
|
|
|
- * @copyright (c) 2007 OSI
|
|
|
+ * @copyright (c) 2007-2012 Ouest Systèmes Informatiques
|
|
|
* @author Frédéric G. MARAND
|
|
|
* @license Licensed under the CeCILL 2.0
|
|
|
- * @version CVS: $Id: Fsm_Writer.php,v 1.1 2007-06-10 19:42:35 marand Exp $
|
|
|
* @link http:
|
|
|
- * @since Not applicable yet
|
|
|
- * @package fsm
|
|
|
- * @subpackage fsm.core
|
|
|
*/
|
|
|
|
|
|
+namespace OSInet\Finite_State_Machine;
|
|
|
+
|
|
|
|
|
|
- * needed notably for autoload and func_name()
|
|
|
+ * Needed notably for func_name()
|
|
|
*/
|
|
|
require_once('misc.php');
|
|
|
|
|
@@ -26,84 +24,79 @@ $erFiniteStateMachine = error_reporting(E_ALL|E_STRICT);
|
|
|
|
|
|
* Read/Write concrete FSM class.
|
|
|
*
|
|
|
- * This (concrete) class extends the default abstract
|
|
|
- * Finite_State_Machine to add write behaviours. It is to be used by
|
|
|
- * applications wishing to write FSM files. Other applications should not
|
|
|
- * use it to avoid code bloat.
|
|
|
- * @package fsm
|
|
|
- * @subpackage fsm.core
|
|
|
+ * This (concrete) class extends the default abstract Finite_State_Machine to
|
|
|
+ * add write behaviours. It is to be used by applications wishing to write FSM
|
|
|
+ * files. Other applications should not use it to avoid code bloat.
|
|
|
*/
|
|
|
-class Fsm_Writer extends Finite_State_Machine
|
|
|
- {
|
|
|
- const VERSION = '$Id: Fsm_Writer.php,v 1.1 2007-06-10 19:42:35 marand Exp $';
|
|
|
+class Writer extends Machine {
|
|
|
|
|
|
- public function __construct()
|
|
|
- {
|
|
|
- $this->f_transitions = array();
|
|
|
+ const FORMAT_VERSION = '1.1';
|
|
|
+
|
|
|
+ public function __construct() {
|
|
|
+ $this->fTransitions = array();
|
|
|
parent::__construct();
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- * Return the initial and final states from an abstract FSM graph
|
|
|
+ * Return the initial and final states from an abstract FSM graph.
|
|
|
+ *
|
|
|
+ * @param array $graph
|
|
|
+ * A concrete FSM derived from the abstract FSM.
|
|
|
*
|
|
|
- * @param array $graph Abstract FSM
|
|
|
* @return array
|
|
|
*/
|
|
|
- protected function getSpecialStates($graph)
|
|
|
- {
|
|
|
- $ret[0] = NULL;
|
|
|
- $ret[1] = NULL;
|
|
|
- foreach ($graph['states'] as $state)
|
|
|
- {
|
|
|
- if (isset($state['type']) && $state['type'] == 'initial')
|
|
|
- {
|
|
|
+ protected function getSpecialStates($graph) {
|
|
|
+ $ret = array(NULL, NULL);
|
|
|
+ foreach ($graph['states'] as $state) {
|
|
|
+ if (isset($state['type']) && $state['type'] == 'initial') {
|
|
|
$ret[0] = $state['name'];
|
|
|
- }
|
|
|
- if (isset($state['type']) && $state['type'] == 'final')
|
|
|
- {
|
|
|
+ }
|
|
|
+ if (isset($state['type']) && $state['type'] == 'final') {
|
|
|
$ret[1] = $state['name'];
|
|
|
- }
|
|
|
- if (isset($ret[0]) && isset($ret[1]))
|
|
|
- {
|
|
|
+ }
|
|
|
+ if (isset($ret[0]) && isset($ret[1])) {
|
|
|
break;
|
|
|
- }
|
|
|
}
|
|
|
- return $ret;
|
|
|
}
|
|
|
+ return $ret;
|
|
|
+ }
|
|
|
|
|
|
|
|
|
* Return the transitions (triggers) available from a given state
|
|
|
*
|
|
|
- * @param array $graph Abstract FSM
|
|
|
- * @param string $id State id in the abstract graph (not id in the XML FSM)
|
|
|
+ * @param array $graph
|
|
|
+ * A concrete Machine.
|
|
|
+ * @param string $id
|
|
|
+ * State id in the abstract graph (not id in the XML FSM).
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
*/
|
|
|
- protected function getTransitionsForState($graph, $id)
|
|
|
- {
|
|
|
+ protected function getTransitionsForState($graph, $id) {
|
|
|
$ret = array();
|
|
|
- foreach ($graph['transitions'] as $txid => $transition)
|
|
|
- {
|
|
|
+ foreach ($graph['transitions'] as $txid => $transition) {
|
|
|
|
|
|
- if ($transition['from'] == $id)
|
|
|
- {
|
|
|
+ if ($transition['from'] == $id) {
|
|
|
$ret[] = $txid;
|
|
|
- }
|
|
|
}
|
|
|
- return $ret;
|
|
|
}
|
|
|
+ return $ret;
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- * Generate the XML FSM file and optionally PHP stubs file from an abstract
|
|
|
- * FSM representation as an array of states and transitions
|
|
|
+ * Generate the XML FSM file and optionally PHP stubs file.
|
|
|
+ *
|
|
|
+ * Source is an abstract FSM representation as an array of states/transitions.
|
|
|
*
|
|
|
* @param array $graph
|
|
|
* @param string $prefix
|
|
|
* @param boolean $php
|
|
|
* @param boolean $overwrite
|
|
|
+ *
|
|
|
* @todo take $overwrite and $php into account
|
|
|
+ *
|
|
|
* @deprecated
|
|
|
*/
|
|
|
- public function old_save_fsm($graph, $prefix = 'fsm', $php = FALSE, $overwrite = FALSE)
|
|
|
- {
|
|
|
+ public function oldSaveFsm($graph, $prefix = 'fsm', $php = FALSE, $overwrite = FALSE) {
|
|
|
echo "This is a dump for FSM $prefix"
|
|
|
. ', ' . ($php ? 'with' : 'without') . ' PHP generation'
|
|
|
. ', ' . ($overwrite ? 'with' : 'without') . ' overwrite mode'
|
|
@@ -124,12 +117,13 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
$fsm->setAttribute('init', $init);
|
|
|
$fsm->setAttribute('final', $final);
|
|
|
|
|
|
- foreach ($graph['states'] as $id => $state)
|
|
|
- {
|
|
|
+ foreach ($graph['states'] as $id => $state) {
|
|
|
$state_node = new DOMElement('state');
|
|
|
$fsm->appendChild($state_node);
|
|
|
- $state_node->setAttribute('id', $state['name']);
|
|
|
+
|
|
|
+ $state_node->setAttribute('id', $state['name']);
|
|
|
$transitions = $this->getTransitionsForState($graph, $id);
|
|
|
+
|
|
|
|
|
|
* FGM on ne peut pas simplement boucler ainsi: on a dans le graphe
|
|
|
* abstrait une transition par évènement, alors que dans la FSM les
|
|
@@ -137,8 +131,7 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
* de boucler ainsi, on crée un "event" pour chaque transition alors
|
|
|
* qu'il faut créer 1 event et "n" next.
|
|
|
*/
|
|
|
- foreach ($transitions as $transition_id)
|
|
|
- {
|
|
|
+ foreach ($transitions as $transition_id) {
|
|
|
$event_node = new DOMElement('event');
|
|
|
$state_node->appendChild($event_node);
|
|
|
$transition = $graph['transitions'][$transition_id];
|
|
@@ -147,34 +140,32 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
* @todo support other event types
|
|
|
*/
|
|
|
$event_node->setAttribute('type', 'string');
|
|
|
- }
|
|
|
}
|
|
|
+ }
|
|
|
$doc->save("$prefix.xml");
|
|
|
print_r($graph['transitions']);
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- * Add a new state to the transitions table
|
|
|
+ * Add a new state to the transitions table.
|
|
|
*
|
|
|
* @param string $name
|
|
|
+ *
|
|
|
* @return integer 0 on success, < 0 on failure
|
|
|
*/
|
|
|
- public function addState($name)
|
|
|
- {
|
|
|
+ public function addState($name) {
|
|
|
|
|
|
|
|
|
- if (array_key_exists($name, $this->f_transitions))
|
|
|
- {
|
|
|
+ if (array_key_exists($name, $this->fTransitions)) {
|
|
|
$ret = -1;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- $this->f_transitions[$name] = array();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $this->fTransitions[$name] = array();
|
|
|
$ret = 0;
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
return $ret;
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
|
|
|
* Add an event definition (name + optional type of handler) to an existing state
|
|
@@ -182,27 +173,28 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
* @param string $state
|
|
|
* @param string $event
|
|
|
* @param string $type
|
|
|
- * @return integer 0 on success, < 0 on failure
|
|
|
+ *
|
|
|
+ * @return integer
|
|
|
+ * - 0 on success
|
|
|
+ * - < 0 on failure
|
|
|
*/
|
|
|
- public function addEvent($state, $event, $type = 'string')
|
|
|
- {
|
|
|
- if (!array_key_exists($state, $this->f_transitions))
|
|
|
- {
|
|
|
+ public function addEvent($state, $event, $type = 'string') {
|
|
|
+ if (!array_key_exists($state, $this->fTransitions)) {
|
|
|
$ret = -1;
|
|
|
- }
|
|
|
- elseif (array_key_exists($event, $this->f_transitions[$state]))
|
|
|
- {
|
|
|
+ }
|
|
|
+ elseif (array_key_exists($event, $this->fTransitions[$state])) {
|
|
|
$ret = -2;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- $this->f_transitions[$state][$event] = array();
|
|
|
- $this->f_transitions[$state][$event]["#type"] = $type;
|
|
|
+ }
|
|
|
+
|
|
|
+ else {
|
|
|
+ $this->fTransitions[$state][$event] = array();
|
|
|
+ $this->fTransitions[$state][$event]["#type"] = $type;
|
|
|
$ret = 0;
|
|
|
- }
|
|
|
- return $ret;
|
|
|
}
|
|
|
|
|
|
+ return $ret;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
* Add an outcome definition to an existing event:
|
|
|
* - event handler result
|
|
@@ -212,37 +204,35 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
*
|
|
|
* @param name $state
|
|
|
* @param name $event
|
|
|
- * @return integer 0 on success, < 0 on failure
|
|
|
+ *
|
|
|
+ * @return integer
|
|
|
+ * - 0 on success
|
|
|
+ * - < 0 on failure
|
|
|
*/
|
|
|
public function addOutcome($state, $event,
|
|
|
$result = NULL,
|
|
|
$next_state = NULL,
|
|
|
$action = NULL)
|
|
|
{
|
|
|
- $t = &$this->f_transitions;
|
|
|
- if (!array_key_exists($state, $t))
|
|
|
- {
|
|
|
+ $t = &$this->fTransitions;
|
|
|
+ if (!array_key_exists($state, $t)) {
|
|
|
$ret = -1;
|
|
|
- }
|
|
|
- elseif (!array_key_exists($event, $t[$state]))
|
|
|
- {
|
|
|
+ }
|
|
|
+ elseif (!array_key_exists($event, $t[$state])) {
|
|
|
$ret = -2;
|
|
|
- }
|
|
|
- elseif (($result == NULL) && ($t[$state][$event]['#type'] <> 'void'))
|
|
|
- {
|
|
|
+ }
|
|
|
+ elseif (($result == NULL) && ($t[$state][$event]['#type'] <> 'void')) {
|
|
|
$ret = -3;
|
|
|
- }
|
|
|
- elseif (($result != NULL) && array_key_exists($result, $t[$state][$event]))
|
|
|
- {
|
|
|
+ }
|
|
|
+ elseif (($result != NULL) && array_key_exists($result, $t[$state][$event])) {
|
|
|
$ret = -4;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
+ }
|
|
|
+ else {
|
|
|
$t[$state][$event][$result] = array($next_state, $action);
|
|
|
$ret = 0;
|
|
|
- }
|
|
|
- return $ret;
|
|
|
}
|
|
|
+ return $ret;
|
|
|
+ }
|
|
|
|
|
|
|
|
|
* Defines the special states in the FSM
|
|
@@ -250,40 +240,38 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
* @param string $kind Finite_State_Machine::INIT_STATE or ..FINAL_STATE
|
|
|
* @param string $name
|
|
|
*/
|
|
|
- public function setSpecialState($kind, $name)
|
|
|
- {
|
|
|
+ public function setSpecialState($kind, $name) {
|
|
|
|
|
|
- if (($kind != Finite_State_Machine::INIT_STATE) && ($kind != Finite_State_Machine::FINAL_STATE))
|
|
|
- {
|
|
|
+ if (($kind != Machine::INIT_STATE) && ($kind != Machine::FINAL_STATE)) {
|
|
|
$ret = -1;
|
|
|
- }
|
|
|
- elseif (!array_key_exists($name, $this->f_transitions))
|
|
|
- {
|
|
|
+ }
|
|
|
+ elseif (!array_key_exists($name, $this->fTransitions)) {
|
|
|
$ret = -2;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- $this->f_transitions["#$kind"] = $name;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $this->fTransitions["#$kind"] = $name;
|
|
|
$ret = 0;
|
|
|
- }
|
|
|
- return $ret;
|
|
|
}
|
|
|
+ return $ret;
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- * Generate the XML FSM file and optionally PHP stubs file from a valid
|
|
|
- * instanc of Finite_State_Machine
|
|
|
+ * Generate the XML FSM file and optionally PHP stubs file.
|
|
|
+ *
|
|
|
+ * Source is a valid instance of FSM.
|
|
|
*
|
|
|
* @param string $prefix
|
|
|
* @param boolean $php
|
|
|
* @param boolean $overwrite
|
|
|
+ *
|
|
|
* @todo take $overwrite and $php into account
|
|
|
- * @throws Exception from within _check_transitions
|
|
|
+ *
|
|
|
+ * @throws Exception from within checkTransitions
|
|
|
*/
|
|
|
- public function save_fsm($prefix = 'fsm', $php = FALSE, $overwrite = FALSE)
|
|
|
- {
|
|
|
- $this->_check_transitions();
|
|
|
+ public function saveFsm($prefix = 'fsm', $php = FALSE, $overwrite = FALSE) {
|
|
|
+ $this->checkTransitions();
|
|
|
|
|
|
- $t = &$this->f_transitions;
|
|
|
+ $t = &$this->fTransitions;
|
|
|
echo "This is a dump for FSM $prefix"
|
|
|
. ', ' . ($php ? 'with' : 'without') . ' PHP generation'
|
|
|
. ', ' . ($overwrite ? 'with' : 'without') . ' overwrite mode'
|
|
@@ -292,8 +280,8 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
$doc = new DOMDocument('1.0', 'utf-8');
|
|
|
$comment = new DOMComment(' Generated by '
|
|
|
. basename(__FILE__, '.php')
|
|
|
- . " version " . Fsm_Writer::VERSION
|
|
|
- . " on FSM version " . Finite_State_Machine::VERSION
|
|
|
+ . " version " . Writer::FORMAT_VERSION
|
|
|
+ . " on FSM version " . Machine::VERSION
|
|
|
. " ");
|
|
|
$doc->appendChild($comment);
|
|
|
$fsm = new DOMElement('fsm');
|
|
@@ -302,54 +290,47 @@ class Fsm_Writer extends Finite_State_Machine
|
|
|
$fsm->setAttribute('idle_work', "1");
|
|
|
$fsm->setAttribute('allow_actions', "1");
|
|
|
|
|
|
- $fsm->setAttribute('init', $t['#' . Finite_State_Machine::INIT_STATE]);
|
|
|
- $fsm->setAttribute('final', $t['#' . Finite_State_Machine::FINAL_STATE]);
|
|
|
+ $fsm->setAttribute('init', $t['#' . Machine::INIT_STATE]);
|
|
|
+ $fsm->setAttribute('final', $t['#' . Machine::FINAL_STATE]);
|
|
|
|
|
|
- foreach ($this->f_transitions as $state_name => $state)
|
|
|
- {
|
|
|
- if ($state_name[0] == '#')
|
|
|
- {
|
|
|
+ foreach ($this->fTransitions as $stateName => $state) {
|
|
|
+ if ($stateName[0] == '#') {
|
|
|
continue;
|
|
|
- }
|
|
|
- $state_node = new DOMElement('state', "\n");
|
|
|
- $fsm->appendChild($state_node);
|
|
|
- $state_node->setAttribute('id', $state_name);
|
|
|
- foreach ($state as $event_name => $event)
|
|
|
- {
|
|
|
- $event_node = new DOMElement('event', "\n");
|
|
|
- $state_node->appendChild($event_node);
|
|
|
- $event_node->setAttribute('name', $event_name);
|
|
|
- $event_node->setAttribute('type', $event['#type']);
|
|
|
- foreach ($event as $outcome_name => $outcome)
|
|
|
- {
|
|
|
- if ($outcome_name[0] == '#')
|
|
|
- {
|
|
|
+ }
|
|
|
+ $stateNode = new DOMElement('state', "\n");
|
|
|
+ $fsm->appendChild($stateNode);
|
|
|
+ $stateNode->setAttribute('id', $stateName);
|
|
|
+ foreach ($state as $eventName => $event) {
|
|
|
+ $eventNode = new DOMElement('event', "\n");
|
|
|
+ $stateNode->appendChild($eventNode);
|
|
|
+ $eventNode->setAttribute('name', $eventName);
|
|
|
+ $eventNode->setAttribute('type', $event['#type']);
|
|
|
+ foreach ($event as $outcomeName => $outcome) {
|
|
|
+ if ($outcomeName[0] == '#') {
|
|
|
continue;
|
|
|
- }
|
|
|
- $outcome_node = new DOMElement('next', "\n");
|
|
|
- $event_node->appendChild($outcome_node);
|
|
|
+ }
|
|
|
+ $outcomeNode = new DOMElement('next', "\n");
|
|
|
+ $eventNode->appendChild($outcomeNode);
|
|
|
|
|
|
* Generated FSMs currently always use the "string" event handler type,
|
|
|
* meaning they always have outcome results. This will not always be
|
|
|
* the case, hence this test (think ftp.xml)
|
|
|
*/
|
|
|
- if (!empty($outcome_name))
|
|
|
- {
|
|
|
- $outcome_node->setAttribute('result', $outcome_name);
|
|
|
- }
|
|
|
+ if (!empty($outcomeName)) {
|
|
|
+ $outcomeNode->setAttribute('result', $outcomeName);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($outcome[0])) {
|
|
|
+ $outcomeNode->setAttribute('state', $outcome[0]);
|
|
|
+ }
|
|
|
|
|
|
- if (!empty($outcome[0]))
|
|
|
- {
|
|
|
- $outcome_node->setAttribute('state', $outcome[0]);
|
|
|
- }
|
|
|
- if (!empty($outcome[1]))
|
|
|
- {
|
|
|
- $outcome_node->setAttribute('action', $outcome[1]);
|
|
|
- }
|
|
|
+ if (!empty($outcome[1])) {
|
|
|
+ $outcomeNode->setAttribute('action', $outcome[1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
$doc->save("$prefix.xml");
|
|
|
- }
|
|
|
- }
|
|
|
+ }
|
|
|
+}
|