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 ( 36e137...faa881 )
by Андрей
06:16
created

AbstractWorkflow::canModifyEntryState()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
cc 2
eloc 5
nc 2
nop 2
crap 2
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
218
        // now clone the memory PS to the real PS
219
        //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.

Loading history...
220 16
        return $entryId;
221
    }
222
223
    /**
224
     * @param WorkflowEntryInterface $entry
225
     * @param TransientVarsInterface $transientVars
226
     * @param array|Traversable|RegisterDescriptor[]|SplObjectStorage $registersStorage
227
     * @param integer $actionId
228
     * @param array|Traversable $currentSteps
229
     * @param PropertySetInterface $ps
230
     *
231
     *
232
     * @return TransientVarsInterface
233
     *
234
     * @throws InvalidArgumentException
235
     * @throws WorkflowException
236
     * @throws InternalWorkflowException
237
     */
238 19
    protected function populateTransientMap(WorkflowEntryInterface $entry, TransientVarsInterface $transientVars, $registersStorage, $actionId = null, $currentSteps, PropertySetInterface $ps)
239
    {
240 19
        $this->validateIterateData($currentSteps);
241
242 19
        $registers = $this->convertDataInArray($registersStorage);
243
244
245
        /** @var RegisterDescriptor[] $registers */
246
247 19
        $transientVars['context'] = $this->context;
248 19
        $transientVars['entry'] = $entry;
249 19
        $transientVars['entryId'] = $entry->getId();
250 19
        $transientVars['store'] = $this->getPersistence();
251 19
        $transientVars['configuration'] = $this->getConfiguration();
252 19
        $transientVars['descriptor'] = $this->getConfiguration()->getWorkflow($entry->getWorkflowName());
253
254 19
        if (null !== $actionId) {
255 19
            $transientVars['actionId'] = $actionId;
256 19
        }
257
258 19
        $transientVars['currentSteps'] = $currentSteps;
259
260
261 19
        foreach ($registers as $register) {
262 3
            $args = $register->getArgs();
263 3
            $type = $register->getType();
264
265
            try {
266 3
                $r = $this->getResolver()->getRegister($type, $args);
267 3
            } catch (\Exception $e) {
268
                $errMsg = 'Ошибка при инициализации register';
269
                $this->context->setRollbackOnly();
270
                throw new WorkflowException($errMsg, $e->getCode(), $e);
271
            }
272
273 3
            $variableName = $register->getVariableName();
274
            try {
275 3
                $value = $r->registerVariable($this->context, $entry, $args, $ps);
276
277 3
                $transientVars[$variableName] = $value;
278 3
            } catch (\Exception $e) {
279
                $this->context->setRollbackOnly();
280
281
                $errMsg = sprintf(
282
                    'При получение значения переменной %s из registry %s произошла ошибка',
283
                    $variableName,
284
                    get_class($r)
285
                );
286
287
                throw new WorkflowException($errMsg, $e->getCode(), $e);
288
            }
289 19
        }
290
291 19
        return $transientVars;
292
    }
293
294
    /**
295
     * Проверка того что данные могут быть использованы в цикле
296
     *
297
     * @param $data
298
     *
299
     * @throws InvalidArgumentException
300
     */
301 19
    protected function validateIterateData($data)
302
    {
303 19
        if (!is_array($data) && !$data  instanceof Traversable) {
304
            $errMsg = 'Data not iterate';
305
            throw new InvalidArgumentException($errMsg);
306
        }
307 19
    }
308
309
    /**
310
     * Преобразование данных в массив
311
     *
312
     * @param $data
313
     *
314
     * @return array
315
     *
316
     * @throws InvalidArgumentException
317
     */
318 19
    protected function convertDataInArray($data)
319
    {
320 19
        $result = [];
321 19
        if ($data instanceof Traversable) {
322 19
            foreach ($data as $k => $v) {
323 7
                $result[$k] = $v;
324 19
            }
325 19
        } elseif (is_array($data)) {
326
            $result = $data;
327
        } else {
328
            $errMsg = 'Data must be an array or an interface to implement Traversable';
329
            throw new InvalidArgumentException($errMsg);
330
        }
331
332 19
        return $result;
333
    }
334
335
336
    /**
337
     * Переход между двумя статусами
338
     *
339
     * @param WorkflowEntryInterface $entry
340
     * @param SplObjectStorage|StepInterface[] $currentSteps
341
     * @param WorkflowStoreInterface $store
342
     * @param WorkflowDescriptor $wf
343
     * @param ActionDescriptor $action
344
     * @param TransientVarsInterface $transientVars
345
     * @param TransientVarsInterface $inputs
346
     * @param PropertySetInterface $ps
347
     *
348
     * @return boolean
349
     *
350
     * @throws InternalWorkflowException
351
     */
352 18
    protected function transitionWorkflow(WorkflowEntryInterface $entry, SplObjectStorage $currentSteps, WorkflowStoreInterface $store, WorkflowDescriptor $wf, ActionDescriptor $action, TransientVarsInterface $transientVars, TransientVarsInterface $inputs, PropertySetInterface $ps)
353
    {
354
        try {
355 18
            $step = $this->getCurrentStep($wf, $action->getId(), $currentSteps, $transientVars, $ps);
356
357 18
            $validators = $action->getValidators();
358 18
            if ($validators->count() > 0) {
359 3
                $this->verifyInputs($validators, $transientVars, $ps);
360 2
            }
361
362
363 17
            if (null !== $step) {
364 6
                $stepPostFunctions = $wf->getStep($step->getStepId())->getPostFunctions();
365 6
                foreach ($stepPostFunctions as $function) {
366 1
                    $this->executeFunction($function, $transientVars, $ps);
367 6
                }
368 6
            }
369
370 17
            $preFunctions = $action->getPreFunctions();
371 17
            foreach ($preFunctions as $preFunction) {
372 1
                $this->executeFunction($preFunction, $transientVars, $ps);
373 17
            }
374
375 17
            $conditionalResults = $action->getConditionalResults();
376 17
            $extraPreFunctions = null;
377 17
            $extraPostFunctions = null;
378
379 17
            $theResult = null;
380
381
382 17
            $currentStepId = null !== $step ? $step->getStepId()  : -1;
383 17
            foreach ($conditionalResults as $conditionalResult) {
384 6
                if ($this->passesConditionsWithType(null, $conditionalResult->getConditions(), $transientVars, $ps, $currentStepId)) {
385 4
                    $theResult = $conditionalResult;
386
387 4
                    $validatorsStorage = $conditionalResult->getValidators();
388 4
                    if ($validatorsStorage->count() > 0) {
389 1
                        $this->verifyInputs($validatorsStorage, $transientVars, $ps);
390
                    }
391
392 3
                    $extraPreFunctions = $conditionalResult->getPreFunctions();
393 3
                    $extraPostFunctions = $conditionalResult->getPostFunctions();
394
395 3
                    break;
396
                }
397 17
            }
398
399
400 16
            if (null ===  $theResult) {
401 13
                $theResult = $action->getUnconditionalResult();
402 13
                $this->verifyInputs($theResult->getValidators(), $transientVars, $ps);
403 13
                $extraPreFunctions = $theResult->getPreFunctions();
404 13
                $extraPostFunctions = $theResult->getPostFunctions();
405 13
            }
406
407 16
            $logMsg = sprintf('theResult=%s %s', $theResult->getStep(), $theResult->getStatus());
408 16
            $this->getLog()->debug($logMsg);
409
410
411 16
            if ($extraPreFunctions && $extraPreFunctions->count() > 0) {
412 2
                foreach ($extraPreFunctions as $function) {
413 2
                    $this->executeFunction($function, $transientVars, $ps);
414 2
                }
415 2
            }
416
417 16
            $split = $theResult->getSplit();
418 16
            $join = $theResult->getJoin();
419 16
            if (null !== $split && 0 !== $split) {
420
                $splitDesc = $wf->getSplit($split);
421
                $results = $splitDesc->getResults();
422
                $splitPreFunctions = [];
423
                $splitPostFunctions = [];
424
425
                foreach ($results as $resultDescriptor) {
426
                    if ($resultDescriptor->getValidators()->count() > 0) {
427
                        $this->verifyInputs($resultDescriptor->getValidators(), $transientVars, $ps);
428
                    }
429
430
                    foreach ($resultDescriptor->getPreFunctions() as $function) {
431
                        $splitPreFunctions[] = $function;
432
                    }
433
                    foreach ($resultDescriptor->getPostFunctions() as $function) {
434
                        $splitPostFunctions[] = $function;
435
                    }
436
                }
437
438
                foreach ($splitPreFunctions as $function) {
439
                    $this->executeFunction($function, $transientVars, $ps);
440
                }
441
442
                if (!$action->isFinish()) {
443
                    $moveFirst = true;
444
445
                    //???????????????????
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.

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