1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
28:
29: namespace Mothership\StateMachine;
30:
31: use Mothership\Exception\StateMachine\StatusException;
32: use Mothership\Exception\StateMachine\TransictionException;
33: use Mothership\Exception\StateMachine\WorkflowException;
34: use Mothership\StateMachine\WorkflowInterface;
35: use Symfony\Component\Console\Output\ConsoleOutput;
36: use \Symfony\Component\Console\Output\OutputInterface;
37: use Mothership\StateMachine\StatusInterface;
38:
39: abstract class WorkflowAbstract implements WorkflowInterface
40: {
41:
42: protected $outpout;
43: 44: 45: 46:
47: protected $vars = [];
48: 49: 50:
51: protected $states = [];
52: 53: 54:
55: protected $current_status;
56:
57: public function __construct(OutputInterface $output, array $args = [])
58: {
59: $this->output = new ConsoleOutput();
60: foreach ($args as $key => $value) {
61: $this->vars[$key] = $value;
62: }
63:
64: $this->_init();
65: }
66:
67: 68: 69: 70:
71: public function getOutput()
72: {
73: return $this->outpout;
74: }
75:
76: 77: 78:
79: protected function _init()
80: {
81: if (!array_key_exists("states", $this->vars)) {
82: throw new WorkflowException("You must define some states:\n", 99, null, $this->output);
83: }
84:
85:
86: $methods_not_implemented = "";
87: foreach ($this->vars['states'] as $status) {
88: array_push($this->states, new Status($this, $status));
89: if (!method_exists($this, $status['name'])) {
90: $methods_not_implemented .= $status['name'] . "\n";
91: }
92: }
93: if (strlen($methods_not_implemented) > 0) {
94: throw new WorkflowException("This methods are not implemented in the workflow:\n" .
95: $methods_not_implemented, 79, null, $this->output);
96: }
97:
98: $this->setInitialState();
99: }
100:
101: 102: 103: 104:
105: function setInitialState()
106: {
107: foreach ($this->states as $status) {
108: if ($status->getType() == 'initial') {
109: $this->current_status = $status;
110: return $status;
111: }
112: }
113: throw new WorkflowException("No initial state found for the workflow", 90, null, $this->outpout);
114: }
115:
116: 117: 118: 119: 120:
121: protected function executeTransition($transiction_name)
122: {
123: try {
124: $status = $this->getStatus($transiction_name);
125: return $status->execute($transiction_name, $this->current_status);
126: } catch (StatusException $ex) {
127: if ($ex->getGravity() > 50) {
128: throw new WorkflowException("Error executing the transition", 100, $ex, null, $this->outpout);
129: }
130: return false;
131: } catch (TransitionException $ex) {
132: throw new WorkflowException("Error executing the transition", 100, $ex, null, $this->outpout);
133: }
134: }
135:
136: 137: 138: 139:
140: function getCurrentStatus()
141: {
142: return $this->current_status;
143: }
144:
145: 146: 147: 148: 149:
150: function setState(StatusInterface $status)
151: {
152: $this->current_status = $status;
153: }
154:
155: 156: 157: 158: 159: 160:
161: function getStatus($name)
162: {
163: foreach ($this->states as $status) {
164: if ($status->getName() == $name) {
165: return $status;
166: }
167: }
168: throw new WorkflowException("No status found with the name " . $name, 70, null, $this->outpout);
169: }
170:
171: 172: 173:
174: public function run()
175: {
176: $states_count = count($this->states);
177: for ($i = 1; $i < $states_count; $i++) {
178: $transations = $this->states[$i]->getTransitions();
179: foreach ($transations as $t) {
180: try {
181: $status = $this->executeTransition($t->getName());
182: if ($status !== false) {
183: $this->current_status = $status;
184: $changeStatus = $this->checkIfPreviousTransition($status);
185: if ($changeStatus !== false) {
186: $i = $this->getStatusIndex($changeStatus);
187: break;
188: }
189: }
190: } catch (TransitionException $ex) {
191: new WorkflowException("Error during workflow->run()", 100, $ex, $this->output);
192: } catch (WorkflowException $ex) {
193: new WorkflowException("Error during workflow->run()", 100, $ex, $this->output);
194: } catch (StateException $ex) {
195: if ($this->current_status->hasInternalState()) {
196: $i = 1;
197: break;
198: }
199: }
200:
201: }
202: }
203: return true;
204: }
205:
206: 207: 208: 209: 210:
211: private function getStatusIndex($statusname)
212: {
213: $status_count = count($this->states);
214: for ($i = 0; $i < $status_count; $i++) {
215: if ($this->states[$i]->getName() == $statusname) {
216: return $i - 1;
217: }
218: }
219: }
220:
221: 222: 223: 224: 225:
226: private function checkIfPreviousTransition(StatusInterface $status)
227: {
228: $lastIndex = $this->getStatusIndex($status->getName());
229: for ($i = 0; $i < $lastIndex; $i++) {
230: $transictions = $this->states[$i]->getTransitions();
231: foreach ($transictions as $t) {
232: if ($t->getTransitionFrom() == $status->getName()) {
233: return $this->states[$i]->getName();
234: }
235: }
236: }
237: return false;
238: }
239:
240: }
241: