Conditions | 22 |
Total Lines | 224 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
1 | <?php |
||
79 | public function execute(CommandContext $commandContext) |
||
80 | { |
||
81 | $processInstance = $commandContext->getExecutionManager()->findExecutionById($this->processInstanceId); |
||
82 | |||
83 | $processDefinition = $processInstance->getProcessDefinition(); |
||
84 | |||
85 | $elementToInstantiate = $this->getTargetElement($processDefinition); |
||
86 | |||
87 | EnsureUtil::ensureNotNull( |
||
88 | $this->describeFailure("Element '" . $this->getTargetElementId() . "' does not exist in process '" . $processDefinition->getId() . "'"), |
||
89 | "element", |
||
90 | $elementToInstantiate |
||
91 | ); |
||
92 | |||
93 | // rebuild the mapping because the execution tree changes with every iteration |
||
94 | $mapping = new ActivityExecutionTreeMapping($commandContext, $this->processInstanceId); |
||
95 | |||
96 | // before instantiating an activity, two things have to be determined: |
||
97 | // |
||
98 | // activityStack: |
||
99 | // For the activity to instantiate, we build a stack of parent flow scopes |
||
100 | // for which no executions exist yet and that have to be instantiated |
||
101 | // |
||
102 | // scopeExecution: |
||
103 | // This is typically the execution under which a new sub tree has to be created. |
||
104 | // if an explicit ancestor activity instance is set: |
||
105 | // - this is the scope execution for that ancestor activity instance |
||
106 | // - throws exception if that scope execution is not in the parent hierarchy |
||
107 | // of the activity to be started |
||
108 | // if no explicit ancestor activity instance is set: |
||
109 | // - this is the execution of the first parent/ancestor flow scope that has an execution |
||
110 | // - throws an exception if there is more than one such execution |
||
111 | |||
112 | $targetFlowScope = $this->getTargetFlowScope($processDefinition); |
||
113 | |||
114 | // prepare to walk up the flow scope hierarchy and collect the flow scope activities |
||
115 | $stackCollector = new ActivityStackCollector(); |
||
116 | $walker = new FlowScopeWalker($targetFlowScope); |
||
117 | $walker->addPreVisitor($stackCollector); |
||
118 | |||
119 | $scopeExecution = null; |
||
120 | |||
121 | // if no explicit ancestor activity instance is set |
||
122 | if ($this->ancestorActivityInstanceId === null) { |
||
123 | // walk until a scope is reached for which executions exist |
||
124 | $walker->walkWhile(new class ($processDefinition, $mapping) implements WalkConditionInterface { |
||
125 | |||
126 | private $processDefinition; |
||
127 | private $mapping; |
||
128 | |||
129 | public function __construct(ProcessDefinitionImpl $processDefinition, ActivityExecutionTreeMapping $mapping) |
||
130 | { |
||
131 | $this->processDefinition = $processDefinition; |
||
132 | $this->mapping = $mapping; |
||
133 | } |
||
134 | |||
135 | public function isFulfilled(ScopeImpl $element): bool |
||
136 | { |
||
137 | return !empty($this->mapping->getExecutions($element)) || $element == $this->processDefinition; |
||
138 | } |
||
139 | }); |
||
140 | |||
141 | $flowScopeExecutions = $mapping->getExecutions($walker->getCurrentElement()); |
||
142 | |||
143 | if (count($flowScopeExecutions) > 1) { |
||
144 | throw new ProcessEngineException("Ancestor activity execution is ambiguous for activity " . $targetFlowScope); |
||
145 | } |
||
146 | $scopeExecution = $flowScopeExecutions[0]; |
||
147 | } else { |
||
148 | $processInstanceId = $this->processInstanceId; |
||
149 | $tree = $commandContext->runWithoutAuthorization(function () use ($commandContext, $processInstanceId) { |
||
150 | $cmd = new GetActivityInstanceCmd($processInstanceId); |
||
151 | return $cmd->execute($commandContext); |
||
152 | }); |
||
153 | |||
154 | $ancestorInstance = $this->findActivityInstance($tree, $this->ancestorActivityInstanceId); |
||
|
|||
155 | EnsureUtil::ensureNotNull( |
||
156 | describeFailure("Ancestor activity instance '" . $this->ancestorActivityInstanceId . "' does not exist"), |
||
157 | "ancestorInstance", |
||
158 | $ancestorInstance |
||
159 | ); |
||
160 | |||
161 | // determine ancestor activity scope execution and activity |
||
162 | $ancestorScopeExecution = $this->getScopeExecutionForActivityInstance( |
||
163 | $processInstance, |
||
164 | $mapping, |
||
165 | $ancestorInstance |
||
166 | ); |
||
167 | $ancestorScope = $this->getScopeForActivityInstance($processDefinition, $ancestorInstance); |
||
168 | |||
169 | // walk until the scope of the ancestor scope execution is reached |
||
170 | $walker->walkWhile(new class ($mapping, $ancestorScope, $ancestorScopeExecution, $processDefinition) implements WalkConditionInterface { |
||
171 | private $mapping; |
||
172 | private $ancestorScope; |
||
173 | private $ancestorScopeExecution; |
||
174 | private $processDefinition; |
||
175 | |||
176 | public function __construct($mapping, $ancestorScope, $ancestorScopeExecution, $processDefinition) |
||
177 | { |
||
178 | $this->mapping = $mapping; |
||
179 | $this->ancestorScope = $ancestorScope; |
||
180 | $this->ancestorScopeExecution = $ancestorScopeExecution; |
||
181 | $this->processDefinition = $processDefinition; |
||
182 | } |
||
183 | |||
184 | public function isFulfilled(ScopeImpl $element): bool |
||
185 | { |
||
186 | return ( |
||
187 | in_array($this->ancestorScopeExecution, $this->mapping->getExecutions($element)) |
||
188 | && $element == $this->ancestorScope) |
||
189 | || $element == $this->processDefinition; |
||
190 | } |
||
191 | }); |
||
192 | |||
193 | $flowScopeExecutions = $mapping->getExecutions($walker->getCurrentElement()); |
||
194 | |||
195 | if (!in_array($ancestorScopeExecution, $flowScopeExecutions)) { |
||
196 | throw new NotValidException(describeFailure("Scope execution for '" . $this->ancestorActivityInstanceId . |
||
197 | "' cannot be found in parent hierarchy of flow element '" . $elementToInstantiate->getId() . "'")); |
||
198 | } |
||
199 | |||
200 | $scopeExecution = $ancestorScopeExecution; |
||
201 | } |
||
202 | |||
203 | $activitiesToInstantiate = $stackCollector->getActivityStack(); |
||
204 | $activitiesToInstantiate = array_reverse($activitiesToInstantiate); |
||
205 | |||
206 | // We have to make a distinction between |
||
207 | // - "regular" activities for which the activity stack can be instantiated and started |
||
208 | // right away |
||
209 | // - interrupting or cancelling activities for which we have to ensure that |
||
210 | // the interruption and cancellation takes place before we instantiate the activity stack |
||
211 | $topMostActivity = null; |
||
212 | $flowScope = null; |
||
213 | if (!empty($activitiesToInstantiate)) { |
||
214 | $topMostActivity = $activitiesToInstantiate[0]; |
||
215 | $flowScope = $topMostActivity->getFlowScope(); |
||
216 | } elseif (is_a($elementToInstantiate, ActivityImpl::class)) { |
||
217 | $topMostActivity = $elementToInstantiate; |
||
218 | $flowScope = $topMostActivity->getFlowScope(); |
||
219 | } elseif (is_a($elementToInstantiate, TransitionImpl::class)) { |
||
220 | $transitionToInstantiate = $elementToInstantiate; |
||
221 | $flowScope = $transitionToInstantiate->getSource()->getFlowScope(); |
||
222 | } |
||
223 | |||
224 | if (!$this->supportsConcurrentChildInstantiation($flowScope)) { |
||
225 | throw new ProcessEngineException( |
||
226 | "Concurrent instantiation not possible for activities in scope " . $flowScope->getId() |
||
227 | ); |
||
228 | } |
||
229 | |||
230 | $startBehavior = ActivityStartBehavior::CONCURRENT_IN_FLOW_SCOPE; |
||
231 | if ($topMostActivity !== null) { |
||
232 | $startBehavior = $topMostActivity->getActivityStartBehavior(); |
||
233 | |||
234 | if (!empty($activitiesToInstantiate)) { |
||
235 | // this is in BPMN relevant if there is an interrupting event sub process. |
||
236 | // we have to distinguish between instantiation of the start event and any other activity. |
||
237 | // instantiation of the start event means interrupting behavior; instantiation |
||
238 | // of any other task means no interruption. |
||
239 | $initialActivity = null; |
||
240 | $props = $topMostActivity->getProperties(); |
||
241 | if (array_key_exists(BpmnProperties::INITIAL_ACTIVITY, $props)) { |
||
242 | $initialActivity = $props[BpmnProperties::INITIAL_ACTIVITY]; |
||
243 | } |
||
244 | $secondTopMostActivity = null; |
||
245 | if (count($activitiesToInstantiate) > 1) { |
||
246 | $secondTopMostActivity = $activitiesToInstantiate[1]; |
||
247 | } elseif (is_a($elementToInstantiate, ActivityImpl::class)) { |
||
248 | $secondTopMostActivity = $elementToInstantiate; |
||
249 | } |
||
250 | |||
251 | if ($initialActivity != $secondTopMostActivity) { |
||
252 | $startBehavior = ActivityStartBehavior::CONCURRENT_IN_FLOW_SCOPE; |
||
253 | } |
||
254 | } |
||
255 | } |
||
256 | |||
257 | switch ($startBehavior) { |
||
258 | case self::CANCEL_EVENT_SCOPE: |
||
259 | $scopeToCancel = $topMostActivity->getEventScope(); |
||
260 | $executionToCancel = $this->getSingleExecutionForScope($mapping, $scopeToCancel); |
||
261 | if ($executionToCancel !== null) { |
||
262 | $executionToCancel->deleteCascade("Cancelling activity " . $topMostActivity . " executed.", $this->skipCustomListeners, $this->skipIoMappings); |
||
263 | $this->instantiate($executionToCancel->getParent(), $activitiesToInstantiate, $elementToInstantiate); |
||
264 | } else { |
||
265 | $flowScopeExecution = $this->getSingleExecutionForScope($mapping, $topMostActivity->getFlowScope()); |
||
266 | $this->instantiateConcurrent($flowScopeExecution, $activitiesToInstantiate, $elementToInstantiate); |
||
267 | } |
||
268 | break; |
||
269 | case self::INTERRUPT_EVENT_SCOPE: |
||
270 | $scopeToCancel = $topMostActivity->getEventScope(); |
||
271 | $executionToCancel = $this->getSingleExecutionForScope($mapping, $scopeToCancel); |
||
272 | $executionToCancel->interrupt("Interrupting activity " . $topMostActivity . " executed.", $this->skipCustomListeners, $this->skipIoMappings, false); |
||
273 | $executionToCancel->setActivity(null); |
||
274 | $executionToCancel->leaveActivityInstance(); |
||
275 | $this->instantiate($executionToCancel, $activitiesToInstantiate, $elementToInstantiate); |
||
276 | break; |
||
277 | case self::INTERRUPT_FLOW_SCOPE: |
||
278 | $scopeToCancel = $topMostActivity->getFlowScope(); |
||
279 | $executionToCancel = $this->getSingleExecutionForScope($mapping, $scopeToCancel); |
||
280 | $executionToCancel->interrupt("Interrupting activity " . $topMostActivity . " executed.", $this->skipCustomListeners, $this->skipIoMappings, false); |
||
281 | $executionToCancel->setActivity(null); |
||
282 | $executionToCancel->leaveActivityInstance(); |
||
283 | $this->instantiate($executionToCancel, $activitiesToInstantiate, $elementToInstantiate); |
||
284 | break; |
||
285 | default: |
||
286 | // if all child executions have been cancelled |
||
287 | // or this execution has ended executing its scope, it can be reused |
||
288 | if ( |
||
289 | !$scopeExecution->hasChildren() && |
||
290 | ($scopeExecution->getActivity() === null || $scopeExecution->isEnded()) |
||
291 | ) { |
||
292 | // reuse the scope execution |
||
293 | $this->instantiate($scopeExecution, $activitiesToInstantiate, $elementToInstantiate); |
||
294 | } else { |
||
295 | // if the activity is not cancelling/interrupting, it can simply be instantiated as |
||
296 | // a concurrent child of the scopeExecution |
||
297 | $this->instantiateConcurrent($scopeExecution, $activitiesToInstantiate, $elementToInstantiate); |
||
298 | } |
||
299 | break; |
||
300 | } |
||
301 | |||
302 | return null; |
||
303 | } |
||
394 |