/** * Parses the given source and returns an array which represent a flow * structure. * * This method is to be overriden by the appropriate driver for the given * file. * * @return array * @throws PIECE_FLOW_ERROR_INVALID_FORMAT */ function _parseSource() { ob_start(); $dom = domxml_open_mem(file_get_contents($this->_source)); $contents = ob_get_contents(); ob_end_clean(); if (!$dom) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_FORMAT, "The file [{$this->_source}] containts invalid format. See below for more details.\n {$contents}"); return; } $element = $dom->document_element(); if ($element->has_attribute('firstState')) { $flow['firstState'] = $element->get_attribute('firstState'); } $lastState = $element->get_elements_by_tagname('lastState'); if (count($lastState)) { $flow['lastState'] = array(); if ($lastState[0]->has_attribute('name')) { $flow['lastState']['name'] = $lastState[0]->get_attribute('name'); } if ($lastState[0]->has_attribute('view')) { $flow['lastState']['view'] = $lastState[0]->get_attribute('view'); } $flow['lastState'] = array_merge($flow['lastState'], $this->_parseState($lastState[0])); } $viewStates = $element->get_elements_by_tagname('viewState'); $flow['viewState'] = $this->_parseViewStates($viewStates); $actionState = $element->get_elements_by_tagname('actionState'); $flow['actionState'] = $this->_parseActionStates($actionState); $initial = $element->get_elements_by_tagname('initial'); if (count($initial)) { $flow['initial'] = $this->_parseAction($initial[0]); } $final = $element->get_elements_by_tagname('final'); if (count($final)) { $flow['final'] = $this->_parseAction($final[0]); } return $flow; }
/** * Starts a flow execution. * * @param mixed &$payload * @return string * @throws PIECE_FLOW_ERROR_NOT_FOUND */ function _start(&$payload) { if (!array_key_exists($this->_activeFlowID, $this->_flowDefinitions)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_FOUND, "The flow ID [ {$this->_activeFlowID} ] not found in the flow definitions."); return; } $flow =& new Piece_Flow(); $flow->configure($this->_flowDefinitions[$this->_activeFlowID]['source'], null, $this->_cacheDirectory, $this->_actionDirectory, $this->_configDirectory, $this->_configExtension); if (Piece_Flow_Error::hasErrors()) { return; } while (true) { $flowExecutionTicket = $this->_generateFlowExecutionTicket(); if (!$this->_flowExecution->hasFlowExecution($flowExecutionTicket)) { $this->_flowExecution->addFlowExecution($flowExecutionTicket, $flow, $this->_activeFlowID); if ($this->_isExclusive()) { $this->_flowExecution->markFlowExecutionAsExclusive($flowExecutionTicket, $this->_activeFlowID); } break; } } $this->_flowExecution->activateFlowExecution($flowExecutionTicket, $this->_activeFlowID); $this->_activeFlowExecutionTicket = $flowExecutionTicket; $flow->setPayload($payload); $this->_prepareContext(); $flow->start(); if (Piece_Flow_Error::hasErrors()) { return; } return $flowExecutionTicket; }
/** * Configures a state. * * @param array $state * @throws PIECE_FLOW_ERROR_PROTECTED_EVENT */ function _configureState($state) { for ($i = 0, $count = count(@$state['transitions']); $i < $count; ++$i) { if ($state['transitions'][$i]['event'] == PIECE_FLOW_PROTECTED_EVENT || $this->_fsm->isProtectedEvent($state['transitions'][$i]['event'])) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_PROTECTED_EVENT, "The event [ {$state['transitions'][$i]['event']} ] cannot be used in flow definitions."); return; } $this->_fsm->addTransition($state['name'], $state['transitions'][$i]['event'], $state['transitions'][$i]['nextState'], $this->_wrapEventTriggerAction(@$state['transitions'][$i]['action']), $this->_wrapAction(@$state['transitions'][$i]['guard'])); } if (array_key_exists('entry', $state)) { $this->_fsm->setEntryAction($state['name'], $this->_wrapAction(@$state['entry'])); } if (array_key_exists('exit', $state)) { $this->_fsm->setExitAction($state['name'], $this->_wrapAction(@$state['exit'])); } if (array_key_exists('activity', $state)) { $this->_fsm->setActivity($state['name'], $this->_wrapEventTriggerAction(@$state['activity'])); } }
/** * Parses the given source and returns an array which represent a flow * structure. * * This method is to be overriden by the appropriate driver for the given * file. * * @return array * @throws PIECE_FLOW_ERROR_INVALID_FORMAT */ function _parseSource() { $dom = DOMDocument::loadXML(file_get_contents($this->_source)); ob_start(); $validationResult = $dom->relaxNGValidateSource($this->_schema); $contents = ob_get_contents(); ob_end_clean(); if (!$validationResult) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_FORMAT, "The file [{$this->_source}] containts invalid format. See below for more details.\n {$contents}"); return; } $element = $dom->getElementsByTagName('flow')->item(0); if ($element->hasAttribute('firstState')) { $flow['firstState'] = $element->getAttribute('firstState'); } $lastState = $element->getElementsByTagName('lastState')->item(0); if (!is_null($lastState)) { $flow['lastState'] = array(); if ($lastState->hasAttribute('name')) { $flow['lastState']['name'] = $lastState->getAttribute('name'); } if ($lastState->hasAttribute('view')) { $flow['lastState']['view'] = $lastState->getAttribute('view'); } $flow['lastState'] = array_merge($flow['lastState'], $this->_parseState($lastState)); } $flow['viewState'] = $this->_parseViewStates($element->getElementsByTagName('viewState')); $flow['actionState'] = $this->_parseActionStates($element->getElementsByTagName('actionState')); $initial = $element->getElementsByTagName('initial')->item(0); if (!is_null($initial)) { $flow['initial'] = $this->_parseAction($initial); } $final = $element->getElementsByTagName('final')->item(0); if (!is_null($final)) { $flow['final'] = $this->_parseAction($final); } return $flow; }
/** * Gets the current state name for the active flow object. * * @return string * @throws PIECE_FLOW_ERROR_INVALID_OPERATION */ function getCurrentStateName() { if (!$this->_flowExecution->activated()) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_OPERATION, __FUNCTION__ . ' method must be called after starting/continuing flows.'); return; } $flow =& $this->_flowExecution->getActiveFlow(); return $flow->getCurrentStateName(); }
/** * Starts a flow execution. * * @param mixed &$payload * @return string * @throws PIECE_FLOW_ERROR_NOT_FOUND */ function _start(&$payload) { if (!array_key_exists($this->_currentFlowName, $this->_flowDefinitions)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_FOUND, "The flow name [ {$this->_currentFlowName} ] not found in the flow definitions."); return; } $flow =& new Piece_Flow(); $flow->configure($this->_flowDefinitions[$this->_currentFlowName]['file'], null, $this->_cacheDirectory); if (Piece_Flow_Error::hasErrors()) { return; } while (true) { $flowExecutionTicket = $this->_generateFlowExecutionTicket(); if (!$this->_hasFlowExecutionTicket($flowExecutionTicket)) { $this->_flowExecutions[$flowExecutionTicket] =& $flow; break; } } $this->_currentFlowExecutionTicket = $flowExecutionTicket; $this->_activated = true; $flow->setPayload($payload); $flow->start(); if (Piece_Flow_Error::hasErrors()) { return; } if ($this->_isExclusive()) { $this->_exclusiveFlowExecutionTicketsByFlowName[$this->_currentFlowName] = $flowExecutionTicket; $this->_exclusiveFlowNamesByFlowExecutionTicket[$flowExecutionTicket] = $this->_currentFlowName; } return $flowExecutionTicket; }
/** * Reads configuration from the given source and creates * a Piece_Flow_Config object. * * @param mixed $source * @param string $driverName * @param string $cacheDirectory * @param string $configDirectory * @param string $configExtension * @return Piece_Flow_Config * @throws PIECE_FLOW_ERROR_NOT_FOUND * @static */ function &read($source, $driverName, $cacheDirectory, $configDirectory, $configExtension) { if (!is_callable($source)) { $flowName = $source; if (!is_null($configDirectory)) { $source = str_replace('_', '/', $source); $source = "{$configDirectory}/{$source}{$configExtension}"; } if (is_null($driverName)) { $driverName = strtoupper(substr(strrchr($source, '.'), 1)); if ($driverName != 'YAML' && $driverName != 'XML') { $driverName = 'YAML'; } } if ($driverName == 'XML') { if (version_compare(phpversion(), '5.0.0', '>=')) { $driverName = 'XML5'; } else { $driverName = 'XML4'; } } } else { $driverName = 'PHPArray'; } $class = "Piece_Flow_ConfigReader_{$driverName}"; if (!Piece_Flow_ClassLoader::loaded($class)) { Piece_Flow_ClassLoader::load($class); if (Piece_Flow_Error::hasErrors()) { $return = null; return $return; } if (!Piece_Flow_ClassLoader::loaded($class)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_FOUND, "The class [ {$class} ] not found in the loaded file."); $return = null; return $return; } } $driver =& new $class($source, $cacheDirectory); $config =& $driver->read(); if (Piece_Flow_Error::hasErrors()) { $return = null; return $return; } if (!is_callable($source)) { if (is_null($configDirectory)) { $flowName = basename($source); $positionOfExtension = strrpos($flowName, '.'); if ($positionOfExtension !== false) { $flowName = substr($flowName, 0, $positionOfExtension); } } $config->setName($flowName); } return $config; }
/** * Loads an action class corresponding to the given class name. * * @param string $class * @throws PIECE_FLOW_ERROR_NOT_GIVEN * @throws PIECE_FLOW_ERROR_NOT_FOUND */ function load($class) { if (!Piece_Flow_ClassLoader::loaded($class)) { if (is_null($GLOBALS['PIECE_FLOW_Action_Directory'])) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_GIVEN, 'The action directory is not given.'); return; } Piece_Flow_ClassLoader::load($class, $GLOBALS['PIECE_FLOW_Action_Directory']); if (Piece_Flow_Error::hasErrors()) { return; } if (!Piece_Flow_ClassLoader::loaded($class)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_FOUND, "The class [ {$class} ] not found in the loaded file."); } } }
/** * Removes the payload from the FSM. * * @since Method available since Release 1.11.0 */ function clearPayload() { if (!is_a($this->_fsm, 'Stagehand_FSM')) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_OPERATION, __FUNCTION__ . ' method must be called after configuring flows.'); return; } $this->_fsm->clearPayload(); }
/** * Loads a class. * * @param string $class * @param string $directory * @throws PIECE_FLOW_ERROR_NOT_READABLE * @throws PIECE_FLOW_ERROR_NOT_FOUND * @throws PIECE_FLOW_ERROR_CANNOT_READ * @static */ function load($class, $directory = null) { $file = str_replace('_', '/', $class) . '.php'; if (!is_null($directory)) { $file = "{$directory}/{$file}"; if (!file_exists($file)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_FOUND, "The class file [ {$file} ] for the class [ {$class} ] is not found."); return; } if (!is_readable($file)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_READABLE, "The class file [ {$file} ] is not readable."); return; } } if (!(include_once $file)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_CANNOT_READ, "The class file [ {$file} ] is not found or is not readable."); } }
/** * Invokes an event handler in an action. * * @param string $eventName * @param mixed &$payload * @return string * @throws PIECE_FLOW_ERROR_NOT_FOUND */ function _invokeEventHandler($eventName, &$payload) { if (!is_null($this->_actionDirectory)) { Piece_Flow_Action_Factory::setActionDirectory($this->_actionDirectory); } $action =& Piece_Flow_Action_Factory::factory($this->_class); if (Piece_Flow_Error::hasErrors()) { return; } if (!method_exists($action, $this->_method)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_NOT_FOUND, "The method [ {$this->_method} ] does not exist in the action class [ {$this->_class} ]."); return; } if (method_exists($action, 'setFlow')) { $action->setFlow($this->_flow); } if (method_exists($action, 'setPayload')) { $action->setPayload($payload); } if (method_exists($action, 'setEvent')) { $action->setEvent($eventName); } if (method_exists($action, 'prepare')) { $action->prepare(); } $result = call_user_func(array(&$action, $this->_method)); if (method_exists($action, 'clear')) { $action->clear(); } return $result; }
/** * Configures the final action. * * @param array $finalAction * @throws PIECE_FLOW_ERROR_INVALID_FORMAT * @since Method available since Release 1.10.0 */ function _configureFinalAction($finalAction) { if (is_null($finalAction)) { return; } if (!is_array($finalAction)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_FORMAT, "The \"final\" element is invalid in the flow definition file [ {$this->_source} ]."); return; } if (!array_key_exists('method', $finalAction)) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_FORMAT, "The \"method\" element in the \"final\" element is required in the flow definition file [ {$this->_source} ]."); return; } if (is_null($finalAction['method']) || !strlen($finalAction['method'])) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_FORMAT, "The \"method\" element in the \"final\" element is invalid in the flow definition file [ {$this->_source} ]."); return; } if (array_key_exists('class', $finalAction)) { if (!strlen($finalAction['class'])) { Piece_Flow_Error::push(PIECE_FLOW_ERROR_INVALID_FORMAT, "The \"class\" element in the \"final\" element is invalid in the flow definition file [ {$this->_source} ]."); return; } } $this->_config->setFinalAction($finalAction); }