|
@@ -1,13 +1,14 @@
|
|
|
<?php
|
|
|
/**
|
|
|
* Finite state machine skeleton
|
|
|
- * embodying a Moore FSM
|
|
|
+ *
|
|
|
* (c) 2006 Ouest Systèmes Informatiques (OSI)
|
|
|
* Licensed under the CeCILL 2.0 license
|
|
|
*
|
|
|
- * $Id: u_fsm.php,v 1.2 2007-04-28 20:02:50 marand Exp $
|
|
|
+ * $Id: u_fsm.php,v 1.3 2007-04-29 15:40:04 marand Exp $
|
|
|
*/
|
|
|
require_once('misc.php'); // for func_name()
|
|
|
+error_reporting(E_ALL|E_STRICT);
|
|
|
|
|
|
/**
|
|
|
* This class defines a possible outcome for a given FSM transition
|
|
@@ -42,8 +43,37 @@ class fsm_result
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * This class must be inherited by code implementing actual FSMs.
|
|
|
+ *
|
|
|
+ * Applications must create a fsm descendant in which they will:
|
|
|
+ *
|
|
|
+ * - define the f_transitions table, usually in their constructor
|
|
|
+ * - create "f_foo" functions for each "foo" event, except "idle", which is builtin.
|
|
|
+ *
|
|
|
+ * Applications may:
|
|
|
+ * - disable event processing by setting $this->event to one of the fsm::EVENT_* constants
|
|
|
+ * - disable post-event actions by setting $this->allows actions to false
|
|
|
+ * - disable the builtin idle event by setting $this->idle_work to false
|
|
|
+ * - query the current state by using $this->get_state()
|
|
|
+ * - send an idle event by using $this->idle()
|
|
|
+ * - submit any event (including idle) by using $this->apply_event($event_name)
|
|
|
+ *
|
|
|
+ */
|
|
|
abstract class fsm
|
|
|
{
|
|
|
+ const IDLE_EVENT = 'idle'; // must not change name: method f_idle depends on it
|
|
|
+
|
|
|
+ const EVENT_NORMAL = 1; // processes events
|
|
|
+ const EVENT_QUEUE = 2; // queue events for later use
|
|
|
+ const EVENT_SINK = 3; // throw away events
|
|
|
+
|
|
|
+ public $idle_work = TRUE;
|
|
|
+ public $allow_actions = TRUE;
|
|
|
+
|
|
|
+ protected $f_event_mode = fsm::EVENT_NORMAL;
|
|
|
+ protected $f_queue = array(); // event queue for EVENT_QUEUE mode
|
|
|
+
|
|
|
/**
|
|
|
* the current state of the object
|
|
|
* @var string
|
|
@@ -178,7 +208,14 @@ abstract class fsm
|
|
|
|
|
|
do {
|
|
|
$result = $this->apply_simple_event($event_name);
|
|
|
- $event_name = $result->fsm_action; // can be NULL
|
|
|
+ if ($this->allow_actions)
|
|
|
+ {
|
|
|
+ $event_name = $result->fsm_action; // can be NULL
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ $event_name = NULL;
|
|
|
+ }
|
|
|
} while($event_name);
|
|
|
|
|
|
return $result;
|
|
@@ -194,6 +231,10 @@ abstract class fsm
|
|
|
private function apply_simple_event($event_name)
|
|
|
{
|
|
|
//echo func_name() . "\n";
|
|
|
+ if (($event_name == fsm::IDLE_EVENT) && !$this->idle_work)
|
|
|
+ {
|
|
|
+ return new fsm_result();
|
|
|
+ }
|
|
|
|
|
|
if (! $this->is_event_allowed($event_name))
|
|
|
throw new Exception(func_name()
|
|
@@ -217,4 +258,65 @@ abstract class fsm
|
|
|
// echo func_name() . ", new state: $this->f_state, action: $result->fsm_action\n";
|
|
|
return $result;
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Default event
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public function f_idle()
|
|
|
+ {
|
|
|
+ return TRUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Apply an fsm::IDLE_EVENT event. Do not confuse with f_idle !
|
|
|
+ *
|
|
|
+ * @return fsm_result
|
|
|
+ */
|
|
|
+ public function idle()
|
|
|
+ {
|
|
|
+ return $this->apply_event(fsm::IDLE_EVENT);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * return the current operating mode of the FSM
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+ public function get_event_mode()
|
|
|
+ {
|
|
|
+ return $this->f_event_mode;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * set the current operating mode for the FSM
|
|
|
+ *
|
|
|
+ * @param int $mode fsm::EVENT_* constants
|
|
|
+ */
|
|
|
+ public function set_event_mode($mode)
|
|
|
+ {
|
|
|
+ switch ($mode)
|
|
|
+ {
|
|
|
+ case fsm::EVENT_NORMAL :
|
|
|
+ if (count($this->f_queue) > 0)
|
|
|
+ {
|
|
|
+ while ($event = array_shift($this->f_queue))
|
|
|
+ {
|
|
|
+ $this->apply_event($event);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case fsm::EVENT_QUEUE : // nothing special to do
|
|
|
+ break;
|
|
|
+ case fsm::EVENT_SINK : // empty queue if needed
|
|
|
+ if (count($this->f_queue) > 0)
|
|
|
+ {
|
|
|
+ $this->f_queue = array();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new Exception("Trying to set unknown FSM mode $mode");
|
|
|
+ }
|
|
|
+ $this->f_event_mode = $mode;
|
|
|
+ return $this->f_event_mode;
|
|
|
+ }
|
|
|
+ }
|