| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 | 
							- <?php
 
- namespace OSInet\Finite_State_Machine;
 
- /**
 
-  * Abstract state machine.
 
-  *
 
-  * @copyright  (c) 2007-2012 Ouest Systèmes Informatiques
 
-  * @author     Frederic G. MARAND
 
-  * @license    CeCILL 2.0
 
-  * @link       http://wiki.audean.com/fsm/fsm
 
-  * @since      FSM 1.6
 
-  *
 
-  * This class must be inherited by code implementing actual FSMs.
 
-  *
 
-  * Applications must create a Machine descendant in which they will:
 
-  *   - define the fTransitions table, usually in their constructor
 
-  *   - invoke the parent constructor to set up the FSM from the transitions table
 
-  *   - 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->allowsActions to false
 
-  *   - disable the builtin idle event by setting $this->idleWork to false
 
-  *   - query the current state by using $this->getState()
 
-  *   - send an idle event by using $this->idle()
 
-  *   - submit any event (including idle) by using $this->applyEvent($eventName)
 
-  */
 
- abstract class Machine {
 
-   const VERSION      = '12D22';
 
-   const IDLE_EVENT   = 'idle'; // must not change name: method fIdle depends on it
 
-   const INIT_STATE   = 'init';
 
-   const FINAL_STATE  = 'final';
 
-   const EVENT_NORMAL = 1; // processes events
 
-   const EVENT_QUEUE  = 2; // queue events for later use
 
-   const EVENT_SINK   = 3; // throw away events
 
-   public $idleWork     = TRUE;
 
-   public $allowActions = TRUE;
 
-   protected $fEventMode = self::EVENT_NORMAL;
 
-   protected $fQueue     = array(); // event queue for EVENT_QUEUE mode
 
-   /**
 
-    * the current state of the object
 
-    * @var string
 
-    */
 
-   protected $fState;
 
-   /**
 
-    * Transitions holds the transitions table
 
-    * state1
 
-    *   event1
 
-    *     result1
 
-    *     state_name|Result
 
-    *   event2
 
-    *     ...
 
-    *   ..
 
-    * ..
 
-    * @var array
 
-    */
 
-   public $fTransitions = null;
 
-   /**
 
-    * constructor initializes the FSM to the first
 
-    * state in the transitions table
 
-    * @return void
 
-    */
 
-   public function __construct() {
 
-     $this->checkTransitions();
 
-     reset($this->fTransitions);
 
-     $x = each($this->fTransitions);
 
-     $x = $x[0];
 
-     $this->fState = $x;
 
-   }
 
-   /**
 
-    * Make sure a transitions graph has been defined
 
-    *
 
-    * @return void
 
-    */
 
-   protected function checkTransitions() {
 
-     if (!isset($this->fTransitions))
 
-       throw new Exception('No FSM processing is allowed without a transitions table\n');
 
-   }
 
-   /**
 
-    * Getter for fState
 
-    *
 
-    * @return string
 
-    */
 
-   public function getState() {
 
-     return $this->fState;
 
-   }
 
-   /**
 
-    * return the list of events accepted in the current state
 
-    * @return array
 
-    */
 
-   public function getAcceptedEvents() {
 
-     $this->checkTransitions();
 
-     try {
 
-       $events = array_keys($this->fTransitions[$this->fState]);
 
-       // echo func_name() . ": state $this->fState, accepted events are:\n" . print_r($events, true). "\n";
 
-     }
 
-     catch (Exception $e) {
 
-       echo "Exception in getAcceptedEvents" . print_r($e);
 
-       print_r(debug_backtrace());
 
-       $events = array();
 
-     }
 
-     return $events;
 
-   }
 
-   /**
 
-    * return the list of outcome accepted in the current state for the give event
 
-    * @param string $eventName
 
-    * @param mixed $outcome
 
-    * @return array
 
-    */
 
-   public function getAcceptedOutcomes($eventName) {
 
-     // echo func_name() . "\n";
 
-     $this->checkTransitions();
 
-     /**
 
-      * Spare some paranioa
 
-      *
 
-      if (!$this->isEventAllowed($eventName))
 
-        throw new Exception(func_name() . ": event \"$eventName\" not allowed in state \"$this->fState\"\n");
 
-      */
 
-     $outcomes = array_keys($this->fTransitions[$this->fState][$eventName]);
 
-     // print_r($this->fTransitions[$this->fState][$eventName]);
 
-     // echo "outcomes for event $eventName: " . var_dump($outcomes) . "\n";
 
-     return $outcomes;
 
-   }
 
-   /**
 
-    * is this event accepted in the current state
 
-    * the FSM is in ?
 
-    *
 
-    * @param string $eventName
 
-    * @return boolean
 
-    */
 
-   public function isEventAllowed($eventName) {
 
-     // echo func_name() . "($eventName)";
 
-     $this->checkTransitions();
 
-     $ret = in_array($eventName, $this->getAcceptedEvents());
 
-     // echo " in state $this->fState, result = <$ret>\n";
 
-     return $ret;
 
-   }
 
-   /**
 
-    * is a given outcome available for a given event,
 
-    * considering the current context ?
 
-    *
 
-    * @param string $eventName
 
-    * @param mixed $outcome
 
-    * @return boolean
 
-    */
 
-   public function isOutcomeAllowed($eventName, $outcome) {
 
-     $this->checkTransitions();
 
-     if (!$this->isEventAllowed($eventName)) {
 
-       return false;
 
-     }
 
-     $ret = array_key_exists($outcome, $this->getAcceptedOutcomes($eventName));
 
-     return $ret;
 
-   }
 
-   /**
 
-    * apply an event, and the resulting event chain if needed
 
-    *
 
-    * @param string $eventName
 
-    * @param array $params the
 
-    * @return Result resulting state
 
-    */
 
-   public function applyEvent($eventName) {
 
-     // echo "Start of " . func_name() . "\n";
 
-     do {
 
-       $result = $this->applySimpleEvent($eventName);
 
-       if ($this->allowActions) {
 
-         $eventName = $result->fsmAction; // can be NULL
 
-       } else {
 
-         $eventName = NULL;
 
-       }
 
-     } while ($eventName);
 
-     // echo "End of " . func_name() . "\n";
 
-     return $result;
 
-   }
 
-   /**
 
-    * Helper for applyEvent that does not implement the post-transition action
 
-    *
 
-    * @see applyEvent()
 
-    *
 
-    * @param string $eventName
 
-    *
 
-    * @return Result
 
-    */
 
-   private function applySimpleEvent($eventName) {
 
-     // echo "Start of " . func_name() . ", event = $eventName\n";
 
-     $currentState = $this->fState;
 
-     if (($eventName == self::IDLE_EVENT) && !$this->idleWork) {
 
-       return new Result();
 
-     }
 
-     if (!$this->isEventAllowed($eventName)) {
 
-       die("Event $eventName not allowed in current state $currentState.\n");
 
-       /* throw new Exception(func_name()
 
-        . ":  Event \"$eventName\" not accepted in current state \"$currentState\"");
 
-       */
 
-     }
 
-     $outcomes = $this->getAcceptedOutcomes($eventName);
 
-     $methodName = "event$eventName";
 
-     if (!method_exists($this, $methodName)) {
 
-       die (func_name() . ": missing method "
 
-         . get_class($this) . "::$methodName. Aborting.\n");
 
-     }
 
-     $outcome = $this->$methodName();
 
-     if (!in_array($outcome, $outcomes)) {
 
-       throw new \Exception(func_name()
 
-         . ": event guard. Transition on \"$eventName\" return invalid outcome: "
 
-         . print_r($outcome, true)
 
-         . " for state $this->fState\n");
 
-     }
 
-     $transition = &$this->fTransitions[$currentState][$eventName][$outcome];
 
-     $result = new Result (
 
-         $outcome,
 
-         $transition[0],
 
-         $transition[1]
 
-     );
 
-     if (isset($result->fsmState)) {
 
-       $this->fState = $result->fsmState;
 
-     }
 
-     /* print_r($this->fTransitions[$currentState][$eventName]);
 
-      var_dump($result); */
 
-     if (!isset($outcome)) {
 
-       $outcome = 'NULL';
 
-     }
 
-     /*
 
-      echo func_name()
 
-     . ": $currentState: " . $eventName . '[' . $outcome . ']'
 
-     . " -> $this->fState / " . $result->fsmAction . PHP_EOL;
 
-     */
 
-     return $result;
 
-   }
 
-   /**
 
-    * Default event.
 
-    *
 
-    * @return boolean
 
-    */
 
-   public function fIdle() {
 
-     return TRUE;
 
-   }
 
-   /**
 
-    * Apply an fsm::IDLE_EVENT event. Do not confuse with fIdle !
 
-    *
 
-    * @return Result
 
-    */
 
-   public function idle() {
 
-     return $this->applyEvent(self::IDLE_EVENT);
 
-   }
 
-   /**
 
-    * return the current operating mode of the FSM
 
-    * @return int
 
-    */
 
-   public function getEventMode() {
 
-     return $this->fEventMode;
 
-   }
 
-   /**
 
-    * set the current operating mode for the FSM
 
-    *
 
-    * @param int $mode fsm::EVENT_* constants
 
-    * @return int $mode fsm::EVENT_* constants
 
-    */
 
-   public function setEventMode($mode) {
 
-     switch ($mode) {
 
-       case self::EVENT_NORMAL :
 
-         if (count($this->fQueue) > 0) {
 
-           while (($event = array_shift($this->fQueue)) !== NULL) {
 
-             $this->applyEvent($event);
 
-           }
 
-         }
 
-         break;
 
-       // Nothing special to do
 
-       case self::EVENT_QUEUE:
 
-         break;
 
-       // Empty queue if needed
 
-       case self::EVENT_SINK :
 
-         if (count($this->fQueue) > 0) {
 
-           $this->fQueue = array();
 
-         }
 
-         break;
 
-       default:
 
-         throw new Exception("Trying to set unknown FSM mode $mode");
 
-     }
 
-     $this->fEventMode = $mode;
 
-     return $this->fEventMode;
 
-   }
 
-   /**
 
-    * Load the fTransitions table from an external resource.
 
-    *
 
-    * @param string $url
 
-    *   Optional: defaults to the name of the class, . ".xml"
 
-    */
 
-   public function loadFsm($url = NULL) {
 
-     $osd = FALSE; // on screen display (debug)
 
-     if ($osd) {
 
-       echo "Loading FSM from $url\n";
 
-     }
 
-     if (!isset($url)) {
 
-       $url = get_class($this) . ".xml";
 
-     }
 
-     $fsm = simplexml_load_file($url);
 
-     $fsmVersion = (string) $fsm['fsm_version'];
 
-     if ($fsmVersion !== '1.3') {
 
-       die("Revision $fsmVersion of schema is not supported.\n");
 
-     }
 
-     $this->idleWork     = ($fsm['idle_work']     == 1);
 
-     $this->allowActions = ($fsm['allow_actions'] == 1);
 
-     $this->fTransitions = array();
 
-     $t = &$this->fTransitions;
 
-     // (string) casts in this loop are required: RHS is a SimpleXMLElement
 
-     foreach ($fsm->state as $state) {
 
-       $id = (string) $state['id'];
 
-       if ($osd) {
 
-         echo "State $id :\n";
 
-       }
 
-       $t[$id] = array();
 
-       switch ($id) {
 
-         case self::INIT_STATE :
 
-           if ($osd) {
 
-             echo "  Initial state\n";
 
-           }
 
-           break;
 
-         case self::FINAL_STATE :
 
-           if ($osd) {
 
-             echo "  Final state\n";
 
-           }
 
-           break;
 
-       }
 
-       foreach ($state->event as $event) {
 
-         $name = (string) $event['name'];
 
-         if ($osd) {
 
-           echo "  Event $name";
 
-         }
 
-         if (!isset($event['type'])) {
 
-           $event['type'] = 'void';
 
-         }
 
-         $eventType = (string) $event['type'];
 
-         if ($osd) {
 
-           echo ", type $eventType\n";
 
-         }
 
-         foreach ($event as $next) {
 
-           if ($event['type'] == 'void') {
 
-             $next['result'] = 'always';
 
-             $result = null;
 
-           }
 
-           else {
 
-             $result = (string) $next['result'];
 
-           }
 
-           if (!isset($next['state'])) {
 
-             $next['state'] = (string) $state['id'];
 
-           }
 
-           if ($osd) {
 
-             echo "    Next(" . $next['result'] . ') = ' . $next['state'];
 
-             if (isset($next['action'])) {
 
-               echo " + event " . $next['action'];
 
-             }
 
-             echo PHP_EOL;
 
-           }
 
-           $t[$id][$name][$result] = array(
 
-             (string) $next['state'],
 
-             (string) $next['action']);
 
-         }
 
-       }
 
-     }
 
-   }
 
- }
 
 
  |