GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — dev ( faa881...07ffba )
by Андрей
06:42
created

AbstractWorkflow::getWorkflowDescriptorForAction()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.0961

Importance

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