/src/lib/Zikula/Workflow/Parser.php
https://github.com/philipp2/core · PHP · 400 lines · 267 code · 39 blank · 94 comment · 42 complexity · a79dfaf5a8228eac1c29fbcbc7d06db5 MD5 · raw file
- <?php
- /**
- * Copyright Zikula Foundation 2009 - Zikula Application Framework
- *
- * This work is contributed to the Zikula Foundation under one or more
- * Contributor Agreements and licensed to You under the following license:
- *
- * @license GNU/LGPL3 (or at your option any later version).
- * @package Zikula
- *
- * Please see the NOTICE file distributed with this source code for further
- * information regarding copyright and licensing.
- */
- /**
- * Zikula_Workflow_Parser.
- *
- * Parse workflow schema into associative arrays.
- */
- class Zikula_Workflow_Parser
- {
- // Declare object variables;
- /**
- * XML parser object.
- *
- * @var object
- */
- protected $parser;
- /**
- * Workflow data.
- *
- * @var array
- */
- protected $workflow;
- /**
- * Parse workflow into array format.
- */
- public function __construct()
- {
- $this->workflow = array('state' => 'initial');
- // create xml parser
- $this->parser = xml_parser_create();
- xml_set_object($this->parser, $this);
- xml_set_element_handler($this->parser, 'startElement', 'endElement');
- xml_set_character_data_handler($this->parser, 'characterData');
- }
- /**
- * parse xml
- *
- * @param string $xmldata XML data.
- * @param string $schemaName Schema name.
- * @param string $module Module name.
- *
- * @return mixed Associative array of workflow or false.
- */
- public function parse($xmldata, $schemaName, $module)
- {
- // parse XML
- if (!xml_parse($this->parser, $xmldata, true)) {
- xml_parser_free($this->parser);
- z_exit(__f('Unable to parse XML workflow (line %1$s, %2$s): %3$s',
- array(xml_get_current_line_number($this->parser),
- xml_get_current_column_number($this->parser),
- xml_error_string($this->parser))));
- }
- // close parser
- xml_parser_free($this->parser);
- // check for errors
- if ($this->workflow['state'] == 'error') {
- return LogUtil::registerError($this->workflow['errorMessage']);
- }
- $this->mapWorkflow();
- if (!$this->validate()) {
- return false;
- }
- $this->workflow['workflow']['module'] = $module;
- $this->workflow['workflow']['id'] = $schemaName;
- return $this->workflow;
- }
- /**
- * Map workflow.
- *
- * Marshall data in to meaningful associative arrays.
- *
- * @return void
- */
- public function mapWorkflow()
- {
- // create associative arrays maps
- $stateMap = array();
- $actionMap = array();
- foreach ($this->workflow['states'] as $state) {
- $stateMap[$state['id']] = array('id' => $state['id'],
- 'title' => $state['title'],
- 'description' => $state['description']);
- }
- $states = array_keys($stateMap);
- foreach ($this->workflow['actions'] as $action) {
- if (($action['state'] == 'initial') || ($action['state'] == null) || in_array($action['state'], $states)) {
- $action['state'] = $stateID = !empty($action['state']) ? $action['state'] : 'initial';
- // change the case of array keys for parameter variables
- $operations = &$action['operations'];
- foreach (array_keys($operations) as $key) {
- $parameters = &$operations[$key]['parameters'];
- $parameters = array_change_key_case($parameters, CASE_LOWER);
- }
- // commit results
- $actionMap[$stateID][$action['id']] = $action;
- } else {
- LogUtil::registerError(__f('Unknown %1$s name \'%2$s\' in action \'%3$s\'.', array('state', $action['state'], $action['title'])));
- }
- }
- // commit new array to workflow
- $this->workflow['states'] = $stateMap;
- $this->workflow['actions'] = $actionMap;
- }
- /**
- * Validate workflow actions.
- *
- * @return boolean
- */
- public function validate()
- {
- foreach (array_keys($this->workflow['actions']) as $stateID) {
- foreach ($this->workflow['actions'][$stateID] as $action) {
- if (isset($action['nextState']) && !isset($this->workflow['states'][$action['nextState']])) {
- return LogUtil::registerError(__f('Unknown %1$s name \'%2$s\' in action \'%3$s\' of the \'%4$s\' state.', array('next-state', $action['nextState'], $action['title'], $action['state'])));
- }
- foreach ($action['operations'] as $operation) {
- if (isset($operation['parameters']['nextState'])) {
- $stateName = $operation['parameters']['nextState'];
- if (!isset($this->workflow['states'][$stateName])) {
- return LogUtil::registerError(__f('Unknown state name \'%1$s\' in action \'%2$s\', operation \'%3$s\'.', array($stateName, $action['title'], $operation['name'])));
- }
- }
- }
- }
- }
- return true;
- }
- /**
- * XML start element handler.
- *
- * @param object $parser Parser object.
- * @param string $name Element name.
- * @param array $attribs Element attributes.
- *
- * @return void
- */
- public function startElement($parser, $name, $attribs)
- {
- $name = strtoupper($name);
- $state = &$this->workflow['state'];
- switch ($state) {
- case 'initial':
- if ($name == 'WORKFLOW') {
- $state = 'workflow';
- $this->workflow['workflow'] = array();
- } else {
- $state = 'error';
- $this->workflow['errorMessage'] = $this->unexpectedXMLError($name, "$state ". __LINE__);
- }
- break;
- case 'workflow':
- switch ($name) {
- case 'TITLE':
- case 'DESCRIPTION':
- $this->workflow['value'] = '';
- break;
- case 'STATES':
- $state = 'states';
- $this->workflow['states'] = array();
- break;
- case 'ACTIONS':
- $state = 'actions';
- $this->workflow['actions'] = array();
- break;
- default:
- $this->workflow['errorMessage'] = $this->unexpectedXMLError($name, "$state ". __LINE__);
- $state = 'error';
- break;
- }
- break;
- case 'states':
- if ($name == 'STATE') {
- $this->workflow['stateValue'] = array('id' => trim($attribs['ID']));
- $state = 'state';
- } else {
- $this->workflow['errorMessage'] = $this->unexpectedXMLError($name, "$state ". __LINE__);
- $state = 'error';
- }
- break;
- case 'state':
- if ($name == 'TITLE' || $name == 'DESCRIPTION') {
- $this->workflow['value'] = '';
- } else {
- $this->workflow['errorMessage'] = $this->unexpectedXMLError($name, "$state ". __LINE__);
- $state = 'error';
- }
- break;
- case 'actions':
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
- if ($name == 'ACTION') {
- $this->workflow['action'] = array('id' => trim($attribs['ID']),
- 'operations' => array(),
- 'state' => null);
- $state = 'action';
- } else {
- $this->workflow['errorMessage'] = $this->unexpectedXMLError($name, "$state ". __LINE__);
- $state = 'error';
- }
- break;
- case 'action':
- switch ($name) {
- case 'TITLE':
- case 'DESCRIPTION':
- case 'PERMISSION':
- case 'STATE':
- case 'NEXTSTATE':
- $this->workflow['value'] = '';
- break;
- case 'OPERATION':
- $this->workflow['value'] = '';
- $this->workflow['operationParameters'] = $attribs;
- break;
- case 'PARAMETER':
- $this->workflow['value'] = '';
- $this->workflow['actionParameter'] = $attribs;
- break;
- default:
- $this->workflow['errorMessage'] = $this->unexpectedXMLError($name, "$state ". __LINE__);
- $state = 'error';
- break;
- }
- break;
- case '':
- if ($name == '') {
- $state = '';
- } else {
- $this->workflow['errorMessage'] = $this->unexpectedXMLError($name, "$state ". __LINE__);
- $state = 'error';
- }
- break;
- case 'error':
- // ignore
- break;
- default:
- $this->workflow['errorMessage'] = __f('Workflow state error: \'%2$s\' tag of the state \'%1$s\'.', array($state, $name));
- $state = 'error';
- break;
- }
- }
- /**
- * XML end element handler.
- *
- * @param object $parser Parser object.
- * @param string $name Element name.
- *
- * @return void
- */
- public function endElement($parser, $name)
- {
- $name = strtoupper($name);
- $state = &$this->workflow['state'];
- switch ($state) {
- case 'workflow':
- switch ($name) {
- case 'TITLE':
- $this->workflow['workflow']['title'] = $this->workflow['value'];
- break;
- case 'DESCRIPTION':
- $this->workflow['workflow']['description'] = $this->workflow['value'];
- break;
- }
- break;
- case 'state':
- switch ($name) {
- case 'TITLE':
- $this->workflow['stateValue']['title'] = $this->workflow['value'];
- break;
- case 'DESCRIPTION':
- $this->workflow['stateValue']['description'] = $this->workflow['value'];
- break;
- case 'STATE':
- $this->workflow['states'][] = $this->workflow['stateValue'];
- $this->workflow['stateValue'] = null;
- $state = 'states';
- break;
- }
- break;
- case 'action':
- switch ($name) {
- case 'TITLE':
- $this->workflow['action']['title'] = $this->workflow['value'];
- break;
- case 'DESCRIPTION':
- $this->workflow['action']['description'] = $this->workflow['value'];
- break;
- case 'PERMISSION':
- $this->workflow['action']['permission'] = trim($this->workflow['value']);
- break;
- case 'STATE':
- $this->workflow['action']['state'] = trim($this->workflow['value']);
- break;
- case 'NEXTSTATE':
- $this->workflow['action']['nextState'] = trim($this->workflow['value']);
- break;
- case 'OPERATION':
- $this->workflow['action']['operations'][] = array('name' => trim($this->workflow['value']),
- 'parameters' => $this->workflow['operationParameters']);
- $this->workflow['operation'] = null;
- break;
- case 'PARAMETER':
- $this->workflow['action']['parameters'][trim($this->workflow['value'])] = $this->workflow['actionParameter'];
- break;
- case 'ACTION':
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 1);
- $this->workflow['actions'][] = $this->workflow['action'];
- $this->workflow['action'] = null;
- $state = 'actions';
- break;
- }
- break;
- case 'actions':
- if ($name == 'ACTIONS') {
- $state = 'workflow';
- }
- break;
- case 'states':
- if ($name == 'STATES') {
- $state = 'workflow';
- }
- break;
- }
- }
- /**
- * XML data element handler.
- *
- * @param object $parser Parser object.
- * @param string $data Character data.
- *
- * @return boolean True.
- */
- public function characterData($parser, $data)
- {
- $value = &$this->workflow['value'];
- $value .= $data;
- return true;
- }
- /**
- * Hander for unexpected XML errors.
- *
- * @param string $name Tag name.
- * @param string $state Workflow state.
- *
- * @return string
- */
- public function unexpectedXMLError($name, $state)
- {
- return __f('Unexpected %1$s tag in %2$s state', array($name, $state));
- }
- }