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\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:      * Usefull variables for the object passed throw workflow configuration file
 45:      * @var array
 46:      */
 47:     protected $vars = [];
 48:     /**
 49:      * @var StatusInterface;
 50:      */
 51:     protected $states = [];
 52:     /**
 53:      * @var \Mothership\StateMachine\StatusInterface
 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:      * Get the output for the workflow
 69:      * @return OutputInterface
 70:      */
 71:     public function getOutput()
 72:     {
 73:         return $this->outpout;
 74:     }
 75: 
 76:     /**
 77:      * @throws WorkflowException
 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:         //check if all the methods for each status is callable
 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:      * @return StatusInterface
103:      * @throws WorkflowException
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:      * @param $transiction_name
118:      * @return mixed|void
119:      * @throws WorkflowException
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:      * Return the current status of the workflow
138:      * @return \Mothership\StateMachine\StatusInterface $status
139:      */
140:     function getCurrentStatus()
141:     {
142:         return $this->current_status;
143:     }
144: 
145:     /**
146:      * Set the status of the workflow
147:      * @param \Mothership\StateMachine\StatusInterface $status
148:      * @return mixed
149:      */
150:     function setState(StatusInterface $status)
151:     {
152:         $this->current_status = $status;
153:     }
154: 
155:     /**
156:      * Get the status of the workflow by its name
157:      * @param $name
158:      * @return \Mothership\StateMachine\StatusInterface WorkflowException
159:      * @throws WorkflowException
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:      * execute the workflow
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:      * Get the position of a state
208:      * @param $statusname
209:      * @return int
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:      * Check if there is a previous transition that could be executed from $status
223:      * @param \Mothership\StateMachine\StatusInterface $status
224:      * @return bool|string false or the name of the status to execute
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: 
API documentation generated by ApiGen