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 ( c7bcbf...fead3a )
by Андрей
06:13
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 5
Bugs 0 Features 0
Metric Value
c 5
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\InvalidRoleException;
19
use OldTown\Workflow\Exception\StoreException;
20
use OldTown\Workflow\Exception\WorkflowException;
21
use OldTown\Workflow\Loader\ActionDescriptor;
22
use OldTown\Workflow\Loader\PermissionDescriptor;
23
use OldTown\Workflow\Loader\WorkflowDescriptor;
24
use OldTown\Workflow\Query\WorkflowExpressionQuery;
25
use OldTown\Workflow\Spi\SimpleWorkflowEntry;
26
use OldTown\Workflow\Spi\StepInterface;
27
use OldTown\Workflow\Spi\WorkflowEntryInterface;
28
use OldTown\Workflow\Spi\WorkflowStoreInterface;
29
use OldTown\Workflow\TransientVars\TransientVarsInterface;
30
use Psr\Log\LoggerInterface;
31
use SplObjectStorage;
32
use OldTown\Workflow\TransientVars\BaseTransientVars;
33
use ReflectionClass;
34
use ArrayObject;
35
use OldTown\Workflow\Engine\EngineManagerInterface;
36
use OldTown\Workflow\Engine\EngineManager;
37
38
39
40
/**
41
 * Class AbstractWorkflow
42
 *
43
 * @package OldTown\Workflow
44
 */
45
abstract class  AbstractWorkflow implements WorkflowInterface
46
{
47
    /**
48
     * @var string
49
     */
50
    const CURRENT_STEPS = 'currentSteps';
51
52
    /**
53
     * @var string
54
     */
55
    const HISTORY_STEPS = 'historySteps';
56
57
    /**
58
     * @var WorkflowContextInterface
59
     */
60
    protected $context;
61
62
    /**
63
     * @var ConfigurationInterface
64
     */
65
    protected $configuration;
66
67
68
    /**
69
     * @var TypeResolverInterface
70
     */
71
    protected $typeResolver;
72
73
    /**
74
     * Логер
75
     *
76
     * @var LoggerInterface
77
     */
78
    protected $log;
79
80
    /**
81
     * Резолвер для создания провайдеров отвечающих за исполнение функций, проверку условий, выполнение валидаторов и т.д.
82
     *
83
     * @var string
84
     */
85
    protected $defaultTypeResolverClass = TypeResolver::class;
86
87
    /**
88
     * Карта переходов состояния процесса workflow
89
     *
90
     * @var null|array
91
     */
92
    protected $mapEntryState;
93
94
    /**
95
     * Менеджер движков
96
     *
97
     * @var EngineManagerInterface
98
     */
99
    protected $engineManager;
100
101
    /**
102
     * AbstractWorkflow constructor.
103
     *
104
     * @throws InternalWorkflowException
105
     */
106 19
    public function __construct()
107
    {
108 19
        $this->initLoger();
109 19
        $this->initMapEntryState();
110 19
    }
111
112
    /**
113
     * Устанавливает менеджер движков
114
     *
115
     * @return EngineManagerInterface
116
     */
117 19
    public function getEngineManager()
118
    {
119 19
        if ($this->engineManager) {
120 19
            return $this->engineManager;
121
        }
122 19
        $this->engineManager = new EngineManager($this);
123
124 19
        return $this->engineManager;
125
    }
126
127
    /**
128
     * Возвращает менеджер движков
129
     *
130
     * @param EngineManagerInterface $engineManager
131
     *
132
     * @return $this
133
     */
134
    public function setEngineManager(EngineManagerInterface $engineManager)
135
    {
136
        $this->engineManager = $engineManager;
137
138
        return $this;
139
    }
140
141
    /**
142
     * Инициация карты переходов состояния процесса workflow
143
     */
144 19
    protected function initMapEntryState()
145
    {
146 19
        $this->mapEntryState = [
147 19
            WorkflowEntryInterface::COMPLETED => [
148 19
                WorkflowEntryInterface::ACTIVATED => WorkflowEntryInterface::ACTIVATED
149 19
            ],
150 19
            WorkflowEntryInterface::CREATED => [],
151 19
            WorkflowEntryInterface::ACTIVATED => [
152 19
                WorkflowEntryInterface::CREATED => WorkflowEntryInterface::CREATED,
153 19
                WorkflowEntryInterface::SUSPENDED => WorkflowEntryInterface::SUSPENDED,
154
155 19
            ],
156 19
            WorkflowEntryInterface::SUSPENDED => [
157 19
                WorkflowEntryInterface::ACTIVATED => WorkflowEntryInterface::ACTIVATED
158 19
            ],
159 19
            WorkflowEntryInterface::KILLED => [
160 19
                WorkflowEntryInterface::SUSPENDED => WorkflowEntryInterface::SUSPENDED,
161 19
                WorkflowEntryInterface::ACTIVATED => WorkflowEntryInterface::ACTIVATED,
162 19
                WorkflowEntryInterface::CREATED => WorkflowEntryInterface::CREATED
163 19
            ]
164 19
        ];
165 19
    }
166
167
    /**
168
     * Инициализация системы логирования
169
     *
170
     * @throws InternalWorkflowException
171
     */
172 19
    protected function initLoger()
173
    {
174 1
        try {
175 19
            $this->log = LogFactory::getLog();
176 19
        } catch (\Exception $e) {
177
            throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e);
178
        }
179 19
    }
180
181
    /**
182
     * Инициализация workflow. Workflow нужно иницаилизровать прежде, чем выполнять какие либо действия.
183
     * Workflow может быть инициализированно только один раз
184
     *
185
     * @param string $workflowName Имя workflow
186
     * @param integer $initialAction Имя первого шага, с которого начинается workflow
187
     * @param TransientVarsInterface $inputs Данные введеные пользователем
188
     *
189
     * @return integer
190
     *
191
     * @throws InternalWorkflowException
192
     * @throws InvalidActionException
193
     * @throws InvalidRoleException
194
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
195
     * @throws InvalidArgumentException
196
     * @throws WorkflowException
197
     */
198 19
    public function initialize($workflowName, $initialAction, TransientVarsInterface $inputs = null)
199
    {
200
        try {
201 19
            $initialAction = (integer)$initialAction;
202
203 19
            $wf = $this->getConfiguration()->getWorkflow($workflowName);
204
205 19
            $store = $this->getPersistence();
206
207 19
            $entry = $store->createEntry($workflowName);
208
209 19
            $ps = $store->getPropertySet($entry->getId());
210
211
212 19
            if (null === $inputs) {
213
                $inputs = $this->transientVarsFactory();
214
            }
215
216 19
            $engineManager = $this->getEngineManager();
217
218 19
            $transientVars = $inputs;
219 19
            $inputs = clone $transientVars;
220
221 19
            $engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $initialAction, new ArrayObject(), $ps);
222
223 19
            if (!$this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps)) {
224 1
                $this->context->setRollbackOnly();
225 1
                $errMsg = 'You are restricted from initializing this workflow';
226 1
                throw new InvalidRoleException($errMsg);
227
            }
228
229 18
            $action = $wf->getInitialAction($initialAction);
230
231 18
            if (null === $action) {
232
                $errMsg = sprintf('Invalid initial action id: %s', $initialAction);
233
                throw new InvalidActionException($errMsg);
234
            }
235
236 18
            $currentSteps = new SplObjectStorage();
237 18
            $transitionManager = $engineManager->getTransitionEngine();
238 18
            $transitionManager->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps);
239
240 16
            $entryId = $entry->getId();
241 19
        } catch (WorkflowException $e) {
242
            $this->context->setRollbackOnly();
243
            throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e);
244
        }
245
246 16
        return $entryId;
247
    }
248
249
    /**
250
     * @param $id
251
     * @param $inputs
252
     *
253
     * @return array
254
     *
255
     */
256 2
    public function getAvailableActions($id, TransientVarsInterface $inputs = null)
257 2
    {
258
        try {
259
            $store = $this->getPersistence();
260
            $entry = $store->findEntry($id);
261
262
            if (null === $entry) {
263
                $errMsg = sprintf(
264
                    'Не существует экземпляра workflow c id %s',
265
                    $id
266
                );
267
                throw new InvalidArgumentException($errMsg);
268
            }
269
270
            if (WorkflowEntryInterface::ACTIVATED === $entry->getState()) {
271
                return [];
272
            }
273
274
            $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName());
275
276
            $l = [];
277
            $ps = $store->getPropertySet($id);
278
279
            $transientVars = $inputs;
280
            if (null === $transientVars) {
281
                $transientVars = $this->transientVarsFactory();
282
            }
283
284
            $currentSteps = $store->findCurrentSteps($id);
285
286
            $engineManager =  $this->getEngineManager();
287
            $engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), 0, $currentSteps, $ps);
288
289
            $globalActions = $wf->getGlobalActions();
290
291
            $conditionsEngine = $engineManager->getConditionsEngine();
292
            foreach ($globalActions as $action) {
293
                $restriction = $action->getRestriction();
294
                $conditions = null;
295
296
                $transientVars['actionId'] = $action->getId();
297
298
                if (null !== $restriction) {
299
                    $conditions = $restriction->getConditionsDescriptor();
300
                }
301
302
                $flag = $conditionsEngine->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, 0) && $conditionsEngine->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0);
303
                if ($flag) {
304
                    $l[] = $action->getId();
305
                }
306
            }
307
308
            foreach ($currentSteps as $currentStep) {
309
                $availableActionsForStep = $this->getAvailableActionsForStep($wf, $currentStep, $transientVars, $ps);
310
                foreach ($availableActionsForStep as $actionId) {
311
                    $l[] = $actionId;
312
                }
313
            }
314
315
316
            return array_unique($l);
317
        } catch (\Exception $e) {
318
            $errMsg = 'Ошибка проверки доступных действий';
319
            $this->getLog()->error($errMsg, [$e]);
320
        }
321
322
        return [];
323
    }
324
325
    /**
326
     * Создает хранилище переменных
327
     *
328
     * @param $class
329
     *
330
     * @return TransientVarsInterface
331
     */
332
    protected function transientVarsFactory($class = BaseTransientVars::class)
333
    {
334
        $r = new \ReflectionClass($class);
335
        return $r->newInstance();
336
    }
337
338
    /**
339
     *
340
     *
341
     * Осуществляет переходл в новое состояние, для заданного процесса workflow
342
     *
343
     * @param integer $entryId id запущенного процесса workflow
344
     * @param integer $actionId id действия, доступного та текущем шаеге процессса workflow
345
     * @param TransientVarsInterface $inputs Входные данные для перехода
346
     *
347
     * @return void
348
     *
349
     * @throws WorkflowException
350
     * @throws InvalidActionException
351
     * @throws InvalidArgumentException
352
     * @throws InternalWorkflowException
353
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
354
     */
355 8
    public function doAction($entryId, $actionId, TransientVarsInterface $inputs = null)
356
    {
357 8
        $actionId = (integer)$actionId;
358 8
        if (null === $inputs) {
359
            $inputs = $this->transientVarsFactory();
360
        }
361 8
        $transientVars = $inputs;
362 8
        $inputs = clone $transientVars;
363
364 8
        $store = $this->getPersistence();
365 8
        $entry = $store->findEntry($entryId);
366
367 8
        if (WorkflowEntryInterface::ACTIVATED !== $entry->getState()) {
368
            return;
369
        }
370
371 8
        $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName());
372
373 8
        $currentSteps = $store->findCurrentSteps($entryId);
374 8
        if (!$currentSteps instanceof SplObjectStorage) {
375
            $errMsg = 'Invalid currentSteps';
376
            throw new InternalWorkflowException($errMsg);
377
        }
378
379 8
        $action = null;
380
381 8
        $ps = $store->getPropertySet($entryId);
382
383 8
        $engineManager = $this->getEngineManager();
384 8
        $engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $actionId, $currentSteps, $ps);
385
386
387 8
        $validAction = false;
388
389 8
        $transitionEngine = $engineManager->getTransitionEngine();
390 8
        foreach ($wf->getGlobalActions() as $actionDesc) {
391 View Code Duplication
            if ($actionId === $actionDesc->getId()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
392
                $action = $actionDesc;
393
394
                if ($transitionEngine->isActionAvailable($action, $transientVars, $ps, 0)) {
395
                    $validAction = true;
396
                }
397
            }
398 8
        }
399
400
401 8
        foreach ($currentSteps as $step) {
402 8
            $s = $wf->getStep($step->getStepId());
403
404 8
            foreach ($s->getActions() as $actionDesc) {
405 8
                if (!$actionDesc instanceof ActionDescriptor) {
406
                    $errMsg = 'Invalid action descriptor';
407
                    throw new InternalWorkflowException($errMsg);
408
                }
409
410 8 View Code Duplication
                if ($actionId === $actionDesc->getId()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
411 8
                    $action = $actionDesc;
412
413 8
                    if ($transitionEngine->isActionAvailable($action, $transientVars, $ps, $s->getId())) {
414 6
                        $validAction = true;
415 6
                    }
416 8
                }
417 8
            }
418 8
        }
419
420
421 8
        if (!$validAction) {
422 2
            $errMsg = sprintf(
423 2
                'Action %s is invalid',
424
                $actionId
425 2
            );
426 2
            throw new InvalidActionException($errMsg);
427
        }
428
429
430
        try {
431 6
            $transitionManager = $this->getEngineManager()->getTransitionEngine();
432 6
            if ($transitionManager->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps)) {
433
                $this->checkImplicitFinish($action, $entryId);
434
            }
435 6
        } catch (WorkflowException $e) {
436
            $this->context->setRollbackOnly();
437
            /** @var  WorkflowException $e*/
438
            throw $e;
439
        }
440 5
    }
441
442
    /**
443
     * @param ActionDescriptor $action
444
     * @param                  $id
445
     *
446
     * @return void
447
     *
448
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
449
     * @throws InvalidArgumentException
450
     * @throws InternalWorkflowException
451
     */
452
    protected function checkImplicitFinish(ActionDescriptor $action, $id)
453
    {
454
        $store = $this->getPersistence();
455
        $entry = $store->findEntry($id);
456
457
        $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName());
458
459
        $currentSteps = $store->findCurrentSteps($id);
460
461
        $isCompleted = $wf->getGlobalActions()->count() === 0;
462
463
        foreach ($currentSteps as $step) {
464
            if ($isCompleted) {
465
                break;
466
            }
467
468
            $stepDes = $wf->getStep($step->getStepId());
469
470
            if ($stepDes->getActions()->count() > 0) {
471
                $isCompleted = true;
472
            }
473
        }
474
475
        if ($isCompleted) {
476
            $entryEngine = $this->getEngineManager()->getEntryEngine();
477
            $entryEngine->completeEntry($action, $id, $currentSteps, WorkflowEntryInterface::COMPLETED);
478
        }
479
    }
480
481
482
483
    /**
484
     *
485
     * Check if the state of the specified workflow instance can be changed to the new specified one.
486
     *
487
     * @param integer $id The workflow instance id.
488
     * @param integer $newState The new state id.
489
     *
490
     * @return boolean true if the state of the workflow can be modified, false otherwise.
491
     *
492
     * @throws InternalWorkflowException
493
     */
494 16
    public function canModifyEntryState($id, $newState)
495
    {
496 16
        $store = $this->getPersistence();
497 16
        $entry = $store->findEntry($id);
498
499 16
        $currentState = $entry->getState();
500
501 16
        return array_key_exists($newState, $this->mapEntryState) && array_key_exists($currentState, $this->mapEntryState[$newState]);
502
    }
503
504
505
    /**
506
     *
507
     * Возвращает коллекцию объектов описывающие состояние для текущего экземпляра workflow
508
     *
509
     * @param integer $entryId id экземпляра workflow
510
     *
511
     * @return SplObjectStorage|StepInterface[]
512
     *
513
     * @throws InternalWorkflowException
514
     */
515
    public function getCurrentSteps($entryId)
516
    {
517
        return $this->getStepFromStorage($entryId, static::CURRENT_STEPS);
518
    }
519
520
    /**
521
     * Возвращает информацию о том в какие шаги, были осуществленны переходы, для процесса workflow с заданным id
522
     *
523
     * @param integer $entryId уникальный идентификатор процесса workflow
524
     *
525
     * @return StepInterface[]|SplObjectStorage список шагов
526
     *
527
     * @throws InternalWorkflowException
528
     */
529
    public function getHistorySteps($entryId)
530
    {
531
        return $this->getStepFromStorage($entryId, static::HISTORY_STEPS);
532
    }
533
534
    /**
535
     * Получение шагов информации о шагах процесса workflow
536
     *
537
     * @param $entryId
538
     * @param $type
539
     *
540
     * @return Spi\StepInterface[]|SplObjectStorage
541
     *
542
     * @throws InternalWorkflowException
543
     */
544
    protected function getStepFromStorage($entryId, $type)
545
    {
546
        try {
547
            $store = $this->getPersistence();
548
549
            if (static::CURRENT_STEPS === $type) {
550
                return $store->findCurrentSteps($entryId);
551
            } elseif (static::HISTORY_STEPS === $type) {
552
                return $store->findHistorySteps($entryId);
553
            }
554
        } catch (StoreException $e) {
555
            $errMsg = sprintf(
556
                'Ошибка при получение истории шагов для экземпляра workflow c id# %s',
557
                $entryId
558
            );
559
            $this->getLog()->error($errMsg, [$e]);
560
        }
561
562
        return new SplObjectStorage();
563
    }
564
565
566
567
    /**
568
     *
569
     *
570
     * Modify the state of the specified workflow instance.
571
     * @param integer $id The workflow instance id.
572
     * @param integer $newState the new state to change the workflow instance to.
573
     *
574
     * @throws InvalidArgumentException
575
     * @throws InvalidEntryStateException
576
     * @throws InternalWorkflowException
577
     */
578 19
    public function changeEntryState($id, $newState)
579
    {
580 16
        $store = $this->getPersistence();
581 16
        $entry = $store->findEntry($id);
582
583 16
        if ($newState === $entry->getState()) {
584
            return;
585
        }
586
587 16
        if ($this->canModifyEntryState($id, $newState)) {
588 19
            if (WorkflowEntryInterface::KILLED === $newState || WorkflowEntryInterface::COMPLETED === $newState) {
589
                $currentSteps = $this->getCurrentSteps($id);
590
591
                if (count($currentSteps) > 0) {
592
                    $entryEngine = $this->getEngineManager()->getEntryEngine();
593
                    $entryEngine->completeEntry(null, $id, $currentSteps, $newState);
594
                }
595
            }
596
597 16
            $store->setEntryState($id, $newState);
598 16
        } else {
599
            $errMsg = sprintf(
600
                'Не возможен переход в экземпляре workflow #%s. Текущее состояние %s, ожидаемое состояние %s',
601
                $id,
602
                $entry->getState(),
603
                $newState
604
            );
605
606
            throw new InvalidEntryStateException($errMsg);
607
        }
608
609 16
        $msg = sprintf(
610 16
            '%s : Новое состояние: %s',
611 16
            $entry->getId(),
612 16
            $entry->getState()
613 16
        );
614 16
        $this->getLog()->debug($msg);
615 16
    }
616
617
618
619
620
621
    /**
622
     * Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс
623
     *
624
     * @param string $workflowName имя workflow
625
     * @param integer $initialAction id начального состояния
626
     * @param TransientVarsInterface $inputs
627
     *
628
     * @return bool
629
     *
630
     * @throws InvalidArgumentException
631
     * @throws WorkflowException
632
     * @throws InternalWorkflowException
633
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
634
     */
635
    public function canInitialize($workflowName, $initialAction, TransientVarsInterface $inputs = null)
636
    {
637
        $mockWorkflowName = $workflowName;
638
        $mockEntry = new SimpleWorkflowEntry(0, $mockWorkflowName, WorkflowEntryInterface::CREATED);
639
640
        try {
641
            $ps = PropertySetManager::getInstance('memory', []);
642
            if (!$ps instanceof PropertySetInterface) {
643
                $errMsg = 'Invalid create PropertySet';
644
                throw new InternalWorkflowException($errMsg);
645
            }
646
        } catch (\Exception $e) {
647
            throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e);
648
        }
649
650
651
652
653
        if (null === $inputs) {
654
            $inputs = $this->transientVarsFactory();
655
        }
656
        $transientVars = $inputs;
657
658
        try {
659
            $this->getEngineManager()->getDataEngine()->populateTransientMap($mockEntry, $transientVars, [], $initialAction, [], $ps);
660
661
            $result = $this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps);
662
663
            return $result;
664
        } catch (InvalidActionException $e) {
665
            $this->getLog()->error($e->getMessage(), [$e]);
666
667
            return false;
668
        } catch (WorkflowException $e) {
669
            $errMsg = sprintf(
670
                'Ошибка при проверки canInitialize: %s',
671
                $e->getMessage()
672
            );
673
            $this->getLog()->error($errMsg, [$e]);
674
675
            return false;
676
        }
677
    }
678
679
680
    /**
681
     * Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс
682
     *
683
     * @param string $workflowName имя workflow
684
     * @param integer $initialAction id начального состояния
685
     * @param TransientVarsInterface $transientVars
686
     *
687
     * @param PropertySetInterface $ps
688
     *
689
     * @return bool
690
     *
691
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
692
     * @throws InvalidActionException
693
     * @throws InternalWorkflowException
694
     * @throws WorkflowException
695
     */
696 19
    protected function canInitializeInternal($workflowName, $initialAction, TransientVarsInterface $transientVars, PropertySetInterface $ps)
697
    {
698 19
        $wf = $this->getConfiguration()->getWorkflow($workflowName);
699
700 19
        $actionDescriptor = $wf->getInitialAction($initialAction);
701
702 19
        if (null === $actionDescriptor) {
703
            $errMsg = sprintf(
704
                'Некорректное инициирующие действие # %s',
705
                $initialAction
706
            );
707
            throw new InvalidActionException($errMsg);
708
        }
709
710 19
        $restriction = $actionDescriptor->getRestriction();
711
712
713 19
        $conditions = null;
714 19
        if (null !== $restriction) {
715 2
            $conditions = $restriction->getConditionsDescriptor();
716 2
        }
717
718 19
        $conditionsEngine = $this->getEngineManager()->getConditionsEngine();
719 19
        $passesConditions = $conditionsEngine->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0);
720
721 19
        return $passesConditions;
722
    }
723
724
    /**
725
     * Возвращает резолвер
726
     *
727
     * @return TypeResolverInterface
728
     */
729 19
    public function getResolver()
730
    {
731 19
        if (null !== $this->typeResolver) {
732 16
            return $this->typeResolver;
733
        }
734
735 19
        $classResolver = $this->getDefaultTypeResolverClass();
736 19
        $r = new ReflectionClass($classResolver);
737 19
        $resolver = $r->newInstance();
738 19
        $this->typeResolver = $resolver;
739
740 19
        return $this->typeResolver;
741
    }
742
743
    /**
744
     * Возвращает хранилище состояния workflow
745
     *
746
     * @return WorkflowStoreInterface
747
     *
748
     * @throws InternalWorkflowException
749
     */
750 19
    protected function getPersistence()
751
    {
752 19
        return $this->getConfiguration()->getWorkflowStore();
753
    }
754
755
    /**
756
     * Получить конфигурацию workflow. Метод также проверяет была ли иницилазированн конфигурация, если нет, то
757
     * инициализирует ее.
758
     *
759
     * Если конфигурация не была установленна, то возвращает конфигурацию по умолчанию
760
     *
761
     * @return ConfigurationInterface|DefaultConfiguration Конфигурация которая была установленна
762
     *
763
     * @throws InternalWorkflowException
764
     */
765 19
    public function getConfiguration()
766
    {
767 19
        $config = null !== $this->configuration ? $this->configuration : DefaultConfiguration::getInstance();
768
769 19
        if (!$config->isInitialized()) {
770
            try {
771
                $config->load(null);
772
            } catch (FactoryException $e) {
773
                $errMsg = 'Ошибка при иницилазации конфигурации workflow';
774
                $this->getLog()->critical($errMsg, ['exception' => $e]);
775
                throw new InternalWorkflowException($errMsg, $e->getCode(), $e);
776
            }
777
        }
778
779 19
        return $config;
780
    }
781
782
    /**
783
     * @return LoggerInterface
784
     */
785 17
    public function getLog()
786
    {
787 17
        return $this->log;
788
    }
789
790
    /**
791
     * @param LoggerInterface $log
792
     *
793
     * @return $this
794
     *
795
     * @throws InternalWorkflowException
796
     */
797
    public function setLog($log)
798
    {
799
        try {
800
            LogFactory::validLogger($log);
801
        } catch (\Exception $e) {
802
            $errMsg = 'Ошибка при валидации логера';
803
            throw new InternalWorkflowException($errMsg, $e->getCode(), $e);
804
        }
805
806
807
        $this->log = $log;
808
809
        return $this;
810
    }
811
812
813
    /**
814
     * Get the workflow descriptor for the specified workflow name.
815
     *
816
     * @param string $workflowName The workflow name.
817
     * @return WorkflowDescriptor
818
     *
819
     * @throws InternalWorkflowException
820
     */
821
    public function getWorkflowDescriptor($workflowName)
822
    {
823
        try {
824
            return $this->getConfiguration()->getWorkflow($workflowName);
825
        } catch (FactoryException $e) {
826
            $errMsg = 'Ошибка при загрузке workflow';
827
            $this->getLog()->error($errMsg, ['exception' => $e]);
828
            throw new InternalWorkflowException($errMsg, $e->getCode(), $e);
829
        }
830
    }
831
832
833
    /**
834
     * Executes a special trigger-function using the context of the given workflow instance id.
835
     * Note that this method is exposed for Quartz trigger jobs, user code should never call it.
836
     *
837
     * @param integer $id The workflow instance id
838
     * @param integer $triggerId The id of the special trigger-function
839
     *
840
     * @throws InvalidArgumentException
841
     * @throws WorkflowException
842
     * @throws InternalWorkflowException
843
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
844
     */
845
    public function executeTriggerFunction($id, $triggerId)
846
    {
847
        $store = $this->getPersistence();
848
        $entry = $store->findEntry($id);
849
850
        if (null === $entry) {
851
            $errMsg = sprintf(
852
                'Ошибка при выполнение тригера # %s для несуществующего экземпляра workflow id# %s',
853
                $triggerId,
854
                $id
855
            );
856
            $this->getLog()->warning($errMsg);
857
            return;
858
        }
859
860
        $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName());
861
862
        $ps = $store->getPropertySet($id);
863
        $transientVars = $this->transientVarsFactory();
864
865
        $this->getEngineManager()->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $store->findCurrentSteps($id), $ps);
866
867
        $functionsEngine = $this->getEngineManager()->getFunctionsEngine();
868
        $functionsEngine->executeFunction($wf->getTriggerFunction($triggerId), $transientVars, $ps);
869
    }
870
871
872
    /**
873
     * @param WorkflowDescriptor   $wf
874
     * @param StepInterface        $step
875
     * @param TransientVarsInterface                $transientVars
876
     * @param PropertySetInterface $ps
877
     *
878
     * @return array
879
     *
880
     * @throws InternalWorkflowException
881
     * @throws WorkflowException
882
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
883
     */
884
    protected function getAvailableActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps)
885
    {
886
        $l = [];
887
        $s = $wf->getStep($step->getStepId());
888
889
        if (null === $s) {
890
            $errMsg = sprintf(
891
                'getAvailableActionsForStep вызван с не существующим id шага %s',
892
                $step->getStepId()
893
            );
894
895
            $this->getLog()->warning($errMsg);
896
897
            return $l;
898
        }
899
900
        $actions  = $s->getActions();
901
902
        if (null === $actions || 0  === $actions->count()) {
903
            return $l;
904
        }
905
906
        $conditionsEngine = $this->getEngineManager()->getConditionsEngine();
907
        foreach ($actions as $action) {
908
            $restriction = $action->getRestriction();
909
            $conditions = null;
910
911
            $transientVars['actionId'] = $action->getId();
912
913
914
            if (null !== $restriction) {
915
                $conditions = $restriction->getConditionsDescriptor();
916
            }
917
918
            $f = $conditionsEngine->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, $s->getId())
919
                 && $conditionsEngine->passesConditionsByDescriptor($conditions, $transientVars, $ps, $s->getId());
920
            if ($f) {
921
                $l[] = $action->getId();
922
            }
923
        }
924
925
        return $l;
926
    }
927
928
    /**
929
     * @param ConfigurationInterface $configuration
930
     *
931
     * @return $this
932
     */
933 19
    public function setConfiguration(ConfigurationInterface $configuration)
934
    {
935 19
        $this->configuration = $configuration;
936
937 19
        return $this;
938
    }
939
940
    /**
941
     * Возвращает состояние для текущего экземпляра workflow
942
     *
943
     * @param integer $id id экземпляра workflow
944
     * @return integer id текущего состояния
945
     *
946
     * @throws InternalWorkflowException
947
     */
948
    public function getEntryState($id)
949
    {
950
        try {
951
            $store = $this->getPersistence();
952
953
            return $store->findEntry($id)->getState();
954
        } catch (StoreException $e) {
955
            $errMsg = sprintf(
956
                'Ошибка при получение состояния экземпляра workflow c id# %s',
957
                $id
958
            );
959
            $this->getLog()->error($errMsg, [$e]);
960
        }
961
962
        return WorkflowEntryInterface::UNKNOWN;
963
    }
964
965
966
    /**
967
     * Настройки хранилища
968
     *
969
     * @return array
970
     *
971
     * @throws InternalWorkflowException
972
     */
973
    public function getPersistenceProperties()
974
    {
975
        return $this->getConfiguration()->getPersistenceArgs();
976
    }
977
978
979
    /**
980
     * Get the PropertySet for the specified workflow instance id.
981
     * @param integer $id The workflow instance id.
982
     *
983
     * @return PropertySetInterface
984
     * @throws InternalWorkflowException
985
     */
986
    public function getPropertySet($id)
987
    {
988
        $ps = null;
989
990
        try {
991
            $ps = $this->getPersistence()->getPropertySet($id);
992
        } catch (StoreException $e) {
993
            $errMsg = sprintf(
994
                'Ошибка при получение PropertySet для экземпляра workflow c id# %s',
995
                $id
996
            );
997
            $this->getLog()->error($errMsg, [$e]);
998
        }
999
1000
        return $ps;
1001
    }
1002
1003
    /**
1004
     * @return string[]
1005
     *
1006
     * @throws InternalWorkflowException
1007
     */
1008
    public function getWorkflowNames()
1009
    {
1010
        try {
1011
            return $this->getConfiguration()->getWorkflowNames();
1012
        } catch (FactoryException $e) {
1013
            $errMsg = 'Ошибка при получение имен workflow';
1014
            $this->getLog()->error($errMsg, [$e]);
1015
        }
1016
1017
        return [];
1018
    }
1019
1020
    /**
1021
     * @param TypeResolverInterface $typeResolver
1022
     *
1023
     * @return $this
1024
     */
1025
    public function setTypeResolver(TypeResolverInterface $typeResolver)
1026
    {
1027
        $this->typeResolver = $typeResolver;
1028
1029
        return $this;
1030
    }
1031
1032
1033
    /**
1034
     * Get a collection (Strings) of currently defined permissions for the specified workflow instance.
1035
     * @param integer $id id the workflow instance id.
1036
     * @param TransientVarsInterface $inputs inputs The inputs to the workflow instance.
1037
     *
1038
     * @return array  A List of permissions specified currently (a permission is a string name).
1039
     *
1040
     */
1041
    public function getSecurityPermissions($id, TransientVarsInterface $inputs = null)
1042
    {
1043
        try {
1044
            $store = $this->getPersistence();
1045
            $entry = $store->findEntry($id);
1046
            $wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName());
1047
1048
            $ps = $store->getPropertySet($id);
1049
1050
            if (null === $inputs) {
1051
                $inputs = $this->transientVarsFactory();
1052
            }
1053
            $transientVars = $inputs;
1054
1055
            $currentSteps = $store->findCurrentSteps($id);
1056
1057
            $engineManager = $this->getEngineManager();
1058
            try {
1059
                $engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $currentSteps, $ps);
1060
            } catch (\Exception $e) {
1061
                $errMsg = sprintf(
1062
                    'Внутреннея ошибка: %s',
1063
                    $e->getMessage()
1064
                );
1065
                throw new InternalWorkflowException($errMsg, $e->getCode(), $e);
1066
            }
1067
1068
1069
            $s = [];
1070
1071
            $conditionsEngine = $engineManager->getConditionsEngine();
1072
            foreach ($currentSteps as $step) {
1073
                $stepId = $step->getStepId();
1074
1075
                $xmlStep = $wf->getStep($stepId);
1076
1077
                $securities = $xmlStep->getPermissions();
1078
1079
                foreach ($securities as $security) {
1080
                    if (!$security instanceof PermissionDescriptor) {
1081
                        $errMsg = 'Invalid PermissionDescriptor';
1082
                        throw new InternalWorkflowException($errMsg);
1083
                    }
1084
                    $conditionsDescriptor = $security->getRestriction()->getConditionsDescriptor();
1085
                    if (null !== $security->getRestriction() && $conditionsEngine->passesConditionsByDescriptor($conditionsDescriptor, $transientVars, $ps, $xmlStep->getId())) {
1086
                        $s[$security->getName()] = $security->getName();
1087
                    }
1088
                }
1089
            }
1090
1091
            return $s;
1092
        } catch (\Exception $e) {
1093
            $errMsg = sprintf(
1094
                'Ошибка при получение информации о правах доступа для экземпляра workflow c id# %s',
1095
                $id
1096
            );
1097
            $this->getLog()->error($errMsg, [$e]);
1098
        }
1099
1100
        return [];
1101
    }
1102
1103
1104
    /**
1105
     * Get the name of the specified workflow instance.
1106
     *
1107
     * @param integer $id the workflow instance id.
1108
     *
1109
     * @return string
1110
     *
1111
     * @throws InternalWorkflowException
1112
     */
1113
    public function getWorkflowName($id)
1114
    {
1115
        try {
1116
            $store = $this->getPersistence();
1117
            $entry = $store->findEntry($id);
1118
1119
            if (null !== $entry) {
1120
                return $entry->getWorkflowName();
1121
            }
1122
        } catch (FactoryException $e) {
1123
            $errMsg = sprintf(
1124
                'Ошибка при получение имен workflow для инстанса с id # %s',
1125
                $id
1126
            );
1127
            $this->getLog()->error($errMsg, [$e]);
1128
        }
1129
1130
        return null;
1131
    }
1132
1133
    /**
1134
     * Удаляет workflow
1135
     *
1136
     * @param string $workflowName
1137
     *
1138
     * @return bool
1139
     *
1140
     * @throws InternalWorkflowException
1141
     */
1142
    public function removeWorkflowDescriptor($workflowName)
1143
    {
1144
        return $this->getConfiguration()->removeWorkflow($workflowName);
1145
    }
1146
1147
    /**
1148
     * @param                    $workflowName
1149
     * @param WorkflowDescriptor $descriptor
1150
     * @param                    $replace
1151
     *
1152
     * @return bool
1153
     *
1154
     * @throws InternalWorkflowException
1155
     */
1156
    public function saveWorkflowDescriptor($workflowName, WorkflowDescriptor $descriptor, $replace)
1157
    {
1158
        $success = $this->getConfiguration()->saveWorkflow($workflowName, $descriptor, $replace);
1159
1160
        return $success;
1161
    }
1162
1163
1164
    /**
1165
     * Query the workflow store for matching instances
1166
     *
1167
     * @param WorkflowExpressionQuery $query
1168
     *
1169
     * @return array
1170
     *
1171
     * @throws InternalWorkflowException
1172
     */
1173
    public function query(WorkflowExpressionQuery $query)
1174
    {
1175
        return $this->getPersistence()->query($query);
1176
    }
1177
1178
    /**
1179
     * @return string
1180
     */
1181 19
    public function getDefaultTypeResolverClass()
1182
    {
1183 19
        return $this->defaultTypeResolverClass;
1184
    }
1185
1186
    /**
1187
     * @param string $defaultTypeResolverClass
1188
     *
1189
     * @return $this
1190
     */
1191
    public function setDefaultTypeResolverClass($defaultTypeResolverClass)
1192
    {
1193
        $this->defaultTypeResolverClass = (string)$defaultTypeResolverClass;
1194
1195
        return $this;
1196
    }
1197
1198
1199
    /**
1200
     * @return WorkflowContextInterface
1201
     */
1202 19
    public function getContext()
1203
    {
1204 19
        return $this->context;
1205
    }
1206
}
1207