Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Transition often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Transition, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
32 | class Transition extends AbstractEngine implements TransitionInterface |
||
33 | { |
||
34 | /** |
||
35 | * |
||
36 | * @var array |
||
37 | */ |
||
38 | protected $stateCache = []; |
||
39 | |||
40 | /** |
||
41 | * Переход между двумя статусами |
||
42 | * |
||
43 | * @param WorkflowEntryInterface $entry |
||
44 | * @param SplObjectStorage|StepInterface[] $currentSteps |
||
45 | * @param WorkflowStoreInterface $store |
||
46 | * @param WorkflowDescriptor $wf |
||
47 | * @param ActionDescriptor $action |
||
48 | * @param TransientVarsInterface $transientVars |
||
49 | * @param TransientVarsInterface $inputs |
||
50 | * @param PropertySetInterface $ps |
||
51 | * |
||
52 | * @return boolean |
||
53 | * |
||
54 | * @throws InternalWorkflowException |
||
55 | */ |
||
56 | 18 | public function transitionWorkflow(WorkflowEntryInterface $entry, SplObjectStorage $currentSteps, WorkflowStoreInterface $store, WorkflowDescriptor $wf, ActionDescriptor $action, TransientVarsInterface $transientVars, TransientVarsInterface $inputs, PropertySetInterface $ps) |
|
57 | { |
||
58 | try { |
||
59 | 18 | $step = $this->getCurrentStep($wf, $action->getId(), $currentSteps, $transientVars, $ps); |
|
60 | |||
61 | 18 | $validators = $action->getValidators(); |
|
62 | 18 | if ($validators->count() > 0) { |
|
63 | 3 | $this->verifyInputs($validators, $transientVars, $ps); |
|
64 | 2 | } |
|
65 | |||
66 | 17 | $workflowManager = $this->getWorkflowManager(); |
|
67 | 17 | $engineManager = $workflowManager->getEngineManager(); |
|
68 | 17 | $functionsEngine = $engineManager->getFunctionsEngine(); |
|
69 | |||
70 | 17 | if (null !== $step) { |
|
71 | 6 | $stepPostFunctions = $wf->getStep($step->getStepId())->getPostFunctions(); |
|
72 | 6 | foreach ($stepPostFunctions as $function) { |
|
73 | 1 | $functionsEngine->executeFunction($function, $transientVars, $ps); |
|
74 | 6 | } |
|
75 | 6 | } |
|
76 | |||
77 | 17 | $preFunctions = $action->getPreFunctions(); |
|
78 | 17 | foreach ($preFunctions as $preFunction) { |
|
79 | 7 | $functionsEngine->executeFunction($preFunction, $transientVars, $ps); |
|
80 | 17 | } |
|
81 | |||
82 | 17 | $conditionalResults = $action->getConditionalResults(); |
|
83 | 17 | $extraPreFunctions = null; |
|
84 | 17 | $extraPostFunctions = null; |
|
85 | |||
86 | 17 | $theResult = null; |
|
87 | |||
88 | |||
89 | |||
90 | 17 | $currentStepId = null !== $step ? $step->getStepId() : -1; |
|
91 | |||
92 | |||
93 | 17 | $conditionsEngine = $engineManager->getConditionsEngine(); |
|
94 | 17 | $log = $workflowManager->getLog(); |
|
95 | 17 | $context = $workflowManager->getContext(); |
|
96 | |||
97 | 17 | foreach ($conditionalResults as $conditionalResult) { |
|
98 | 6 | if ($conditionsEngine->passesConditionsWithType(null, $conditionalResult->getConditions(), $transientVars, $ps, $currentStepId)) { |
|
99 | 4 | $theResult = $conditionalResult; |
|
100 | |||
101 | 4 | $validatorsStorage = $conditionalResult->getValidators(); |
|
102 | 4 | if ($validatorsStorage->count() > 0) { |
|
103 | 1 | $this->verifyInputs($validatorsStorage, $transientVars, $ps); |
|
104 | } |
||
105 | |||
106 | 3 | $extraPreFunctions = $conditionalResult->getPreFunctions(); |
|
107 | 3 | $extraPostFunctions = $conditionalResult->getPostFunctions(); |
|
108 | |||
109 | 3 | break; |
|
110 | } |
||
111 | 17 | } |
|
112 | |||
113 | |||
114 | 16 | if (null === $theResult) { |
|
115 | 15 | $theResult = $action->getUnconditionalResult(); |
|
116 | 13 | $this->verifyInputs($theResult->getValidators(), $transientVars, $ps); |
|
117 | 13 | $extraPreFunctions = $theResult->getPreFunctions(); |
|
118 | 13 | $extraPostFunctions = $theResult->getPostFunctions(); |
|
119 | 13 | } |
|
120 | |||
121 | 16 | $logMsg = sprintf('theResult=%s %s', $theResult->getStep(), $theResult->getStatus()); |
|
122 | 16 | $log->debug($logMsg); |
|
123 | |||
124 | |||
125 | 16 | if ($extraPreFunctions && $extraPreFunctions->count() > 0) { |
|
126 | 3 | foreach ($extraPreFunctions as $function) { |
|
127 | 2 | $functionsEngine->executeFunction($function, $transientVars, $ps); |
|
128 | 2 | } |
|
129 | 2 | } |
|
130 | |||
131 | 16 | $split = $theResult->getSplit(); |
|
132 | 16 | $join = $theResult->getJoin(); |
|
133 | 16 | if (null !== $split && 0 !== $split) { |
|
134 | $splitDesc = $wf->getSplit($split); |
||
135 | $results = $splitDesc->getResults(); |
||
136 | $splitPreFunctions = []; |
||
137 | $splitPostFunctions = []; |
||
138 | |||
139 | foreach ($results as $resultDescriptor) { |
||
140 | if ($resultDescriptor->getValidators()->count() > 0) { |
||
141 | $this->verifyInputs($resultDescriptor->getValidators(), $transientVars, $ps); |
||
142 | } |
||
143 | |||
144 | foreach ($resultDescriptor->getPreFunctions() as $function) { |
||
145 | $splitPreFunctions[] = $function; |
||
146 | } |
||
147 | foreach ($resultDescriptor->getPostFunctions() as $function) { |
||
148 | $splitPostFunctions[] = $function; |
||
149 | } |
||
150 | } |
||
151 | |||
152 | foreach ($splitPreFunctions as $function) { |
||
153 | $functionsEngine->executeFunction($function, $transientVars, $ps); |
||
154 | } |
||
155 | |||
156 | if (!$action->isFinish()) { |
||
157 | $moveFirst = true; |
||
158 | |||
159 | foreach ($results as $resultDescriptor) { |
||
160 | $moveToHistoryStep = null; |
||
161 | |||
162 | 1 | if ($moveFirst) { |
|
163 | $moveToHistoryStep = $step; |
||
164 | } |
||
165 | |||
166 | $previousIds = []; |
||
167 | |||
168 | if (null !== $step) { |
||
169 | $previousIds[] = $step->getStepId(); |
||
170 | } |
||
171 | |||
172 | $this->createNewCurrentStep($resultDescriptor, $entry, $store, $action->getId(), $moveToHistoryStep, $previousIds, $transientVars, $ps); |
||
173 | $moveFirst = false; |
||
174 | } |
||
175 | } |
||
176 | |||
177 | |||
178 | foreach ($splitPostFunctions as $function) { |
||
179 | $functionsEngine->executeFunction($function, $transientVars, $ps); |
||
180 | } |
||
181 | 16 | } elseif (null !== $join && 0 !== $join) { |
|
182 | $joinDesc = $wf->getJoin($join); |
||
183 | $oldStatus = $theResult->getOldStatus(); |
||
184 | $caller = $context->getCaller(); |
||
185 | if (null !== $step) { |
||
186 | $step = $store->markFinished($step, $action->getId(), new DateTime(), $oldStatus, $caller); |
||
187 | } else { |
||
188 | $errMsg = 'Invalid step'; |
||
189 | throw new InternalWorkflowException($errMsg); |
||
190 | } |
||
191 | |||
192 | |||
193 | $store->moveToHistory($step); |
||
194 | |||
195 | /** @var StepInterface[] $joinSteps */ |
||
196 | 1 | $joinSteps = []; |
|
197 | $joinSteps[] = $step; |
||
198 | |||
199 | $joinSteps = $this->buildJoinsSteps($currentSteps, $step, $wf, $join, $joinSteps); |
||
200 | |||
201 | $historySteps = $store->findHistorySteps($entry->getId()); |
||
202 | |||
203 | $joinSteps = $this->buildJoinsSteps($historySteps, $step, $wf, $join, $joinSteps); |
||
204 | |||
205 | |||
206 | $jn = new JoinNodes($joinSteps); |
||
207 | $transientVars['jn'] = $jn; |
||
208 | |||
209 | |||
210 | if ($conditionsEngine->passesConditionsWithType(null, $joinDesc->getConditions(), $transientVars, $ps, 0)) { |
||
211 | $joinResult = $joinDesc->getResult(); |
||
212 | |||
213 | $joinResultValidators = $joinResult->getValidators(); |
||
214 | if ($joinResultValidators->count() > 0) { |
||
215 | $this->verifyInputs($joinResultValidators, $transientVars, $ps); |
||
216 | } |
||
217 | |||
218 | foreach ($joinResult->getPreFunctions() as $function) { |
||
219 | $functionsEngine->executeFunction($function, $transientVars, $ps); |
||
220 | } |
||
221 | |||
222 | $previousIds = []; |
||
223 | $i = 1; |
||
224 | |||
225 | foreach ($joinSteps as $currentJoinStep) { |
||
226 | if (!$historySteps->contains($currentJoinStep) && $currentJoinStep->getId() !== $step->getId()) { |
||
227 | $store->moveToHistory($step); |
||
228 | } |
||
229 | |||
230 | $previousIds[$i] = $currentJoinStep->getId(); |
||
231 | } |
||
232 | |||
233 | View Code Duplication | if (!$action->isFinish()) { |
|
|
|||
234 | $previousIds[0] = $step->getId(); |
||
235 | $theResult = $joinDesc->getResult(); |
||
236 | |||
237 | $this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), null, $previousIds, $transientVars, $ps); |
||
238 | } |
||
239 | |||
240 | foreach ($joinResult->getPostFunctions() as $function) { |
||
241 | $functionsEngine->executeFunction($function, $transientVars, $ps); |
||
242 | } |
||
243 | } |
||
244 | View Code Duplication | } else { |
|
245 | 16 | $previousIds = []; |
|
246 | |||
247 | 16 | if (null !== $step) { |
|
248 | 5 | $previousIds[] = $step->getId(); |
|
249 | 5 | } |
|
250 | |||
251 | 16 | if (!$action->isFinish()) { |
|
252 | 16 | $this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), $step, $previousIds, $transientVars, $ps); |
|
253 | 16 | } |
|
254 | } |
||
255 | |||
256 | 16 | if ($extraPostFunctions && $extraPostFunctions->count() > 0) { |
|
257 | 2 | foreach ($extraPostFunctions as $function) { |
|
258 | 2 | $functionsEngine->executeFunction($function, $transientVars, $ps); |
|
259 | 2 | } |
|
260 | 2 | } |
|
261 | |||
262 | 16 | if (WorkflowEntryInterface::COMPLETED !== $entry->getState() && null !== $wf->getInitialAction($action->getId())) { |
|
263 | 16 | $workflowManager->changeEntryState($entry->getId(), WorkflowEntryInterface::ACTIVATED); |
|
264 | 16 | } |
|
265 | |||
266 | 16 | if ($action->isFinish()) { |
|
267 | $entryEngine = $engineManager->getEntryEngine(); |
||
268 | $entryEngine->completeEntry($action, $entry->getId(), $workflowManager->getCurrentSteps($entry->getId()), WorkflowEntryInterface::COMPLETED); |
||
269 | return true; |
||
270 | } |
||
271 | |||
272 | 16 | $availableAutoActions = $this->getAvailableAutoActions($entry->getId(), $inputs); |
|
273 | |||
274 | 16 | if (count($availableAutoActions) > 0) { |
|
275 | $workflowManager->doAction($entry->getId(), $availableAutoActions[0], $inputs); |
||
276 | } |
||
277 | |||
278 | 16 | return false; |
|
279 | 3 | } catch (\Exception $e) { |
|
280 | 3 | throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
|
281 | } |
||
282 | } |
||
283 | |||
284 | |||
285 | /** |
||
286 | * @param ResultDescriptor $theResult |
||
287 | * @param WorkflowEntryInterface $entry |
||
288 | * @param WorkflowStoreInterface $store |
||
289 | * @param integer $actionId |
||
290 | * @param StepInterface $currentStep |
||
291 | * @param array $previousIds |
||
292 | * @param TransientVarsInterface $transientVars |
||
293 | * @param PropertySetInterface $ps |
||
294 | * |
||
295 | * @return StepInterface |
||
296 | * |
||
297 | * @throws InternalWorkflowException |
||
298 | * @throws StoreException |
||
299 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
300 | * @throws WorkflowException |
||
301 | */ |
||
302 | 16 | protected function createNewCurrentStep( |
|
420 | |||
421 | |||
422 | /** |
||
423 | * |
||
424 | * Возвращает текущий шаг |
||
425 | * |
||
426 | * @param WorkflowDescriptor $wfDesc |
||
427 | * @param integer $actionId |
||
428 | * @param StepInterface[]|SplObjectStorage $currentSteps |
||
429 | * @param TransientVarsInterface $transientVars |
||
430 | * @param PropertySetInterface $ps |
||
431 | * |
||
432 | * @return StepInterface |
||
433 | * |
||
434 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
435 | * @throws InternalWorkflowException |
||
436 | */ |
||
437 | 18 | protected function getCurrentStep(WorkflowDescriptor $wfDesc, $actionId, SplObjectStorage $currentSteps, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
|
456 | |||
457 | |||
458 | /** |
||
459 | * @param $validatorsStorage |
||
460 | * @param TransientVarsInterface $transientVars |
||
461 | * @param PropertySetInterface $ps |
||
462 | * |
||
463 | * @throws InvalidInputException |
||
464 | * @throws WorkflowException |
||
465 | */ |
||
466 | 18 | protected function verifyInputs($validatorsStorage, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
|
514 | |||
515 | |||
516 | /** |
||
517 | * Подготавливает данные о шагах используемых в объеденение |
||
518 | * |
||
519 | * @param StepInterface[]|SplObjectStorage $steps |
||
520 | * @param StepInterface $step |
||
521 | * @param WorkflowDescriptor $wf |
||
522 | * @param integer $join |
||
523 | * |
||
524 | * @param array $joinSteps |
||
525 | * |
||
526 | * @return array |
||
527 | * |
||
528 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
529 | */ |
||
530 | protected function buildJoinsSteps($steps, StepInterface $step, WorkflowDescriptor $wf, $join, array $joinSteps = []) |
||
544 | |||
545 | |||
546 | /** |
||
547 | * @param $id |
||
548 | * @param TransientVarsInterface $inputs |
||
549 | * |
||
550 | * @return array |
||
551 | */ |
||
552 | 16 | protected function getAvailableAutoActions($id, TransientVarsInterface $inputs) |
|
606 | |||
607 | |||
608 | /** |
||
609 | * @param WorkflowDescriptor $wf |
||
610 | * @param StepInterface $step |
||
611 | * @param TransientVarsInterface $transientVars |
||
612 | * @param PropertySetInterface $ps |
||
613 | * |
||
614 | * @return array |
||
615 | * |
||
616 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
617 | * @throws InternalWorkflowException |
||
618 | * @throws WorkflowException |
||
619 | */ |
||
620 | 16 | protected function getAvailableAutoActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
|
641 | |||
642 | |||
643 | /** |
||
644 | * Подготавливает список id действий в workflow |
||
645 | * |
||
646 | * @param ActionDescriptor[]|SplObjectStorage $actions |
||
647 | * @param TransientVarsInterface $transientVars |
||
648 | * @param PropertySetInterface $ps |
||
649 | * @param array $storage |
||
650 | * |
||
651 | * @return array |
||
652 | * |
||
653 | * @throws InternalWorkflowException |
||
654 | * @throws WorkflowException |
||
655 | */ |
||
656 | 16 | protected function buildListIdsAvailableActions($actions, TransientVarsInterface $transientVars, PropertySetInterface $ps, array $storage = []) |
|
672 | |||
673 | |||
674 | /** |
||
675 | * |
||
676 | * @param ActionDescriptor|null $action |
||
677 | * @param TransientVarsInterface $transientVars |
||
678 | * @param PropertySetInterface $ps |
||
679 | * @param $stepId |
||
680 | * |
||
681 | * @return boolean |
||
682 | * |
||
683 | * @throws InternalWorkflowException |
||
684 | */ |
||
685 | 8 | public function isActionAvailable(ActionDescriptor $action = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $stepId) |
|
718 | |||
719 | |||
720 | |||
721 | /** |
||
722 | * |
||
723 | * По дейсвтию получаем дексрипторв workflow |
||
724 | * |
||
725 | * @param ActionDescriptor $action |
||
726 | * |
||
727 | * @return WorkflowDescriptor |
||
728 | * |
||
729 | * @throws InternalWorkflowException |
||
730 | */ |
||
731 | 8 | private function getWorkflowDescriptorForAction(ActionDescriptor $action) |
|
748 | } |
||
749 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.