Overview

Namespaces

  • Mothership
    • Exception
      • StateMachine
    • StateMachine

Classes

  • Mothership\StateMachine\StateMachineAbstract
  • Mothership\StateMachine\Status
  • Mothership\StateMachine\Transition
  • Mothership\StateMachine\WorkflowAbstract

Interfaces

  • Mothership\StateMachine\StateMachineInterface
  • Mothership\StateMachine\StatusInterface
  • Mothership\StateMachine\TransitionInterface
  • Mothership\StateMachine\WorkflowInterface

Exceptions

  • Mothership\Exception\ExceptionAbstract
  • Mothership\Exception\StateMachine\StateMachineAdapterException
  • Mothership\Exception\StateMachine\StateMachineException
  • Mothership\Exception\StateMachine\StatusException
  • Mothership\Exception\StateMachine\TransitionException
  • Mothership\Exception\StateMachine\WorkflowException
  • Overview
  • Namespace
  • Class
  1: <?php
  2: /**
  3:  * Magento
  4:  *
  5:  * NOTICE OF LICENSE
  6:  *
  7:  * This source file is subject to the Open Software License (OSL 3.0)
  8:  * that is bundled with this package in the file LICENSE.txt.
  9:  * It is also available through the world-wide-web at this URL:
 10:  * http://opensource.org/licenses/osl-3.0.php
 11:  * If you did not receive a copy of the license and are unable to
 12:  * obtain it through the world-wide-web, please send an email
 13:  * to license@magentocommerce.com so we can send you a copy immediately.
 14:  *
 15:  * DISCLAIMER
 16:  *
 17:  * Do not edit or add to this file if you wish to upgrade Magento to newer
 18:  * versions in the future. If you wish to customize Magento for your
 19:  * needs please refer to http://www.magentocommerce.com for more information.
 20:  *
 21:  * @category  Mothership
 22:  * @package   Mothership_StateMachine
 23:  * @author    Maurizio Brioschi <brioschi@mothership.de>
 24:  * @copyright Copyright (c) 2015 Mothership GmbH
 25:  * @license   http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 26:  * @link      http://www.mothership.de/
 27:  */
 28: 
 29: namespace Mothership\StateMachine;
 30: 
 31: use Mothership\Exception\StateMachine\StateMachineException;
 32: use Mothership\Exception\StateMachine\WorkflowException;
 33: use Symfony\Component\Yaml\Yaml;
 34: use \Symfony\Component\Console\Output\OutputInterface;
 35: use Mothership\StateMachine\Transition;
 36: 
 37: abstract class StateMachineAbstract implements StateMachineInterface
 38: {
 39:     protected $workflow_file;
 40:     protected $workflow_array;
 41:     protected $workflow;
 42:     protected $output;
 43: 
 44:     public function __construct($file = null, OutputInterface $output)
 45:     {
 46:         $this->output = $output;
 47:         $this->workflow_file = $file;
 48:         if (!file_exists($this->workflow_file) || is_null($file)) {
 49:             throw new StateMachineException("File " . $this->workflow_file . "  doesn't exist or null, you
 50:             must provide an existing workflow YAML file",
 51:                 100, null, $this->output);
 52:         }
 53:         if ($output === null) {
 54:             throw new StateMachineException("No Output is defined for Mothership State Machine", 99, null, $this->output);
 55:         }
 56:         //read the file
 57:         try {
 58:             $this->workflow_array = $this->parseYAML();
 59:             if ($this->workflow_array === false || $this->workflow_array === null) {
 60:                 throw new StateMachineException("Error parsing " . $this->workflow_file . " file", 98, null, $this->output);
 61:             }
 62:         } catch (Symfony\Component\Yaml\Exception\ParseException $ex) {
 63:             throw new StateMachineException("Error parsing " . $this->workflow_file . " file", 98, $ex, $this->output);
 64:         }
 65: 
 66:         $this->initWorkflow();
 67:     }
 68: 
 69:     /**
 70:      * Parse the yaml file
 71:      * @return array
 72:      * @throws Symfony\Component\Yaml\Exception\ParseException
 73:      * @throws \Exception
 74:      */
 75:     protected function parseYAML()
 76:     {
 77:         try {
 78:             $yaml = Yaml::parse(file_get_contents($this->workflow_file));
 79:             $yaml_fixed = [];
 80:             $yaml_fixed['class'] = $yaml['class'];
 81:             foreach ($yaml['states'] as $key => $value) {
 82:                 if ($value['type'] != 'initial') {
 83:                     $state = ['name' => $key,
 84:                         'type' => $value['type'],
 85:                         'transitions_from' => $value['transitions_from'],
 86:                         'transitions_to' => $value['transitions_to']];
 87:                     $yaml_fixed['states'][] = $state;
 88:                 } else {
 89:                     $state = ['name' => $key,
 90:                         'type' => $value['type']];
 91:                     $yaml_fixed['states'][] = $state;
 92:                 }
 93:             }
 94:             return $yaml_fixed;
 95:         } catch (Symfony\Component\Yaml\Exception\ParseException $ex) {
 96:             throw $ex;
 97:         }
 98:     }
 99: 
100:     /**
101:      * Create the instance of the real workflow
102:      */
103:     protected function initWorkflow()
104:     {
105:         try {
106:             $class_name = $this->workflow_array['class']['name'];
107:             $this->workflow = new $class_name($this->output, $this->workflow_array);
108:         } catch (WorkflowException $ex) {
109:             throw new StateMachineException("Workflow with some problems", 90, $ex, $this->output);
110:         }
111: 
112:     }
113: 
114:     /**
115:      * create a graph for the state machine
116:      * @param bool|false $exit if we want to exit after graphic generation
117:      */
118:     public function renderGraph($outputPath = './workflow.png', $exit = true)
119:     {
120: 
121:         /**
122:          * This example is based on http://martin-thoma.com/how-to-draw-a-finite-state-machine/
123:          * Feel free to tweak the rendering output. I have decided do use the most simple
124:          * implementation over the fancy stuff to avoid additional complexity.
125:          */
126:         $template
127:             = "
128:             digraph finite_state_machine {
129:                 rankdir=LR;
130:                 size=\"%d\"
131: 
132:                 node [shape = doublecircle]; S;
133:                 node [shape = circle];
134: 
135:                 %s
136:             }
137:         ";
138: 
139:         $pattern = " %s  -> %s [ label = \"%s\" ];";
140: 
141:         $_transitions = array();
142:         foreach ($this->workflow_array['states'] as $state) {
143:             if (array_key_exists("transitions_from", $state)) {
144:                 $transitions_from = $state['transitions_from'];
145:                 foreach ($transitions_from as $from) {
146:                     if (is_array($from)) {
147:                         $_transitions[] = sprintf($pattern, $from['status'], $state['name'], "<< IF "
148:                             . $from['result'] . " >>" . $state['name']);
149:                     } else {
150:                         $_transitions[] = sprintf($pattern, $from, $state['name'], $state['name']);
151:                     }
152:                 }
153:             }
154:         }
155:         file_put_contents('/tmp/sm.gv', sprintf($template, count($_transitions) * 2, implode("\n", $_transitions)));
156:         shell_exec('dot -Tpng /tmp/sm.gv -o ' . $outputPath);
157: 
158:         if ($exit) {
159:             exit;
160:         }
161:     }
162: 
163:     /**
164:      * Run the state machine
165:      */
166:     public function run()
167:     {
168:         try {
169:             return $this->workflow->run();
170:         } catch (WorkflowException $ex) {
171:             throw new StateMachineException("Error running State Machine", 100, $ex, $this->output);
172:         }
173:     }
174: 
175: }
176: 
API documentation generated by ApiGen