This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @link https://github.com/old-town/old-town-workflow |
||
4 | * @author Malofeykin Andrey <[email protected]> |
||
5 | */ |
||
6 | namespace OldTown\Workflow; |
||
7 | |||
8 | use OldTown\Log\LogFactory; |
||
9 | use OldTown\PropertySet\PropertySetInterface; |
||
10 | use OldTown\PropertySet\PropertySetManager; |
||
11 | use OldTown\Workflow\Config\ConfigurationInterface; |
||
12 | use OldTown\Workflow\Config\DefaultConfiguration; |
||
13 | use OldTown\Workflow\Exception\FactoryException; |
||
14 | use OldTown\Workflow\Exception\InternalWorkflowException; |
||
15 | use OldTown\Workflow\Exception\InvalidActionException; |
||
16 | use OldTown\Workflow\Exception\InvalidArgumentException; |
||
17 | use OldTown\Workflow\Exception\InvalidEntryStateException; |
||
18 | use OldTown\Workflow\Exception\InvalidInputException; |
||
19 | use OldTown\Workflow\Exception\InvalidRoleException; |
||
20 | use OldTown\Workflow\Exception\StoreException; |
||
21 | use OldTown\Workflow\Exception\WorkflowException; |
||
22 | use OldTown\Workflow\Loader\ActionDescriptor; |
||
23 | use OldTown\Workflow\Loader\ConditionDescriptor; |
||
24 | use OldTown\Workflow\Loader\ConditionsDescriptor; |
||
25 | use OldTown\Workflow\Loader\FunctionDescriptor; |
||
26 | use OldTown\Workflow\Loader\RegisterDescriptor; |
||
27 | use OldTown\Workflow\Loader\ResultDescriptor; |
||
28 | use OldTown\Workflow\Loader\ValidatorDescriptor; |
||
29 | use OldTown\Workflow\Loader\WorkflowDescriptor; |
||
30 | use OldTown\Workflow\Query\WorkflowExpressionQuery; |
||
31 | use OldTown\Workflow\Spi\SimpleWorkflowEntry; |
||
32 | use OldTown\Workflow\Spi\StepInterface; |
||
33 | use OldTown\Workflow\Spi\WorkflowEntryInterface; |
||
34 | use OldTown\Workflow\Spi\WorkflowStoreInterface; |
||
35 | use OldTown\Workflow\TransientVars\TransientVarsInterface; |
||
36 | use Psr\Log\LoggerInterface; |
||
37 | use Traversable; |
||
38 | use SplObjectStorage; |
||
39 | use DateTime; |
||
40 | use OldTown\Workflow\TransientVars\BaseTransientVars; |
||
41 | use ReflectionClass; |
||
42 | use ArrayObject; |
||
43 | |||
44 | |||
45 | /** |
||
46 | * Class AbstractWorkflow |
||
47 | * |
||
48 | * @package OldTown\Workflow |
||
49 | */ |
||
50 | abstract class AbstractWorkflow implements WorkflowInterface |
||
51 | { |
||
52 | /** |
||
53 | * @var WorkflowContextInterface |
||
54 | */ |
||
55 | protected $context; |
||
56 | |||
57 | /** |
||
58 | * @var ConfigurationInterface |
||
59 | */ |
||
60 | protected $configuration; |
||
61 | |||
62 | /** |
||
63 | * |
||
64 | * @var array |
||
65 | */ |
||
66 | protected $stateCache = []; |
||
67 | |||
68 | /** |
||
69 | * @var TypeResolverInterface |
||
70 | */ |
||
71 | protected $typeResolver; |
||
72 | |||
73 | /** |
||
74 | * Логер |
||
75 | * |
||
76 | * @var LoggerInterface |
||
77 | */ |
||
78 | protected $log; |
||
79 | |||
80 | /** |
||
81 | * Резолвер для создания провайдеров отвечающих за исполнение функций, проверку условий, выполнение валидаторов и т.д. |
||
82 | * |
||
83 | * @var string |
||
84 | */ |
||
85 | protected $defaultTypeResolverClass = TypeResolver::class; |
||
86 | |||
87 | /** |
||
88 | * |
||
89 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
90 | * @throws \OldTown\Log\Exception\InvalidArgumentException |
||
91 | * @throws \OldTown\Log\Exception\DomainException |
||
92 | * |
||
93 | */ |
||
94 | public function __construct() |
||
95 | { |
||
96 | $this->log = LogFactory::getLog(); |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Инициализация workflow. Workflow нужно иницаилизровать прежде, чем выполнять какие либо действия. |
||
101 | * Workflow может быть инициализированно только один раз |
||
102 | * |
||
103 | * @param string $workflowName Имя workflow |
||
104 | * @param integer $initialAction Имя первого шага, с которого начинается workflow |
||
105 | * @param TransientVarsInterface $inputs Данные введеные пользователем |
||
106 | 19 | * @return integer |
|
107 | * @throws \OldTown\Workflow\Exception\InvalidRoleException |
||
108 | 19 | * @throws \OldTown\Workflow\Exception\InvalidInputException |
|
109 | 19 | * @throws \OldTown\Workflow\Exception\WorkflowException |
|
110 | 19 | * @throws \OldTown\Workflow\Exception\InvalidEntryStateException |
|
111 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
112 | * @throws \OldTown\Workflow\Exception\FactoryException |
||
113 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
114 | * @throws \OldTown\Workflow\Exception\StoreException |
||
115 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
116 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
117 | 19 | * |
|
118 | */ |
||
119 | 19 | public function initialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
|
120 | 19 | { |
|
121 | try { |
||
122 | 19 | $initialAction = (integer)$initialAction; |
|
123 | |||
124 | 19 | $wf = $this->getConfiguration()->getWorkflow($workflowName); |
|
125 | |||
126 | $store = $this->getPersistence(); |
||
127 | |||
128 | $entry = $store->createEntry($workflowName); |
||
129 | |||
130 | $ps = $store->getPropertySet($entry->getId()); |
||
131 | |||
132 | |||
133 | if (null === $inputs) { |
||
134 | $inputs = $this->transientVarsFactory(); |
||
135 | } |
||
136 | $transientVars = $inputs; |
||
137 | $inputs = clone $transientVars; |
||
138 | |||
139 | $this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $initialAction, new ArrayObject(), $ps); |
||
140 | |||
141 | if (!$this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps)) { |
||
142 | $this->context->setRollbackOnly(); |
||
143 | $errMsg = 'You are restricted from initializing this workflow'; |
||
144 | 19 | throw new InvalidRoleException($errMsg); |
|
145 | } |
||
146 | 19 | ||
147 | 19 | $action = $wf->getInitialAction($initialAction); |
|
148 | 19 | ||
149 | 19 | $currentSteps = new SplObjectStorage(); |
|
150 | 19 | $this->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps); |
|
0 ignored issues
–
show
|
|||
151 | 19 | ||
152 | 19 | $entryId = $entry->getId(); |
|
153 | 19 | } catch (WorkflowException $e) { |
|
154 | $this->context->setRollbackOnly(); |
||
155 | 19 | throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
|
156 | 19 | } |
|
157 | 19 | ||
158 | 19 | ||
159 | 19 | // now clone the memory PS to the real PS |
|
160 | 19 | //PropertySetManager.clone(ps, store.getPropertySet(entryId)); |
|
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
47% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
161 | 19 | return $entryId; |
|
162 | 19 | } |
|
163 | 19 | ||
164 | 19 | /** |
|
165 | 19 | * @param WorkflowEntryInterface $entry |
|
166 | * @param TransientVarsInterface $transientVars |
||
167 | * @param array|Traversable|RegisterDescriptor[]|SplObjectStorage $registersStorage |
||
168 | * @param integer $actionId |
||
169 | * @param array|Traversable $currentSteps |
||
170 | * @param PropertySetInterface $ps |
||
171 | * |
||
172 | 19 | * |
|
173 | * @return TransientVarsInterface |
||
174 | 1 | * |
|
175 | 19 | * @throws \OldTown\Workflow\Exception\WorkflowException |
|
176 | 19 | * @throws \OldTown\Workflow\Exception\FactoryException |
|
177 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
178 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
179 | 19 | * @throws \OldTown\Workflow\Exception\StoreException |
|
180 | */ |
||
181 | protected function populateTransientMap(WorkflowEntryInterface $entry, TransientVarsInterface $transientVars, $registersStorage, $actionId = null, $currentSteps, PropertySetInterface $ps) |
||
182 | { |
||
183 | View Code Duplication | if (!is_array($currentSteps) && !$currentSteps instanceof Traversable) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
184 | $errMsg = 'CurrentSteps должен быть массивом, либо реализовывать интерфейс Traversable'; |
||
185 | throw new InvalidArgumentException($errMsg); |
||
186 | } |
||
187 | |||
188 | View Code Duplication | if ($registersStorage instanceof Traversable) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
189 | $registers = []; |
||
190 | foreach ($registersStorage as $k => $v) { |
||
191 | $registers[$k] = $v; |
||
192 | } |
||
193 | } elseif (is_array($registersStorage)) { |
||
194 | $registers = $registersStorage; |
||
195 | } else { |
||
196 | $errMsg = 'Registers должен быть массивом, либо реализовывать интерфейс Traversable'; |
||
197 | throw new InvalidArgumentException($errMsg); |
||
198 | 19 | } |
|
199 | /** @var RegisterDescriptor[] $registers */ |
||
200 | |||
201 | 19 | $transientVars['context'] = $this->context; |
|
202 | $transientVars['entry'] = $entry; |
||
203 | 19 | $transientVars['entryId'] = $entry->getId(); |
|
204 | $transientVars['store'] = $this->getPersistence(); |
||
205 | 19 | $transientVars['configuration'] = $this->getConfiguration(); |
|
206 | $transientVars['descriptor'] = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
||
207 | 19 | ||
208 | if (null !== $actionId) { |
||
209 | 19 | $transientVars['actionId'] = $actionId; |
|
210 | } |
||
211 | |||
212 | 19 | $transientVars['currentSteps'] = $currentSteps; |
|
213 | |||
214 | |||
215 | foreach ($registers as $register) { |
||
216 | 19 | $args = $register->getArgs(); |
|
217 | $type = $register->getType(); |
||
218 | 19 | ||
219 | 19 | try { |
|
220 | $r = $this->getResolver()->getRegister($type, $args); |
||
221 | 19 | } catch (\Exception $e) { |
|
222 | $errMsg = 'Ошибка при инициализации register'; |
||
223 | 19 | $this->context->setRollbackOnly(); |
|
224 | 1 | throw new WorkflowException($errMsg, $e->getCode(), $e); |
|
225 | 1 | } |
|
226 | 1 | ||
227 | $variableName = $register->getVariableName(); |
||
228 | try { |
||
229 | 18 | $value = $r->registerVariable($this->context, $entry, $args, $ps); |
|
230 | |||
231 | 18 | $transientVars[$variableName] = $value; |
|
232 | } catch (\Exception $e) { |
||
233 | $this->context->setRollbackOnly(); |
||
234 | |||
235 | $errMsg = sprintf( |
||
236 | 18 | 'При получение значения переменной %s из registry %s произошла ошибка', |
|
237 | 18 | $variableName, |
|
238 | 18 | get_class($r) |
|
239 | ); |
||
240 | 16 | ||
241 | 19 | throw new WorkflowException($errMsg, $e->getCode(), $e); |
|
242 | } |
||
243 | } |
||
244 | |||
245 | return $transientVars; |
||
246 | 16 | } |
|
247 | |||
248 | /** |
||
249 | * Переход между двумя статусами |
||
250 | * |
||
251 | * @param WorkflowEntryInterface $entry |
||
252 | * @param SplObjectStorage|StepInterface[] $currentSteps |
||
253 | * @param WorkflowStoreInterface $store |
||
254 | * @param WorkflowDescriptor $wf |
||
255 | * @param ActionDescriptor $action |
||
256 | 2 | * @param TransientVarsInterface $transientVars |
|
257 | 2 | * @param TransientVarsInterface $inputs |
|
258 | * @param PropertySetInterface $ps |
||
259 | * |
||
260 | * @return boolean |
||
261 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
262 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
263 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
264 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
265 | * @throws \OldTown\Workflow\Exception\StoreException |
||
266 | * @throws \OldTown\Workflow\Exception\InvalidEntryStateException |
||
267 | * @throws \OldTown\Workflow\Exception\InvalidInputException |
||
268 | * @throws \OldTown\Workflow\Exception\FactoryException |
||
269 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
270 | */ |
||
271 | protected function transitionWorkflow(WorkflowEntryInterface $entry, SplObjectStorage $currentSteps, WorkflowStoreInterface $store, WorkflowDescriptor $wf, ActionDescriptor $action, TransientVarsInterface $transientVars, TransientVarsInterface $inputs, PropertySetInterface $ps) |
||
272 | { |
||
273 | $step = $this->getCurrentStep($wf, $action->getId(), $currentSteps, $transientVars, $ps); |
||
274 | |||
275 | $validators = $action->getValidators(); |
||
276 | if ($validators->count() > 0) { |
||
277 | $this->verifyInputs($entry, $validators, $transientVars, $ps); |
||
278 | } |
||
279 | |||
280 | |||
281 | if (null !== $step) { |
||
282 | $stepPostFunctions = $wf->getStep($step->getStepId())->getPostFunctions(); |
||
283 | foreach ($stepPostFunctions as $function) { |
||
284 | $this->executeFunction($function, $transientVars, $ps); |
||
285 | } |
||
286 | } |
||
287 | |||
288 | $preFunctions = $action->getPreFunctions(); |
||
289 | foreach ($preFunctions as $preFunction) { |
||
290 | $this->executeFunction($preFunction, $transientVars, $ps); |
||
291 | } |
||
292 | |||
293 | $conditionalResults = $action->getConditionalResults(); |
||
294 | $extraPreFunctions = null; |
||
295 | $extraPostFunctions = null; |
||
296 | |||
297 | $theResult = null; |
||
298 | |||
299 | |||
300 | $currentStepId = null !== $step ? $step->getStepId() : -1; |
||
301 | foreach ($conditionalResults as $conditionalResult) { |
||
302 | if ($this->passesConditionsWithType(null, $conditionalResult->getConditions(), $transientVars, $ps, $currentStepId)) { |
||
303 | $theResult = $conditionalResult; |
||
304 | |||
305 | $validatorsStorage = $conditionalResult->getValidators(); |
||
306 | if ($validatorsStorage->count() > 0) { |
||
307 | $this->verifyInputs($entry, $validatorsStorage, $transientVars, $ps); |
||
308 | } |
||
309 | |||
310 | $extraPreFunctions = $conditionalResult->getPreFunctions(); |
||
311 | $extraPostFunctions = $conditionalResult->getPostFunctions(); |
||
312 | |||
313 | break; |
||
314 | } |
||
315 | } |
||
316 | |||
317 | |||
318 | if (null === $theResult) { |
||
319 | $theResult = $action->getUnconditionalResult(); |
||
320 | $this->verifyInputs($entry, $theResult->getValidators(), $transientVars, $ps); |
||
321 | $extraPreFunctions = $theResult->getPreFunctions(); |
||
322 | $extraPostFunctions = $theResult->getPostFunctions(); |
||
323 | } |
||
324 | |||
325 | $logMsg = sprintf('theResult=%s %s', $theResult->getStep(), $theResult->getStatus()); |
||
326 | $this->getLog()->debug($logMsg); |
||
327 | |||
328 | |||
329 | if ($extraPreFunctions && $extraPreFunctions->count() > 0) { |
||
330 | foreach ($extraPreFunctions as $function) { |
||
331 | $this->executeFunction($function, $transientVars, $ps); |
||
332 | } |
||
333 | } |
||
334 | |||
335 | $split = $theResult->getSplit(); |
||
336 | $join = $theResult->getJoin(); |
||
337 | if (null !== $split && 0 !== $split) { |
||
338 | $splitDesc = $wf->getSplit($split); |
||
339 | $results = $splitDesc->getResults(); |
||
340 | $splitPreFunctions = []; |
||
341 | $splitPostFunctions = []; |
||
342 | |||
343 | foreach ($results as $resultDescriptor) { |
||
344 | if ($resultDescriptor->getValidators()->count() > 0) { |
||
345 | $this->verifyInputs($entry, $resultDescriptor->getValidators(), $transientVars, $ps); |
||
346 | } |
||
347 | |||
348 | foreach ($resultDescriptor->getPreFunctions() as $function) { |
||
349 | $splitPreFunctions[] = $function; |
||
350 | } |
||
351 | foreach ($resultDescriptor->getPostFunctions() as $function) { |
||
352 | $splitPostFunctions[] = $function; |
||
353 | } |
||
354 | } |
||
355 | 8 | ||
356 | foreach ($splitPreFunctions as $function) { |
||
357 | 8 | $this->executeFunction($function, $transientVars, $ps); |
|
358 | 8 | } |
|
359 | |||
360 | if (!$action->isFinish()) { |
||
361 | 8 | $moveFirst = true; |
|
362 | 8 | ||
363 | //??????????????????? |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
67% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
364 | 8 | // $theResults = []; |
|
365 | 8 | // foreach ($results as $result) { |
|
366 | // $theResults[] = $result; |
||
367 | 8 | // } |
|
368 | |||
369 | foreach ($results as $resultDescriptor) { |
||
370 | $moveToHistoryStep = null; |
||
371 | 8 | ||
372 | if ($moveFirst) { |
||
373 | 8 | $moveToHistoryStep = $step; |
|
374 | 8 | } |
|
375 | |||
376 | $previousIds = []; |
||
377 | |||
378 | if (null !== $step) { |
||
379 | 8 | $previousIds[] = $step->getStepId(); |
|
380 | } |
||
381 | 8 | ||
382 | $this->createNewCurrentStep($resultDescriptor, $entry, $store, $action->getId(), $moveToHistoryStep, $previousIds, $transientVars, $ps); |
||
383 | 8 | $moveFirst = false; |
|
384 | 8 | } |
|
385 | } |
||
386 | 8 | ||
387 | 8 | ||
388 | foreach ($splitPostFunctions as $function) { |
||
389 | $this->executeFunction($function, $transientVars, $ps); |
||
390 | 8 | } |
|
391 | 8 | } elseif (null !== $join && 0 !== $join) { |
|
392 | 6 | $joinDesc = $wf->getJoin($join); |
|
393 | 6 | $oldStatus = $theResult->getOldStatus(); |
|
394 | $caller = $this->context->getCaller(); |
||
395 | 8 | $step = $store->markFinished($step, $action->getId(), new DateTime(), $oldStatus, $caller); |
|
0 ignored issues
–
show
It seems like
$step can be null ; however, markFinished() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
396 | 2 | ||
397 | 2 | $store->moveToHistory($step); |
|
398 | |||
399 | 2 | /** @var StepInterface[] $joinSteps */ |
|
400 | 2 | $joinSteps = []; |
|
401 | $joinSteps[] = $step; |
||
402 | |||
403 | View Code Duplication | foreach ($currentSteps as $currentStep) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
404 | if ($currentStep->getId() !== $step->getId()) { |
||
405 | 6 | $stepDesc = $wf->getStep($currentStep->getStepId()); |
|
406 | 6 | ||
407 | if ($stepDesc->resultsInJoin($join)) { |
||
408 | $joinSteps[] = $currentSteps; |
||
409 | 6 | } |
|
410 | } |
||
411 | } |
||
412 | |||
413 | $historySteps = $store->findHistorySteps($entry->getId()); |
||
414 | 5 | ||
415 | View Code Duplication | foreach ($historySteps as $historyStep) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
416 | if ($historyStep->getId() !== $step->getId()) { |
||
417 | $stepDesc = $wf->getStep($historyStep->getStepId()); |
||
418 | |||
419 | if ($stepDesc->resultsInJoin($join)) { |
||
420 | $joinSteps[] = $currentSteps; |
||
421 | } |
||
422 | } |
||
423 | } |
||
424 | 8 | ||
425 | $jn = new JoinNodes($joinSteps); |
||
426 | 8 | $transientVars['jn'] = $jn; |
|
427 | 8 | ||
428 | 8 | ||
429 | if ($this->passesConditionsWithType(null, $joinDesc->getConditions(), $transientVars, $ps, 0)) { |
||
430 | $joinResult = $joinDesc->getResult(); |
||
431 | |||
432 | 8 | $joinResultValidators = $joinResult->getValidators(); |
|
433 | if ($joinResultValidators->count() > 0) { |
||
434 | 8 | $this->verifyInputs($entry, $joinResultValidators, $transientVars, $ps); |
|
435 | } |
||
436 | |||
437 | foreach ($joinResult->getPreFunctions() as $function) { |
||
438 | $this->executeFunction($function, $transientVars, $ps); |
||
439 | } |
||
440 | |||
441 | $previousIds = []; |
||
442 | $i = 1; |
||
443 | |||
444 | foreach ($joinSteps as $currentStep) { |
||
445 | if (!$historySteps->contains($currentStep) && $currentStep->getId() !== $step->getId()) { |
||
0 ignored issues
–
show
The method
getId does only exist in OldTown\Workflow\Spi\StepInterface , but not in SplObjectStorage .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
![]() |
|||
446 | $store->moveToHistory($step); |
||
447 | } |
||
448 | |||
449 | $previousIds[$i] = $currentStep->getId(); |
||
450 | } |
||
451 | 8 | ||
452 | if (!$action->isFinish()) { |
||
453 | 8 | $previousIds[0] = $step->getId(); |
|
454 | 8 | $theResult = $joinDesc->getResult(); |
|
455 | 8 | ||
456 | 8 | $this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), null, $previousIds, $transientVars, $ps); |
|
457 | } |
||
458 | |||
459 | foreach ($joinResult->getPostFunctions() as $function) { |
||
460 | 8 | $this->executeFunction($function, $transientVars, $ps); |
|
461 | } |
||
462 | 8 | } |
|
463 | 8 | } else { |
|
464 | $previousIds = []; |
||
465 | |||
466 | if (null !== $step) { |
||
467 | $previousIds[] = $step->getId(); |
||
468 | 8 | } |
|
469 | 6 | ||
470 | 6 | if (!$action->isFinish()) { |
|
471 | 8 | $this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), $step, $previousIds, $transientVars, $ps); |
|
472 | 8 | } |
|
473 | } |
||
474 | 8 | ||
475 | if ($extraPostFunctions && $extraPostFunctions->count() > 0) { |
||
476 | foreach ($extraPostFunctions as $function) { |
||
477 | $this->executeFunction($function, $transientVars, $ps); |
||
478 | } |
||
479 | } |
||
480 | |||
481 | if (WorkflowEntryInterface::COMPLETED !== $entry->getState() && null !== $wf->getInitialAction($action->getId())) { |
||
482 | $this->changeEntryState($entry->getId(), WorkflowEntryInterface::ACTIVATED); |
||
483 | } |
||
484 | |||
485 | if ($action->isFinish()) { |
||
486 | $this->completeEntry($action, $entry->getId(), $this->getCurrentSteps($entry->getId()), WorkflowEntryInterface::COMPLETED); |
||
487 | 8 | return true; |
|
488 | 8 | } |
|
489 | |||
490 | $availableAutoActions = $this->getAvailableAutoActions($entry->getId(), $inputs); |
||
491 | |||
492 | if (count($availableAutoActions) > 0) { |
||
493 | $this->doAction($entry->getId(), $availableAutoActions[0], $inputs); |
||
494 | } |
||
495 | |||
496 | return false; |
||
497 | } |
||
498 | |||
499 | |||
500 | /** |
||
501 | * @param $id |
||
502 | * @param TransientVarsInterface $inputs |
||
503 | * |
||
504 | * @return array |
||
505 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
506 | */ |
||
507 | protected function getAvailableAutoActions($id, TransientVarsInterface $inputs) |
||
508 | { |
||
509 | try { |
||
510 | $store = $this->getPersistence(); |
||
511 | $entry = $store->findEntry($id); |
||
512 | |||
513 | if (null === $entry) { |
||
514 | $errMsg = sprintf( |
||
515 | 'Нет сущности workflow c id %s', |
||
516 | $id |
||
517 | ); |
||
518 | throw new InvalidArgumentException($errMsg); |
||
519 | } |
||
520 | |||
521 | |||
522 | if (WorkflowEntryInterface::ACTIVATED !== $entry->getState()) { |
||
523 | $logMsg = sprintf('--> состояние %s', $entry->getState()); |
||
524 | $this->getLog()->debug($logMsg); |
||
525 | return [0]; |
||
526 | } |
||
527 | |||
528 | $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
||
529 | 16 | ||
530 | View Code Duplication | if (null === $wf) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
531 | 16 | $errMsg = sprintf( |
|
532 | 16 | 'Нет workflow c именем %s', |
|
533 | $entry->getWorkflowName() |
||
534 | 16 | ); |
|
535 | throw new InvalidArgumentException($errMsg); |
||
536 | 16 | } |
|
537 | |||
538 | $l = []; |
||
539 | $ps = $store->getPropertySet($id); |
||
540 | $transientVars = $inputs; |
||
541 | $currentSteps = $store->findCurrentSteps($id); |
||
542 | |||
543 | $this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), 0, $currentSteps, $ps); |
||
544 | |||
545 | $globalActions = $wf->getGlobalActions(); |
||
546 | |||
547 | View Code Duplication | foreach ($globalActions as $action) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
548 | $transientVars['actionId'] = $action->getId(); |
||
549 | |||
550 | if ($action->getAutoExecute() && $this->isActionAvailable($action, $transientVars, $ps, 0)) { |
||
551 | $l[] = $action->getId(); |
||
552 | } |
||
553 | } |
||
554 | |||
555 | View Code Duplication | foreach ($currentSteps as $step) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
556 | $availableAutoActionsForStep = $this->getAvailableAutoActionsForStep($wf, $step, $transientVars, $ps); |
||
557 | foreach ($availableAutoActionsForStep as $v) { |
||
558 | $l[] = $v; |
||
559 | } |
||
560 | //$l = array_merge($l, $availableAutoActionsForStep); |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
59% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
561 | } |
||
562 | |||
563 | $l = array_unique($l); |
||
564 | |||
565 | return $l; |
||
566 | } catch (\Exception $e) { |
||
567 | $errMsg = 'Ошибка при проверке доступных действий'; |
||
568 | $this->getLog()->error($errMsg, [$e]); |
||
569 | } |
||
570 | |||
571 | return []; |
||
572 | } |
||
573 | |||
574 | |||
575 | /** |
||
576 | * @param WorkflowDescriptor $wf |
||
577 | * @param StepInterface $step |
||
578 | * @param TransientVarsInterface $transientVars |
||
579 | 19 | * @param PropertySetInterface $ps |
|
580 | * |
||
581 | * @return array |
||
582 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
583 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
584 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
585 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
586 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
587 | */ |
||
588 | 19 | protected function getAvailableAutoActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
|
589 | { |
||
590 | $l = []; |
||
591 | $s = $wf->getStep($step->getStepId()); |
||
592 | |||
593 | if (null === $s) { |
||
594 | $msg = sprintf('getAvailableAutoActionsForStep вызвана с несуществующим id %s', $step->getStepId()); |
||
595 | $this->getLog()->debug($msg); |
||
596 | return $l; |
||
597 | } |
||
598 | |||
599 | |||
600 | $actions = $s->getActions(); |
||
601 | if (null === $actions || 0 === $actions->count()) { |
||
602 | return $l; |
||
603 | } |
||
604 | |||
605 | View Code Duplication | foreach ($actions as $action) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
606 | $transientVars['actionId'] = $action->getId(); |
||
607 | |||
608 | if ($action->getAutoExecute() && $this->isActionAvailable($action, $transientVars, $ps, 0)) { |
||
609 | $l[] = $action->getId(); |
||
610 | } |
||
611 | } |
||
612 | |||
613 | 16 | return $l; |
|
614 | } |
||
615 | 16 | ||
616 | 16 | /** |
|
617 | * @param ActionDescriptor $action |
||
618 | 16 | * @param $id |
|
619 | * @param array|Traversable $currentSteps |
||
620 | * @param $state |
||
621 | * |
||
622 | 16 | * @return void |
|
623 | 16 | * |
|
624 | * @throws \OldTown\Workflow\Exception\StoreException |
||
625 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
626 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
627 | */ |
||
628 | protected function completeEntry(ActionDescriptor $action = null, $id, $currentSteps, $state) |
||
629 | { |
||
630 | View Code Duplication | if (!($currentSteps instanceof Traversable || is_array($currentSteps))) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
631 | $errMsg = 'CurrentSteps должен быть массивом, либо реализовывать интерфейс Traversable'; |
||
632 | 16 | throw new InvalidArgumentException($errMsg); |
|
633 | 16 | } |
|
634 | |||
635 | |||
636 | $this->getPersistence()->setEntryState($id, $state); |
||
637 | |||
638 | $oldStatus = null !== $action ? $action->getUnconditionalResult()->getOldStatus() : 'Finished'; |
||
639 | $actionIdValue = null !== $action ? $action->getId() : -1; |
||
640 | foreach ($currentSteps as $step) { |
||
641 | $this->getPersistence()->markFinished($step, $actionIdValue, new DateTime(), $oldStatus, $this->context->getCaller()); |
||
642 | $this->getPersistence()->moveToHistory($step); |
||
643 | } |
||
644 | 16 | } |
|
645 | 16 | /** |
|
646 | 16 | * @param ResultDescriptor $theResult |
|
647 | 16 | * @param WorkflowEntryInterface $entry |
|
648 | 16 | * @param WorkflowStoreInterface $store |
|
649 | 16 | * @param integer $actionId |
|
650 | 16 | * @param StepInterface $currentStep |
|
651 | * @param array $previousIds |
||
652 | * @param TransientVarsInterface $transientVars |
||
653 | * @param PropertySetInterface $ps |
||
654 | * |
||
655 | * @return StepInterface |
||
656 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
657 | * @throws \OldTown\Workflow\Exception\StoreException |
||
658 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
659 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
660 | */ |
||
661 | protected function createNewCurrentStep( |
||
662 | ResultDescriptor $theResult, |
||
663 | WorkflowEntryInterface $entry, |
||
664 | WorkflowStoreInterface $store, |
||
665 | $actionId, |
||
666 | StepInterface $currentStep = null, |
||
667 | array $previousIds = [], |
||
668 | TransientVarsInterface $transientVars, |
||
669 | PropertySetInterface $ps |
||
670 | ) { |
||
671 | try { |
||
672 | $nextStep = $theResult->getStep(); |
||
673 | |||
674 | if (-1 === $nextStep) { |
||
675 | if (null !== $currentStep) { |
||
676 | $nextStep = $currentStep->getStepId(); |
||
677 | } else { |
||
678 | $errMsg = 'Неверный аргумент. Новый шаг является таким же как текущий. Но текущий шаг не указан'; |
||
679 | throw new StoreException($errMsg); |
||
680 | } |
||
681 | } |
||
682 | |||
683 | $owner = $theResult->getOwner(); |
||
684 | |||
685 | $logMsg = sprintf( |
||
686 | 'Результат: stepId=%s, status=%s, owner=%s, actionId=%s, currentStep=%s', |
||
687 | $nextStep, |
||
688 | $theResult->getStatus(), |
||
689 | $owner, |
||
690 | $actionId, |
||
691 | null !== $currentStep ? $currentStep->getId() : 0 |
||
692 | ); |
||
693 | $this->getLog()->debug($logMsg); |
||
694 | |||
695 | $variableResolver = $this->getConfiguration()->getVariableResolver(); |
||
696 | |||
697 | if (null !== $owner) { |
||
698 | $o = $variableResolver->translateVariables($owner, $transientVars, $ps); |
||
699 | $owner = null !== $o ? (string)$o : null; |
||
700 | } |
||
701 | |||
702 | |||
703 | $oldStatus = $theResult->getOldStatus(); |
||
704 | $oldStatus = (string)$variableResolver->translateVariables($oldStatus, $transientVars, $ps); |
||
705 | |||
706 | $status = $theResult->getStatus(); |
||
707 | $status = (string)$variableResolver->translateVariables($status, $transientVars, $ps); |
||
708 | |||
709 | |||
710 | if (null !== $currentStep) { |
||
711 | $store->markFinished($currentStep, $actionId, new DateTime(), $oldStatus, $this->context->getCaller()); |
||
712 | $store->moveToHistory($currentStep); |
||
713 | } |
||
714 | |||
715 | $startDate = new DateTime(); |
||
716 | $dueDate = null; |
||
717 | |||
718 | $theResultDueDate = (string)$theResult->getDueDate(); |
||
719 | $theResultDueDate = trim($theResultDueDate); |
||
720 | if (strlen($theResultDueDate) > 0) { |
||
721 | $dueDateObject = $variableResolver->translateVariables($theResultDueDate, $transientVars, $ps); |
||
722 | |||
723 | if ($dueDateObject instanceof DateTime) { |
||
724 | $dueDate = $dueDateObject; |
||
725 | } elseif (is_string($dueDateObject)) { |
||
726 | $dueDate = new DateTime($dueDate); |
||
727 | } elseif (is_numeric($dueDateObject)) { |
||
728 | $dueDate = DateTime::createFromFormat('U', $dueDateObject); |
||
729 | } else { |
||
730 | $errMsg = 'Ошибка при преобразование DueData'; |
||
731 | 19 | throw new InternalWorkflowException($errMsg); |
|
732 | } |
||
733 | 19 | } |
|
734 | |||
735 | 19 | $newStep = $store->createCurrentStep($entry->getId(), $nextStep, $owner, $startDate, $dueDate, $status, $previousIds); |
|
0 ignored issues
–
show
It seems like
$dueDate defined by \DateTime::createFromFormat('U', $dueDateObject) on line 728 can also be of type false ; however, OldTown\Workflow\Spi\Wor...ce::createCurrentStep() does only seem to accept null|object<DateTime> , did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new ![]() |
|||
736 | $transientVars['createdStep'] = $newStep; |
||
737 | 19 | ||
738 | if (null === $currentStep && 0 === count($previousIds)) { |
||
739 | $currentSteps = []; |
||
740 | $currentSteps[] = $newStep; |
||
741 | $transientVars['currentSteps'] = $currentSteps; |
||
742 | } |
||
743 | |||
744 | if (! $transientVars->offsetExists('descriptor')) { |
||
745 | 19 | $errMsg = 'Ошибка при получение дескриптора workflow из transientVars'; |
|
746 | throw new InternalWorkflowException($errMsg); |
||
747 | } |
||
748 | 19 | ||
749 | 19 | /** @var WorkflowDescriptor $descriptor */ |
|
750 | 2 | $descriptor = $transientVars['descriptor']; |
|
751 | 2 | $step = $descriptor->getStep($nextStep); |
|
752 | |||
753 | 19 | if (null === $step) { |
|
754 | 19 | $errMsg = sprintf('Шаг #%s не найден', $nextStep); |
|
755 | throw new WorkflowException($errMsg); |
||
756 | 19 | } |
|
757 | |||
758 | $preFunctions = $step->getPreFunctions(); |
||
759 | |||
760 | foreach ($preFunctions as $function) { |
||
761 | $this->executeFunction($function, $transientVars, $ps); |
||
762 | } |
||
763 | } catch (WorkflowException $e) { |
||
764 | 19 | $this->context->setRollbackOnly(); |
|
765 | throw $e; |
||
766 | 19 | } |
|
767 | 16 | } |
|
768 | |||
769 | /** |
||
770 | 19 | * Создает хранилище переменных |
|
771 | 19 | * |
|
772 | 19 | * @param $class |
|
773 | 19 | * |
|
774 | * @return TransientVarsInterface |
||
775 | 19 | */ |
|
776 | protected function transientVarsFactory($class = BaseTransientVars::class) |
||
777 | { |
||
778 | $r = new \ReflectionClass($class); |
||
779 | return $r->newInstance(); |
||
780 | } |
||
781 | |||
782 | /** |
||
783 | * |
||
784 | * |
||
785 | 19 | * Perform an action on the specified workflow instance. |
|
786 | * @param integer $id The workflow instance id. |
||
787 | 19 | * @param integer $actionId The action id to perform (action id's are listed in the workflow descriptor). |
|
788 | * @param TransientVarsInterface $inputs The inputs to the workflow instance. |
||
789 | * @throws \OldTown\Workflow\Exception\InvalidInputException if a validator is specified and an input is invalid. |
||
790 | * @throws WorkflowException if the action is invalid for the specified workflow |
||
791 | * instance's current state. |
||
792 | * |
||
793 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
794 | * @throws \OldTown\Workflow\Exception\StoreException |
||
795 | * @throws \OldTown\Workflow\Exception\FactoryException |
||
796 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
797 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
798 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
799 | * @throws \OldTown\Workflow\Exception\InvalidEntryStateException |
||
800 | 19 | * @throws \OldTown\Workflow\Exception\WorkflowException |
|
801 | */ |
||
802 | 19 | public function doAction($id, $actionId, TransientVarsInterface $inputs = null) |
|
803 | { |
||
804 | 19 | $actionId = (integer)$actionId; |
|
805 | if (null === $inputs) { |
||
806 | $inputs = $this->transientVarsFactory(); |
||
807 | } |
||
808 | $transientVars = $inputs; |
||
809 | $inputs = clone $transientVars; |
||
810 | |||
811 | $store = $this->getPersistence(); |
||
812 | $entry = $store->findEntry($id); |
||
813 | |||
814 | 19 | if (WorkflowEntryInterface::ACTIVATED !== $entry->getState()) { |
|
815 | return; |
||
816 | } |
||
817 | |||
818 | $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
||
819 | |||
820 | 17 | $currentSteps = $store->findCurrentSteps($id); |
|
821 | $action = null; |
||
822 | 17 | ||
823 | $ps = $store->getPropertySet($id); |
||
824 | |||
825 | $this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $actionId, $currentSteps, $ps); |
||
826 | |||
827 | |||
828 | $validAction = false; |
||
829 | |||
830 | View Code Duplication | foreach ($wf->getGlobalActions() as $actionDesc) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
831 | if ($actionId === $actionDesc->getId()) { |
||
832 | $action = $actionDesc; |
||
833 | |||
834 | if ($this->isActionAvailable($action, $transientVars, $ps, 0)) { |
||
835 | $validAction = true; |
||
836 | } |
||
837 | } |
||
838 | } |
||
839 | |||
840 | |||
841 | foreach ($currentSteps as $step) { |
||
842 | $s = $wf->getStep($step->getStepId()); |
||
843 | |||
844 | View Code Duplication | foreach ($s->getActions() as $actionDesc) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
845 | if ($actionId === $actionDesc->getId()) { |
||
846 | $action = $actionDesc; |
||
847 | |||
848 | if ($this->isActionAvailable($action, $transientVars, $ps, $s->getId())) { |
||
849 | $validAction = true; |
||
850 | } |
||
851 | } |
||
852 | } |
||
853 | } |
||
854 | |||
855 | |||
856 | if (!$validAction) { |
||
857 | $errMsg = sprintf( |
||
858 | 'Action %s is invalid', |
||
859 | $actionId |
||
860 | ); |
||
861 | throw new InvalidActionException($errMsg); |
||
862 | } |
||
863 | |||
864 | |||
865 | try { |
||
866 | if ($this->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps)) { |
||
0 ignored issues
–
show
$currentSteps is of type array<integer,object<Old...low\Spi\StepInterface>> , but the function expects a object<SplObjectStorage> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
867 | $this->checkImplicitFinish($action, $id); |
||
868 | } |
||
869 | } catch (WorkflowException $e) { |
||
870 | $this->context->setRollbackOnly(); |
||
871 | throw $e; |
||
872 | } |
||
873 | } |
||
874 | |||
875 | /** |
||
876 | * @param ActionDescriptor $action |
||
877 | * @param $id |
||
878 | * |
||
879 | * @return void |
||
880 | * |
||
881 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
882 | * @throws \OldTown\Workflow\Exception\StoreException |
||
883 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
884 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
885 | * |
||
886 | */ |
||
887 | protected function checkImplicitFinish(ActionDescriptor $action, $id) |
||
888 | { |
||
889 | $store = $this->getPersistence(); |
||
890 | $entry = $store->findEntry($id); |
||
891 | |||
892 | $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
||
893 | |||
894 | $currentSteps = $store->findCurrentSteps($id); |
||
895 | |||
896 | $isCompleted = $wf->getGlobalActions()->count() === 0; |
||
897 | |||
898 | foreach ($currentSteps as $step) { |
||
899 | if ($isCompleted) { |
||
900 | break; |
||
901 | } |
||
902 | |||
903 | $stepDes = $wf->getStep($step->getStepId()); |
||
904 | |||
905 | if ($stepDes->getActions()->count() > 0) { |
||
906 | $isCompleted = true; |
||
907 | } |
||
908 | } |
||
909 | |||
910 | if ($isCompleted) { |
||
911 | $this->completeEntry($action, $id, $currentSteps, WorkflowEntryInterface::COMPLETED); |
||
912 | } |
||
913 | } |
||
914 | |||
915 | /** |
||
916 | * |
||
917 | * Check if the state of the specified workflow instance can be changed to the new specified one. |
||
918 | * @param integer $id The workflow instance id. |
||
919 | * @param integer $newState The new state id. |
||
920 | * @return boolean true if the state of the workflow can be modified, false otherwise. |
||
921 | * @throws \OldTown\Workflow\Exception\StoreException |
||
922 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
923 | * |
||
924 | */ |
||
925 | public function canModifyEntryState($id, $newState) |
||
926 | { |
||
927 | $store = $this->getPersistence(); |
||
928 | $entry = $store->findEntry($id); |
||
929 | |||
930 | $currentState = $entry->getState(); |
||
931 | |||
932 | $result = false; |
||
933 | try { |
||
934 | switch ($newState) { |
||
935 | case WorkflowEntryInterface::COMPLETED: { |
||
936 | if (WorkflowEntryInterface::ACTIVATED === $currentState) { |
||
937 | $result = true; |
||
938 | } |
||
939 | break; |
||
940 | } |
||
941 | |||
942 | //@TODO Разобраться с бизнес логикой. Может быть нужно добавить break |
||
943 | /** @noinspection PhpMissingBreakStatementInspection */ |
||
944 | case WorkflowEntryInterface::CREATED: { |
||
945 | $result = false; |
||
946 | } |
||
947 | View Code Duplication | case WorkflowEntryInterface::ACTIVATED: { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
948 | if (WorkflowEntryInterface::CREATED === $currentState || WorkflowEntryInterface::SUSPENDED === $currentState) { |
||
949 | $result = true; |
||
950 | } |
||
951 | break; |
||
952 | } |
||
953 | case WorkflowEntryInterface::SUSPENDED: { |
||
954 | if (WorkflowEntryInterface::ACTIVATED === $currentState) { |
||
955 | $result = true; |
||
956 | } |
||
957 | break; |
||
958 | } |
||
959 | View Code Duplication | case WorkflowEntryInterface::KILLED: { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
960 | if (WorkflowEntryInterface::CREATED === $currentState || WorkflowEntryInterface::ACTIVATED === $currentState || WorkflowEntryInterface::SUSPENDED === $currentState) { |
||
961 | $result = true; |
||
962 | } |
||
963 | break; |
||
964 | } |
||
965 | default: { |
||
966 | $result = false; |
||
967 | break; |
||
968 | 19 | } |
|
969 | |||
970 | 19 | } |
|
971 | |||
972 | 19 | return $result; |
|
973 | } catch (StoreException $e) { |
||
0 ignored issues
–
show
catch (\OldTown\Workflow...($errMsg, array($e)); } does not seem to be reachable.
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed. Unreachable code is most often the result of function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last ![]() |
|||
974 | $errMsg = sprintf( |
||
975 | 'Ошибка проверки изменения состояния для инстанса #%s', |
||
976 | $id |
||
977 | ); |
||
978 | $this->getLog()->error($errMsg, [$e]); |
||
979 | } |
||
980 | |||
981 | return false; |
||
982 | } |
||
983 | |||
984 | |||
985 | /** |
||
986 | * |
||
987 | * Возвращает коллекцию объектов описывающие состояние для текущего экземпляра workflow |
||
988 | * |
||
989 | * @param integer $id id экземпляра workflow |
||
990 | * @return SplObjectStorage|StepInterface[] |
||
991 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
992 | */ |
||
993 | View Code Duplication | public function getCurrentSteps($id) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
994 | { |
||
995 | try { |
||
996 | $store = $this->getPersistence(); |
||
997 | |||
998 | return $store->findCurrentSteps($id); |
||
999 | } catch (StoreException $e) { |
||
1000 | $errMsg = sprintf( |
||
1001 | 'Ошибка при проверке текущего шага для инстанса # %s', |
||
1002 | $id |
||
1003 | ); |
||
1004 | $this->getLog()->error($errMsg, [$e]); |
||
1005 | |||
1006 | |||
1007 | return []; |
||
0 ignored issues
–
show
The return type of
return array(); (array ) is incompatible with the return type declared by the interface OldTown\Workflow\Workflo...erface::getCurrentSteps of type SplObjectStorage|OldTown...low\Spi\StepInterface[] .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
1008 | } |
||
1009 | } |
||
1010 | |||
1011 | /** |
||
1012 | * |
||
1013 | * |
||
1014 | * Modify the state of the specified workflow instance. |
||
1015 | * @param integer $id The workflow instance id. |
||
1016 | * @param integer $newState the new state to change the workflow instance to. |
||
1017 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1018 | * @throws \OldTown\Workflow\Exception\StoreException |
||
1019 | * @throws \OldTown\Workflow\Exception\InvalidEntryStateException |
||
1020 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1021 | */ |
||
1022 | public function changeEntryState($id, $newState) |
||
1023 | { |
||
1024 | $store = $this->getPersistence(); |
||
1025 | $entry = $store->findEntry($id); |
||
1026 | |||
1027 | |||
1028 | if ($newState === $entry->getState()) { |
||
1029 | return; |
||
1030 | } |
||
1031 | |||
1032 | if ($this->canModifyEntryState($id, $newState)) { |
||
1033 | if (WorkflowEntryInterface::KILLED === $newState || WorkflowEntryInterface::COMPLETED === $newState) { |
||
1034 | $currentSteps = $this->getCurrentSteps($id); |
||
1035 | |||
1036 | if (count($currentSteps) > 0) { |
||
1037 | $this->completeEntry(null, $id, $currentSteps, $newState); |
||
1038 | } |
||
1039 | } |
||
1040 | |||
1041 | $store->setEntryState($id, $newState); |
||
1042 | } else { |
||
1043 | $errMsg = sprintf( |
||
1044 | 'Не возможен переход в экземпляре workflow #%s. Текущее состояние %s, ожидаемое состояние %s', |
||
1045 | $id, |
||
1046 | $entry->getState(), |
||
1047 | $newState |
||
1048 | ); |
||
1049 | |||
1050 | throw new InvalidEntryStateException($errMsg); |
||
1051 | } |
||
1052 | |||
1053 | $msg = sprintf( |
||
1054 | '%s : Новое состояние: %s', |
||
1055 | $entry->getId(), |
||
1056 | $entry->getState() |
||
1057 | ); |
||
1058 | $this->getLog()->debug($msg); |
||
1059 | } |
||
1060 | |||
1061 | |||
1062 | /** |
||
1063 | * @param FunctionDescriptor $function |
||
1064 | * @param TransientVarsInterface $transientVars |
||
1065 | * @param PropertySetInterface $ps |
||
1066 | * |
||
1067 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1068 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1069 | */ |
||
1070 | protected function executeFunction(FunctionDescriptor $function, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
||
1071 | { |
||
1072 | if (null !== $function) { |
||
1073 | $type = $function->getType(); |
||
1074 | |||
1075 | $argsOriginal = $function->getArgs(); |
||
1076 | $args = []; |
||
1077 | |||
1078 | View Code Duplication | foreach ($argsOriginal as $k => $v) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
1079 | $translateValue = $this->getConfiguration()->getVariableResolver()->translateVariables($v, $transientVars, $ps); |
||
1080 | $args[$k] = $translateValue; |
||
1081 | } |
||
1082 | |||
1083 | $provider = $this->getResolver()->getFunction($type, $args); |
||
1084 | |||
1085 | if (null === $provider) { |
||
1086 | $this->context->setRollbackOnly(); |
||
1087 | $errMsg = 'Не загружен провайдер для функции'; |
||
1088 | throw new WorkflowException($errMsg); |
||
1089 | } |
||
1090 | |||
1091 | try { |
||
1092 | $provider->execute($transientVars, $args, $ps); |
||
1093 | } catch (WorkflowException $e) { |
||
1094 | $this->context->setRollbackOnly(); |
||
1095 | throw $e; |
||
1096 | } |
||
1097 | } |
||
1098 | } |
||
1099 | |||
1100 | |||
1101 | /** |
||
1102 | * @param WorkflowEntryInterface $entry |
||
1103 | * @param $validatorsStorage |
||
1104 | * @param TransientVarsInterface $transientVars |
||
1105 | * @param PropertySetInterface $ps |
||
1106 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1107 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1108 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1109 | * @throws \OldTown\Workflow\Exception\InvalidInputException |
||
1110 | */ |
||
1111 | protected function verifyInputs(WorkflowEntryInterface $entry, $validatorsStorage, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
||
1112 | { |
||
1113 | View Code Duplication | if ($validatorsStorage instanceof Traversable) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
1114 | $validators = []; |
||
1115 | foreach ($validatorsStorage as $k => $v) { |
||
1116 | $validators[$k] = $v; |
||
1117 | } |
||
1118 | } elseif (is_array($validatorsStorage)) { |
||
1119 | $validators = $validatorsStorage; |
||
1120 | } else { |
||
1121 | $errMsg = sprintf( |
||
1122 | 'Validators должен быть массивом, либо реализовывать интерфейс Traversable. EntryId: %s', |
||
1123 | $entry->getId() |
||
1124 | ); |
||
1125 | throw new InvalidArgumentException($errMsg); |
||
1126 | } |
||
1127 | |||
1128 | /** @var ValidatorDescriptor[] $validators */ |
||
1129 | foreach ($validators as $input) { |
||
1130 | if (null !== $input) { |
||
1131 | $type = $input->getType(); |
||
1132 | $argsOriginal = $input->getArgs(); |
||
1133 | |||
1134 | $args = []; |
||
1135 | |||
1136 | View Code Duplication | foreach ($argsOriginal as $k => $v) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
1137 | $translateValue = $this->getConfiguration()->getVariableResolver()->translateVariables($v, $transientVars, $ps); |
||
1138 | $args[$k] = $translateValue; |
||
1139 | } |
||
1140 | |||
1141 | |||
1142 | $validator = $this->getResolver()->getValidator($type, $args); |
||
1143 | |||
1144 | if (null === $validator) { |
||
1145 | $this->context->setRollbackOnly(); |
||
1146 | $errMsg = 'Ошибка при загрузке валидатора'; |
||
1147 | throw new WorkflowException($errMsg); |
||
1148 | } |
||
1149 | |||
1150 | try { |
||
1151 | $validator->validate($transientVars, $args, $ps); |
||
1152 | } catch (InvalidInputException $e) { |
||
1153 | throw $e; |
||
1154 | } catch (\Exception $e) { |
||
1155 | $this->context->setRollbackOnly(); |
||
1156 | |||
1157 | if ($e instanceof WorkflowException) { |
||
1158 | throw $e; |
||
1159 | } |
||
1160 | |||
1161 | throw new WorkflowException($e->getMessage(), $e->getCode(), $e); |
||
1162 | } |
||
1163 | } |
||
1164 | } |
||
1165 | } |
||
1166 | |||
1167 | |||
1168 | /** |
||
1169 | * Возвращает текущий шаг |
||
1170 | * |
||
1171 | * @param WorkflowDescriptor $wfDesc |
||
1172 | * @param integer $actionId |
||
1173 | * @param StepInterface[]|SplObjectStorage $currentSteps |
||
1174 | * @param TransientVarsInterface $transientVars |
||
1175 | * @param PropertySetInterface $ps |
||
1176 | * |
||
1177 | * @return StepInterface |
||
1178 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1179 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
1180 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1181 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1182 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
1183 | */ |
||
1184 | protected function getCurrentStep(WorkflowDescriptor $wfDesc, $actionId, SplObjectStorage $currentSteps, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
||
1185 | { |
||
1186 | if (1 === $currentSteps->count()) { |
||
1187 | $currentSteps->rewind(); |
||
1188 | return $currentSteps->current(); |
||
1189 | } |
||
1190 | |||
1191 | |||
1192 | foreach ($currentSteps as $step) { |
||
1193 | $stepId = $step->getId(); |
||
1194 | $action = $wfDesc->getStep($stepId)->getAction($actionId); |
||
1195 | |||
1196 | if ($this->isActionAvailable($action, $transientVars, $ps, $stepId)) { |
||
1197 | return $step; |
||
1198 | } |
||
1199 | } |
||
1200 | |||
1201 | return null; |
||
1202 | } |
||
1203 | |||
1204 | /** |
||
1205 | * @param ActionDescriptor|null $action |
||
1206 | * @param TransientVarsInterface $transientVars |
||
1207 | * @param PropertySetInterface $ps |
||
1208 | * @param $stepId |
||
1209 | * |
||
1210 | * @return boolean |
||
1211 | * |
||
1212 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1213 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1214 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1215 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
1216 | 19 | */ |
|
1217 | protected function isActionAvailable(ActionDescriptor $action = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $stepId) |
||
1218 | 19 | { |
|
1219 | if (null === $action) { |
||
1220 | return false; |
||
1221 | } |
||
1222 | |||
1223 | $result = null; |
||
1224 | $actionHash = spl_object_hash($action); |
||
1225 | |||
1226 | $result = array_key_exists($actionHash, $this->stateCache) ? $this->stateCache[$actionHash] : $result; |
||
1227 | |||
1228 | $wf = $this->getWorkflowDescriptorForAction($action); |
||
1229 | |||
1230 | |||
1231 | if (null === $result) { |
||
1232 | $restriction = $action->getRestriction(); |
||
1233 | $conditions = null; |
||
1234 | |||
1235 | if (null !== $restriction) { |
||
1236 | $conditions = $restriction->getConditionsDescriptor(); |
||
1237 | 19 | } |
|
1238 | |||
1239 | 19 | $result = $this->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, $stepId) |
|
1240 | && $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, $stepId); |
||
1241 | |||
1242 | $this->stateCache[$actionHash] = $result; |
||
1243 | } |
||
1244 | |||
1245 | |||
1246 | $result = (boolean)$result; |
||
1247 | |||
1248 | return $result; |
||
1249 | } |
||
1250 | |||
1251 | /** |
||
1252 | * По дейсвтию получаем дексрипторв workflow |
||
1253 | * |
||
1254 | * @param ActionDescriptor $action |
||
1255 | * @return WorkflowDescriptor |
||
1256 | * |
||
1257 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1258 | */ |
||
1259 | private function getWorkflowDescriptorForAction(ActionDescriptor $action) |
||
1260 | { |
||
1261 | $objWfd = $action; |
||
1262 | |||
1263 | $count = 0; |
||
1264 | while (!$objWfd instanceof WorkflowDescriptor || null === $objWfd) { |
||
1265 | $objWfd = $objWfd->getParent(); |
||
1266 | |||
1267 | $count++; |
||
1268 | if ($count > 10) { |
||
1269 | $errMsg = 'Ошибка при получение WorkflowDescriptor'; |
||
1270 | throw new InternalWorkflowException($errMsg); |
||
1271 | } |
||
1272 | } |
||
1273 | |||
1274 | return $objWfd; |
||
1275 | } |
||
1276 | |||
1277 | |||
1278 | /** |
||
1279 | * Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
||
1280 | * |
||
1281 | * @param string $workflowName имя workflow |
||
1282 | * @param integer $initialAction id начального состояния |
||
1283 | * @param TransientVarsInterface $inputs |
||
1284 | * |
||
1285 | * @return bool |
||
1286 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1287 | * @throws \OldTown\Workflow\Exception\FactoryException |
||
1288 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1289 | * @throws \OldTown\Workflow\Exception\StoreException |
||
1290 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
1291 | */ |
||
1292 | public function canInitialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
||
1293 | { |
||
1294 | $mockWorkflowName = $workflowName; |
||
1295 | $mockEntry = new SimpleWorkflowEntry(0, $mockWorkflowName, WorkflowEntryInterface::CREATED); |
||
1296 | |||
1297 | try { |
||
1298 | $ps = PropertySetManager::getInstance('memory', null); |
||
0 ignored issues
–
show
null is of type null , but the function expects a array .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
1299 | } catch (\Exception $e) { |
||
1300 | $errMsg = sprintf('Ошибка при создание PropertySer: %s', $e->getMessage()); |
||
1301 | throw new InternalWorkflowException($errMsg); |
||
1302 | } |
||
1303 | |||
1304 | if (null === $inputs) { |
||
1305 | $inputs = $this->transientVarsFactory(); |
||
1306 | } |
||
1307 | $transientVars = $inputs; |
||
1308 | |||
1309 | try { |
||
1310 | $this->populateTransientMap($mockEntry, $transientVars, [], $initialAction, [], $ps); |
||
0 ignored issues
–
show
It seems like
$ps defined by \OldTown\PropertySet\Pro...nstance('memory', null) on line 1298 can be null ; however, OldTown\Workflow\Abstrac...:populateTransientMap() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
1311 | |||
1312 | $result = $this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps); |
||
0 ignored issues
–
show
It seems like
$ps defined by \OldTown\PropertySet\Pro...nstance('memory', null) on line 1298 can be null ; however, OldTown\Workflow\Abstrac...canInitializeInternal() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
1313 | |||
1314 | return $result; |
||
1315 | } catch (InvalidActionException $e) { |
||
1316 | $this->getLog()->error($e->getMessage(), [$e]); |
||
1317 | |||
1318 | return false; |
||
1319 | } catch (WorkflowException $e) { |
||
1320 | $errMsg = sprintf( |
||
1321 | 'Ошибка при проверки canInitialize: %s', |
||
1322 | $e->getMessage() |
||
1323 | ); |
||
1324 | $this->getLog()->error($errMsg, [$e]); |
||
1325 | |||
1326 | return false; |
||
1327 | } |
||
1328 | } |
||
1329 | |||
1330 | |||
1331 | /** |
||
1332 | * Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
||
1333 | * |
||
1334 | * @param string $workflowName имя workflow |
||
1335 | * @param integer $initialAction id начального состояния |
||
1336 | * @param TransientVarsInterface $transientVars |
||
1337 | * |
||
1338 | * @param PropertySetInterface $ps |
||
1339 | * |
||
1340 | * @return bool |
||
1341 | * |
||
1342 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1343 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
1344 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
1345 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1346 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1347 | */ |
||
1348 | protected function canInitializeInternal($workflowName, $initialAction, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
||
1349 | { |
||
1350 | $wf = $this->getConfiguration()->getWorkflow($workflowName); |
||
1351 | |||
1352 | $actionDescriptor = $wf->getInitialAction($initialAction); |
||
1353 | |||
1354 | if (null === $actionDescriptor) { |
||
1355 | $errMsg = sprintf( |
||
1356 | 'Некорректное инициирующие действие # %s', |
||
1357 | $initialAction |
||
1358 | ); |
||
1359 | throw new InvalidActionException($errMsg); |
||
1360 | } |
||
1361 | |||
1362 | $restriction = $actionDescriptor->getRestriction(); |
||
1363 | |||
1364 | |||
1365 | $conditions = null; |
||
1366 | if (null !== $restriction) { |
||
1367 | $conditions = $restriction->getConditionsDescriptor(); |
||
1368 | } |
||
1369 | |||
1370 | $passesConditions = $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0); |
||
1371 | |||
1372 | return $passesConditions; |
||
1373 | } |
||
1374 | |||
1375 | /** |
||
1376 | * Возвращает резолвер |
||
1377 | * |
||
1378 | * @return TypeResolverInterface |
||
1379 | */ |
||
1380 | public function getResolver() |
||
1381 | { |
||
1382 | if (null !== $this->typeResolver) { |
||
1383 | return $this->typeResolver; |
||
1384 | } |
||
1385 | |||
1386 | $classResolver = $this->getDefaultTypeResolverClass(); |
||
1387 | $r = new ReflectionClass($classResolver); |
||
1388 | $resolver = $r->newInstance(); |
||
1389 | $this->typeResolver = $resolver; |
||
1390 | |||
1391 | return $this->typeResolver; |
||
1392 | } |
||
1393 | |||
1394 | /** |
||
1395 | * Возвращает хранилище состояния workflow |
||
1396 | * |
||
1397 | * @return WorkflowStoreInterface |
||
1398 | * @throws \OldTown\Workflow\Exception\StoreException |
||
1399 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1400 | */ |
||
1401 | protected function getPersistence() |
||
1402 | { |
||
1403 | return $this->getConfiguration()->getWorkflowStore(); |
||
1404 | } |
||
1405 | |||
1406 | /** |
||
1407 | * Получить конфигурацию workflow. Метод также проверяет была ли иницилазированн конфигурация, если нет, то |
||
1408 | * инициализирует ее. |
||
1409 | * |
||
1410 | * Если конфигурация не была установленна, то возвращает конфигурацию по умолчанию |
||
1411 | * |
||
1412 | * @return ConfigurationInterface|DefaultConfiguration Конфигурация которая была установленна |
||
1413 | * |
||
1414 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1415 | */ |
||
1416 | public function getConfiguration() |
||
1417 | { |
||
1418 | $config = null !== $this->configuration ? $this->configuration : DefaultConfiguration::getInstance(); |
||
1419 | |||
1420 | if (!$config->isInitialized()) { |
||
1421 | try { |
||
1422 | $config->load(null); |
||
1423 | } catch (FactoryException $e) { |
||
1424 | $errMsg = 'Ошибка при иницилазации конфигурации workflow'; |
||
1425 | $this->getLog()->critical($errMsg, ['exception' => $e]); |
||
1426 | throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
||
1427 | } |
||
1428 | } |
||
1429 | |||
1430 | return $config; |
||
1431 | } |
||
1432 | |||
1433 | /** |
||
1434 | * @return LoggerInterface |
||
1435 | */ |
||
1436 | public function getLog() |
||
1437 | { |
||
1438 | return $this->log; |
||
1439 | } |
||
1440 | |||
1441 | /** |
||
1442 | * @param LoggerInterface $log |
||
1443 | * |
||
1444 | * @return $this |
||
1445 | * @throws InternalWorkflowException |
||
1446 | */ |
||
1447 | public function setLog($log) |
||
1448 | { |
||
1449 | try { |
||
1450 | LogFactory::validLogger($log); |
||
1451 | } catch (\Exception $e) { |
||
1452 | $errMsg = 'Ошибка при валидации логера'; |
||
1453 | throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
||
1454 | } |
||
1455 | |||
1456 | |||
1457 | $this->log = $log; |
||
1458 | |||
1459 | return $this; |
||
1460 | } |
||
1461 | |||
1462 | |||
1463 | /** |
||
1464 | * Get the workflow descriptor for the specified workflow name. |
||
1465 | * |
||
1466 | * @param string $workflowName The workflow name. |
||
1467 | * @return WorkflowDescriptor |
||
1468 | * @throws InternalWorkflowException |
||
1469 | */ |
||
1470 | public function getWorkflowDescriptor($workflowName) |
||
1471 | { |
||
1472 | try { |
||
1473 | return $this->getConfiguration()->getWorkflow($workflowName); |
||
1474 | } catch (FactoryException $e) { |
||
1475 | $errMsg = 'Ошибка при загрузке workflow'; |
||
1476 | $this->getLog()->error($errMsg, ['exception' => $e]); |
||
1477 | throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
||
1478 | } |
||
1479 | } |
||
1480 | |||
1481 | |||
1482 | /** |
||
1483 | * Executes a special trigger-function using the context of the given workflow instance id. |
||
1484 | * Note that this method is exposed for Quartz trigger jobs, user code should never call it. |
||
1485 | * @param integer $id The workflow instance id |
||
1486 | * @param integer $triggerId The id of the special trigger-function |
||
1487 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1488 | * @throws \OldTown\Workflow\Exception\StoreException |
||
1489 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1490 | * @throws \OldTown\Workflow\Exception\FactoryException |
||
1491 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1492 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
1493 | */ |
||
1494 | public function executeTriggerFunction($id, $triggerId) |
||
1495 | { |
||
1496 | $store = $this->getPersistence(); |
||
1497 | $entry = $store->findEntry($id); |
||
1498 | |||
1499 | if (null === $entry) { |
||
1500 | $errMsg = sprintf( |
||
1501 | 'Ошибка при выполнение тригера # %s для несуществующего экземпляра workflow id# %s', |
||
1502 | $triggerId, |
||
1503 | $id |
||
1504 | ); |
||
1505 | $this->getLog()->warning($errMsg); |
||
1506 | return; |
||
1507 | } |
||
1508 | |||
1509 | $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
||
1510 | |||
1511 | $ps = $store->getPropertySet($id); |
||
1512 | $transientVars = $this->transientVarsFactory(); |
||
1513 | |||
1514 | $this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $store->findCurrentSteps($id), $ps); |
||
1515 | |||
1516 | $this->executeFunction($wf->getTriggerFunction($triggerId), $transientVars, $ps); |
||
1517 | } |
||
1518 | |||
1519 | /** |
||
1520 | * @param $id |
||
1521 | * @param $inputs |
||
1522 | * |
||
1523 | * @return array |
||
1524 | * @throws \OldTown\Workflow\Exception\StoreException |
||
1525 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1526 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1527 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1528 | * @throws \OldTown\Workflow\Exception\FactoryException |
||
1529 | */ |
||
1530 | public function getAvailableActions($id, TransientVarsInterface $inputs = null) |
||
1531 | { |
||
1532 | try { |
||
1533 | $store = $this->getPersistence(); |
||
1534 | $entry = $store->findEntry($id); |
||
1535 | |||
1536 | if (null === $entry) { |
||
1537 | $errMsg = sprintf( |
||
1538 | 'Не существует экземпляра workflow c id %s', |
||
1539 | $id |
||
1540 | ); |
||
1541 | throw new InvalidArgumentException($errMsg); |
||
1542 | } |
||
1543 | |||
1544 | if (WorkflowEntryInterface::ACTIVATED === $entry->getState()) { |
||
1545 | return []; |
||
1546 | } |
||
1547 | |||
1548 | $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
||
1549 | |||
1550 | View Code Duplication | if (null === $wf) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
1551 | $errMsg = sprintf( |
||
1552 | 'Не существует workflow c именем %s', |
||
1553 | $entry->getWorkflowName() |
||
1554 | ); |
||
1555 | throw new InvalidArgumentException($errMsg); |
||
1556 | } |
||
1557 | |||
1558 | $l = []; |
||
1559 | $ps = $store->getPropertySet($id); |
||
1560 | |||
1561 | $transientVars = $inputs; |
||
1562 | if (null === $transientVars) { |
||
1563 | $transientVars = $this->transientVarsFactory(); |
||
1564 | } |
||
1565 | |||
1566 | $currentSteps = $store->findCurrentSteps($id); |
||
1567 | |||
1568 | $this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), 0, $currentSteps, $ps); |
||
1569 | |||
1570 | $globalActions = $wf->getGlobalActions(); |
||
1571 | |||
1572 | foreach ($globalActions as $action) { |
||
1573 | $restriction = $action->getRestriction(); |
||
1574 | $conditions = null; |
||
1575 | |||
1576 | $transientVars['actionId'] = $action->getId(); |
||
1577 | |||
1578 | if (null !== $restriction) { |
||
1579 | $conditions = $restriction->getConditionsDescriptor(); |
||
1580 | } |
||
1581 | |||
1582 | $flag = $this->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, 0) && $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0); |
||
1583 | if ($flag) { |
||
1584 | $l[] = $action->getId(); |
||
1585 | } |
||
1586 | } |
||
1587 | |||
1588 | |||
1589 | View Code Duplication | foreach ($currentSteps as $step) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
1590 | $data = $this->getAvailableActionsForStep($wf, $step, $transientVars, $ps); |
||
1591 | foreach ($data as $v) { |
||
1592 | $l[] = $v; |
||
1593 | } |
||
1594 | } |
||
1595 | return array_unique($l); |
||
1596 | } catch (\Exception $e) { |
||
1597 | $errMsg = 'Ошибка проверки доступных действий'; |
||
1598 | $this->getLog()->error($errMsg, [$e]); |
||
1599 | } |
||
1600 | |||
1601 | return []; |
||
1602 | } |
||
1603 | |||
1604 | /** |
||
1605 | * @param WorkflowDescriptor $wf |
||
1606 | * @param StepInterface $step |
||
1607 | * @param TransientVarsInterface $transientVars |
||
1608 | * @param PropertySetInterface $ps |
||
1609 | * |
||
1610 | * @return array |
||
1611 | * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
||
1612 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1613 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1614 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1615 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
1616 | */ |
||
1617 | protected function getAvailableActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
||
1618 | { |
||
1619 | $l = []; |
||
1620 | $s = $wf->getStep($step->getStepId()); |
||
1621 | |||
1622 | if (null === $s) { |
||
1623 | $errMsg = sprintf( |
||
1624 | 'getAvailableActionsForStep вызван с не существующим id шага %s', |
||
1625 | $step->getStepId() |
||
1626 | ); |
||
1627 | |||
1628 | $this->getLog()->warning($errMsg); |
||
1629 | |||
1630 | return $l; |
||
1631 | } |
||
1632 | |||
1633 | $actions = $s->getActions(); |
||
1634 | |||
1635 | if (null === $actions || 0 === $actions->count()) { |
||
1636 | return $l; |
||
1637 | } |
||
1638 | |||
1639 | foreach ($actions as $action) { |
||
1640 | $restriction = $action->getRestriction(); |
||
1641 | $conditions = null; |
||
1642 | |||
1643 | $transientVars['actionId'] = $action->getId(); |
||
1644 | |||
1645 | |||
1646 | if (null !== $restriction) { |
||
1647 | $conditions = $restriction->getConditionsDescriptor(); |
||
1648 | } |
||
1649 | |||
1650 | $f = $this->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, $s->getId()) |
||
1651 | && $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, $s->getId()); |
||
1652 | if ($f) { |
||
1653 | $l[] = $action->getId(); |
||
1654 | } |
||
1655 | } |
||
1656 | |||
1657 | return $l; |
||
1658 | } |
||
1659 | |||
1660 | /** |
||
1661 | * @param ConfigurationInterface $configuration |
||
1662 | * |
||
1663 | * @return $this |
||
1664 | */ |
||
1665 | public function setConfiguration(ConfigurationInterface $configuration) |
||
1666 | { |
||
1667 | $this->configuration = $configuration; |
||
1668 | |||
1669 | return $this; |
||
1670 | } |
||
1671 | |||
1672 | /** |
||
1673 | * Возвращает состояние для текущего экземпляра workflow |
||
1674 | * |
||
1675 | * @param integer $id id экземпляра workflow |
||
1676 | * @return integer id текущего состояния |
||
1677 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1678 | */ |
||
1679 | public function getEntryState($id) |
||
1680 | { |
||
1681 | try { |
||
1682 | $store = $this->getPersistence(); |
||
1683 | |||
1684 | return $store->findEntry($id)->getState(); |
||
1685 | } catch (StoreException $e) { |
||
1686 | $errMsg = sprintf( |
||
1687 | 'Ошибка при получение состояния экземпляра workflow c id# %s', |
||
1688 | $id |
||
1689 | ); |
||
1690 | $this->getLog()->error($errMsg, [$e]); |
||
1691 | } |
||
1692 | |||
1693 | return WorkflowEntryInterface::UNKNOWN; |
||
1694 | } |
||
1695 | |||
1696 | /** |
||
1697 | * Returns a list of all steps that are completed for the given workflow instance id. |
||
1698 | * |
||
1699 | * @param integer $id The workflow instance id. |
||
1700 | * @return StepInterface[] a List of Steps |
||
1701 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1702 | */ |
||
1703 | View Code Duplication | public function getHistorySteps($id) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
1704 | { |
||
1705 | try { |
||
1706 | $store = $this->getPersistence(); |
||
1707 | |||
1708 | return $store->findHistorySteps($id); |
||
0 ignored issues
–
show
The expression
$store->findHistorySteps($id); of type OldTown\Workflow\Spi\Ste...face[]|SplObjectStorage adds the type SplObjectStorage to the return on line 1708 which is incompatible with the return type declared by the interface OldTown\Workflow\Workflo...erface::getHistorySteps of type OldTown\Workflow\Spi\StepInterface[] .
![]() |
|||
1709 | } catch (StoreException $e) { |
||
1710 | $errMsg = sprintf( |
||
1711 | 'Ошибка при получение истории шагов для экземпляра workflow c id# %s', |
||
1712 | $id |
||
1713 | ); |
||
1714 | $this->getLog()->error($errMsg, [$e]); |
||
1715 | } |
||
1716 | |||
1717 | return []; |
||
1718 | } |
||
1719 | |||
1720 | /** |
||
1721 | * Настройки хранилища |
||
1722 | * |
||
1723 | * @return array |
||
1724 | * |
||
1725 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1726 | */ |
||
1727 | public function getPersistenceProperties() |
||
1728 | { |
||
1729 | return $this->getConfiguration()->getPersistenceArgs(); |
||
1730 | } |
||
1731 | |||
1732 | |||
1733 | /** |
||
1734 | * Get the PropertySet for the specified workflow instance id. |
||
1735 | * @param integer $id The workflow instance id. |
||
1736 | * @return PropertySetInterface |
||
1737 | * |
||
1738 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1739 | */ |
||
1740 | View Code Duplication | public function getPropertySet($id) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
1741 | { |
||
1742 | $ps = null; |
||
1743 | |||
1744 | try { |
||
1745 | $ps = $this->getPersistence()->getPropertySet($id); |
||
1746 | } catch (StoreException $e) { |
||
1747 | $errMsg = sprintf( |
||
1748 | 'Ошибка при получение PropertySet для экземпляра workflow c id# %s', |
||
1749 | $id |
||
1750 | ); |
||
1751 | $this->getLog()->error($errMsg, [$e]); |
||
1752 | } |
||
1753 | |||
1754 | return $ps; |
||
1755 | } |
||
1756 | |||
1757 | /** |
||
1758 | * @return \String[] |
||
1759 | * |
||
1760 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1761 | */ |
||
1762 | public function getWorkflowNames() |
||
1763 | { |
||
1764 | try { |
||
1765 | return $this->getConfiguration()->getWorkflowNames(); |
||
0 ignored issues
–
show
The return type of
return $this->getConfigu...()->getWorkflowNames(); (string[] ) is incompatible with the return type documented by OldTown\Workflow\Abstrac...kflow::getWorkflowNames of type String[] .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
1766 | } catch (FactoryException $e) { |
||
1767 | $errMsg = 'Ошибка при получение имен workflow'; |
||
1768 | $this->getLog()->error($errMsg, [$e]); |
||
1769 | } |
||
1770 | |||
1771 | return []; |
||
1772 | } |
||
1773 | |||
1774 | /** |
||
1775 | * @param TypeResolverInterface $typeResolver |
||
1776 | * |
||
1777 | * @return $this |
||
1778 | */ |
||
1779 | public function setTypeResolver(TypeResolverInterface $typeResolver) |
||
1780 | { |
||
1781 | $this->typeResolver = $typeResolver; |
||
1782 | |||
1783 | return $this; |
||
1784 | } |
||
1785 | |||
1786 | |||
1787 | /** |
||
1788 | * Get a collection (Strings) of currently defined permissions for the specified workflow instance. |
||
1789 | * @param integer $id id the workflow instance id. |
||
1790 | * @param TransientVarsInterface $inputs inputs The inputs to the workflow instance. |
||
1791 | * @return array A List of permissions specified currently (a permission is a string name). |
||
1792 | * |
||
1793 | */ |
||
1794 | public function getSecurityPermissions($id, TransientVarsInterface $inputs = null) |
||
1795 | { |
||
1796 | try { |
||
1797 | $store = $this->getPersistence(); |
||
1798 | $entry = $store->findEntry($id); |
||
1799 | $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
||
1800 | |||
1801 | $ps = $store->getPropertySet($id); |
||
1802 | |||
1803 | if (null === $inputs) { |
||
1804 | $inputs = $this->transientVarsFactory(); |
||
1805 | } |
||
1806 | $transientVars = $inputs; |
||
1807 | |||
1808 | $currentSteps = $store->findCurrentSteps($id); |
||
1809 | |||
1810 | try { |
||
1811 | $this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $currentSteps, $ps); |
||
1812 | } catch (\Exception $e) { |
||
1813 | $errMsg = sprintf( |
||
1814 | 'Внутреннея ошибка: %s', |
||
1815 | $e->getMessage() |
||
1816 | ); |
||
1817 | throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
||
1818 | } |
||
1819 | |||
1820 | |||
1821 | $s = []; |
||
1822 | |||
1823 | foreach ($currentSteps as $step) { |
||
1824 | $stepId = $step->getStepId(); |
||
1825 | |||
1826 | $xmlStep = $wf->getStep($stepId); |
||
1827 | |||
1828 | $securities = $xmlStep->getPermissions(); |
||
1829 | |||
1830 | foreach ($securities as $security) { |
||
1831 | $conditionsDescriptor = $security->getRestriction()->getConditionsDescriptor(); |
||
1832 | if (null !== $security->getRestriction() && $this->passesConditionsByDescriptor($conditionsDescriptor, $transientVars, $ps, $xmlStep->getId())) { |
||
1833 | $s[$security->getName()] = $security->getName(); |
||
1834 | } |
||
1835 | } |
||
1836 | } |
||
1837 | |||
1838 | return $s; |
||
1839 | } catch (\Exception $e) { |
||
1840 | $errMsg = sprintf( |
||
1841 | 'Ошибка при получение информации о правах доступа для экземпляра workflow c id# %s', |
||
1842 | $id |
||
1843 | ); |
||
1844 | $this->getLog()->error($errMsg, [$e]); |
||
1845 | } |
||
1846 | |||
1847 | return []; |
||
1848 | } |
||
1849 | |||
1850 | |||
1851 | /** |
||
1852 | * Get the name of the specified workflow instance. |
||
1853 | * |
||
1854 | * @param integer $id the workflow instance id. |
||
1855 | * @return string |
||
1856 | * |
||
1857 | * @throws \OldTown\Workflow\Exception\StoreException |
||
1858 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1859 | */ |
||
1860 | public function getWorkflowName($id) |
||
1861 | { |
||
1862 | try { |
||
1863 | $store = $this->getPersistence(); |
||
1864 | $entry = $store->findEntry($id); |
||
1865 | |||
1866 | if (null !== $entry) { |
||
1867 | return $entry->getWorkflowName(); |
||
1868 | } |
||
1869 | } catch (FactoryException $e) { |
||
1870 | $errMsg = sprintf( |
||
1871 | 'Ошибка при получение имен workflow для инстанса с id # %s', |
||
1872 | $id |
||
1873 | ); |
||
1874 | $this->getLog()->error($errMsg, [$e]); |
||
1875 | } |
||
1876 | |||
1877 | return null; |
||
1878 | } |
||
1879 | |||
1880 | /** |
||
1881 | * Удаляет workflow |
||
1882 | * |
||
1883 | * @param string $workflowName |
||
1884 | * |
||
1885 | * @return bool |
||
1886 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1887 | */ |
||
1888 | public function removeWorkflowDescriptor($workflowName) |
||
1889 | { |
||
1890 | return $this->getConfiguration()->removeWorkflow($workflowName); |
||
1891 | } |
||
1892 | |||
1893 | /** |
||
1894 | * @param $workflowName |
||
1895 | * @param WorkflowDescriptor $descriptor |
||
1896 | * @param $replace |
||
1897 | * |
||
1898 | * @return bool |
||
1899 | * |
||
1900 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1901 | */ |
||
1902 | public function saveWorkflowDescriptor($workflowName, WorkflowDescriptor $descriptor, $replace) |
||
1903 | { |
||
1904 | $success = $this->getConfiguration()->saveWorkflow($workflowName, $descriptor, $replace); |
||
1905 | |||
1906 | return $success; |
||
1907 | } |
||
1908 | |||
1909 | |||
1910 | /** |
||
1911 | * Query the workflow store for matching instances |
||
1912 | * |
||
1913 | * @param WorkflowExpressionQuery $query |
||
1914 | * |
||
1915 | * @return array |
||
1916 | * |
||
1917 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1918 | * @throws \OldTown\Workflow\Exception\StoreException |
||
1919 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1920 | |||
1921 | */ |
||
1922 | public function query(WorkflowExpressionQuery $query) |
||
1923 | { |
||
1924 | return $this->getPersistence()->query($query); |
||
1925 | } |
||
1926 | |||
1927 | /** |
||
1928 | * @return string |
||
1929 | */ |
||
1930 | public function getDefaultTypeResolverClass() |
||
1931 | { |
||
1932 | return $this->defaultTypeResolverClass; |
||
1933 | } |
||
1934 | |||
1935 | /** |
||
1936 | * @param string $defaultTypeResolverClass |
||
1937 | * |
||
1938 | * @return $this |
||
1939 | */ |
||
1940 | public function setDefaultTypeResolverClass($defaultTypeResolverClass) |
||
1941 | { |
||
1942 | $this->defaultTypeResolverClass = (string)$defaultTypeResolverClass; |
||
1943 | |||
1944 | return $this; |
||
1945 | } |
||
1946 | |||
1947 | |||
1948 | /** |
||
1949 | * @param ConditionsDescriptor $descriptor |
||
1950 | * @param TransientVarsInterface $transientVars |
||
1951 | * @param PropertySetInterface $ps |
||
1952 | * @param $currentStepId |
||
1953 | * |
||
1954 | * @return bool |
||
1955 | * |
||
1956 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1957 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1958 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1959 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
1960 | */ |
||
1961 | protected function passesConditionsByDescriptor(ConditionsDescriptor $descriptor = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $currentStepId) |
||
1962 | { |
||
1963 | if (null === $descriptor) { |
||
1964 | return true; |
||
1965 | } |
||
1966 | |||
1967 | $type = $descriptor->getType(); |
||
1968 | $conditions = $descriptor->getConditions(); |
||
1969 | $passesConditions = $this->passesConditionsWithType($type, $conditions, $transientVars, $ps, $currentStepId); |
||
0 ignored issues
–
show
It seems like
$conditions defined by $descriptor->getConditions() on line 1968 can also be of type array<integer,object<Old...\ConditionsDescriptor>> ; however, OldTown\Workflow\Abstrac...sesConditionsWithType() does only seem to accept null|object<SplObjectStorage> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
1970 | |||
1971 | return $passesConditions; |
||
1972 | } |
||
1973 | |||
1974 | /** |
||
1975 | * @param string $conditionType |
||
1976 | * @param SplObjectStorage $conditions |
||
1977 | * @param TransientVarsInterface $transientVars |
||
1978 | * @param PropertySetInterface $ps |
||
1979 | * @param integer $currentStepId |
||
1980 | * |
||
1981 | * @return bool |
||
1982 | * @throws \OldTown\Workflow\Exception\InvalidArgumentException |
||
1983 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
1984 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
1985 | * @throws \OldTown\Workflow\Exception\InvalidActionException |
||
1986 | */ |
||
1987 | protected function passesConditionsWithType($conditionType, SplObjectStorage $conditions = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $currentStepId) |
||
1988 | { |
||
1989 | if (null === $conditions) { |
||
1990 | return true; |
||
1991 | } |
||
1992 | |||
1993 | if (0 === $conditions->count()) { |
||
1994 | return true; |
||
1995 | } |
||
1996 | |||
1997 | $and = strtoupper($conditionType) === 'AND'; |
||
1998 | $or = !$and; |
||
1999 | |||
2000 | foreach ($conditions as $descriptor) { |
||
2001 | if ($descriptor instanceof ConditionsDescriptor) { |
||
2002 | $result = $this->passesConditionsWithType($descriptor->getType(), $descriptor->getConditions(), $transientVars, $ps, $currentStepId); |
||
0 ignored issues
–
show
It seems like
$descriptor->getConditions() targeting OldTown\Workflow\Loader\...riptor::getConditions() can also be of type array<integer,object<Old...\ConditionsDescriptor>> ; however, OldTown\Workflow\Abstrac...sesConditionsWithType() does only seem to accept null|object<SplObjectStorage> , maybe add an additional type check?
This check looks at variables that are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. ![]() |
|||
2003 | } else { |
||
2004 | $result = $this->passesCondition($descriptor, $transientVars, $ps, $currentStepId); |
||
2005 | } |
||
2006 | |||
2007 | if ($and && !$result) { |
||
2008 | return false; |
||
2009 | } elseif ($or && $result) { |
||
2010 | return true; |
||
2011 | } |
||
2012 | } |
||
2013 | |||
2014 | if ($and) { |
||
2015 | return true; |
||
2016 | } |
||
2017 | |||
2018 | return false; |
||
2019 | } |
||
2020 | |||
2021 | /** |
||
2022 | * @param ConditionDescriptor $conditionDesc |
||
2023 | * @param TransientVarsInterface $transientVars |
||
2024 | * @param PropertySetInterface $ps |
||
2025 | * @param integer $currentStepId |
||
2026 | * |
||
2027 | * @return boolean |
||
2028 | * |
||
2029 | * @throws \OldTown\Workflow\Exception\InternalWorkflowException |
||
2030 | * @throws \OldTown\Workflow\Exception\WorkflowException |
||
2031 | */ |
||
2032 | protected function passesCondition(ConditionDescriptor $conditionDesc, TransientVarsInterface $transientVars, PropertySetInterface $ps, $currentStepId) |
||
2033 | { |
||
2034 | $type = $conditionDesc->getType(); |
||
2035 | |||
2036 | $argsOriginal = $conditionDesc->getArgs(); |
||
2037 | |||
2038 | |||
2039 | $args = []; |
||
2040 | View Code Duplication | foreach ($argsOriginal as $key => $value) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
2041 | $translateValue = $this->getConfiguration()->getVariableResolver()->translateVariables($value, $transientVars, $ps); |
||
2042 | $args[$key] = $translateValue; |
||
2043 | } |
||
2044 | |||
2045 | if (-1 !== $currentStepId) { |
||
2046 | $stepId = array_key_exists('stepId', $args) ? (integer)$args['stepId'] : null; |
||
2047 | |||
2048 | if (null !== $stepId && -1 === $stepId) { |
||
2049 | $args['stepId'] = $currentStepId; |
||
2050 | } |
||
2051 | } |
||
2052 | |||
2053 | $condition = $this->getResolver()->getCondition($type, $args); |
||
2054 | |||
2055 | if (null === $condition) { |
||
2056 | $this->context->setRollbackOnly(); |
||
2057 | $errMsg = 'Огибка при загрузки условия'; |
||
2058 | throw new WorkflowException($errMsg); |
||
2059 | } |
||
2060 | |||
2061 | try { |
||
2062 | $passed = $condition->passesCondition($transientVars, $args, $ps); |
||
2063 | |||
2064 | if ($conditionDesc->isNegate()) { |
||
2065 | $passed = !$passed; |
||
2066 | } |
||
2067 | } catch (\Exception $e) { |
||
2068 | $this->context->setRollbackOnly(); |
||
2069 | |||
2070 | $errMsg = sprintf( |
||
2071 | 'Ошбика при выполнение условия %s', |
||
2072 | get_class($condition) |
||
2073 | ); |
||
2074 | |||
2075 | throw new WorkflowException($errMsg, $e->getCode(), $e); |
||
2076 | } |
||
2077 | |||
2078 | return $passed; |
||
2079 | } |
||
2080 | } |
||
2081 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: