ExecutionEntity.php$1 ➔ restoreProcessInstance()   F
last analyzed

Complexity

Conditions 31

Size

Total Lines 121

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 121
rs 3.3333
c 0
b 0
f 0
cc 31

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Jabe\Impl\Persistence\Entity;
4
5
use Jabe\{
6
    ProcessEngineInterface,
7
    ProcessEngineServicesInterface
8
};
9
use Jabe\Delegate\ExecutionListenerInterface;
10
use Jabe\Impl\ProcessEngineLogger;
11
use Jabe\Impl\Bpmn\Parser\{
12
    BpmnParse,
13
    EventSubscriptionDeclaration
14
};
15
use Jabe\Impl\Cfg\ProcessEngineConfigurationImpl;
16
use Jabe\Impl\Cfg\Multitenancy\{
17
    TenantIdProviderInterface,
18
    TenantIdProviderProcessInstanceContext
19
};
20
use Jabe\Impl\Context\Context;
21
use Jabe\Impl\Core\Variable\Event\VariableEvent;
22
use Jabe\Impl\Core\Variable\Scope\{
23
    VariableCollectionProvider,
24
    VariableInstanceFactoryInterface,
25
    VariableInstanceLifecycleListenerInterface,
26
    VariableListenerInvocationListener,
27
    VariableStore,
28
    VariablesProviderInterface
29
};
30
use Jabe\Impl\Db\{
31
    DbEntityInterface,
32
    EnginePersistenceLogger,
33
    HasDbReferencesInterface,
34
    HasDbRevisionInterface,
35
};
36
use Jabe\Impl\Event\EventType;
37
use Jabe\Impl\History\Event\{
38
    HistoricVariableUpdateEventEntity,
39
    HistoryEvent,
40
    HistoryEventProcessor,
41
    HistoryEventCreator,
42
    HistoryEventTypes
43
};
44
use Jabe\Impl\Incident\{
45
    IncidentContext,
46
    IncidentHandling
47
};
48
use Jabe\Impl\Interceptor\AtomicOperationInvocation;
49
use Jabe\Impl\JobExecutor\{
50
    MessageJobDeclaration,
51
    TimerDeclarationImpl
52
};
53
use Jabe\Impl\Pvm\{
54
    PvmActivityInterface,
55
    PvmProcessDefinitionInterface
56
};
57
use Jabe\Impl\Pvm\Delegate\CompositeActivityBehaviorInterface;
58
use Jabe\Impl\Pvm\Process\{
59
    ActivityImpl,
60
    ProcessDefinitionImpl,
61
    ScopeImpl
62
};
63
use Jabe\Impl\Pvm\Runtime\{
64
    ActivityInstanceState,
65
    AtomicOperation,
66
    PvmExecutionImpl
67
};
68
use Jabe\Impl\Tree\{
69
    ExecutionTopDownWalker,
70
    TreeVisitorInterface
71
};
72
use Jabe\Impl\Util\{
73
    BitMaskUtil,
74
    CollectionUtil
75
};
76
use Jabe\Impl\Variable\VariableDeclaration;
77
use Jabe\Repository\ProcessDefinitionInterface;
78
use Jabe\Runtime\{
79
    ExecutionInterface,
80
    JobInterface,
81
    ProcessInstanceInterface
82
};
83
use Jabe\Variable\{
84
    VariableMapInterface,
85
    Variables
86
};
87
use Bpmn\BpmnModelInstanceInterface;
88
use Bpmn\Instance\FlowElementInterface;
89
use Xml\Instance\ModelElementInstanceInterface;
90
use Xml\Type\ModelElementTypeInterface;
91
92
class ExecutionEntity extends PvmExecutionImpl implements ExecutionInterface, ProcessInstanceInterface, DbEntityInterface, HasDbRevisionInterface, HasDbReferencesInterface, VariablesProviderInterface
93
{
94
    //protected static final EnginePersistenceLogger LOG = ProcessEngineLogger.PERSISTENCE_LOGGER;
95
96
    // Persistent refrenced entities state //////////////////////////////////////
97
    public const EVENT_SUBSCRIPTIONS_STATE_BIT = 1;
98
    public const TASKS_STATE_BIT = 2;
99
    public const JOBS_STATE_BIT = 3;
100
    public const INCIDENT_STATE_BIT = 4;
101
    public const VARIABLES_STATE_BIT = 5;
102
    public const SUB_PROCESS_INSTANCE_STATE_BIT = 6;
103
    public const SUB_CASE_INSTANCE_STATE_BIT = 7;
104
    public const EXTERNAL_TASKS_BIT = 8;
105
106
    // current position /////////////////////////////////////////////////////////
107
108
    /**
109
     * the process instance. this is the root of the execution tree. the
110
     * processInstance of a process instance is a self reference.
111
     */
112
    protected $processInstance;
113
114
    /** the parent execution */
115
    protected $parent;
116
117
    /** nested executions representing scopes or concurrent paths */
118
    protected $executions = [];
119
120
    /** super execution, not-null if this execution is part of a subprocess */
121
    protected $superExecution;
122
123
    /**
124
     * super case execution, not-null if this execution is part of a case
125
     * execution
126
     */
127
    //protected $superCaseExecution;
128
129
    /**
130
     * reference to a subprocessinstance, not-null if currently subprocess is
131
     * started from this execution
132
     */
133
    protected $subProcessInstance;
134
135
    /**
136
     * reference to a subcaseinstance, not-null if currently subcase is started
137
     * from this execution
138
     */
139
    //protected $subCaseInstance;
140
141
    protected $shouldQueryForSubprocessInstance = false;
142
143
    //protected $shouldQueryForSubCaseInstance = false;
144
145
    // associated entities /////////////////////////////////////////////////////
146
147
    // (we cache associated entities here to minimize db queries)
148
    protected $eventSubscriptions;
149
    protected $jobs;
150
    protected $tasks;
151
    protected $externalTasks;
152
    protected $incidents;
153
    protected $cachedEntityState;
154
155
    protected $variableStore;
156
157
158
    // replaced by //////////////////////////////////////////////////////////////
159
160
    protected $suspensionState;
161
162
    // Persistence //////////////////////////////////////////////////////////////
163
164
    protected $revision = 1;
165
166
    /**
167
     * persisted reference to the processDefinition.
168
     *
169
     * @see #processDefinition
170
     * @see #setProcessDefinition(ProcessDefinitionImpl)
171
     * @see #getProcessDefinition()
172
     */
173
    protected $processDefinitionId;
174
175
    /**
176
     * persisted reference to the current position in the diagram within the
177
     * {@link #processDefinition}.
178
     *
179
     * @see #activity
180
     * @see #getActivity()
181
     */
182
    protected $activityId;
183
184
    /**
185
     * The name of the current activity position
186
     */
187
    protected $activityName;
188
189
    /**
190
     * persisted reference to the process instance.
191
     *
192
     * @see #getProcessInstance()
193
     */
194
    protected $processInstanceId;
195
196
    /**
197
     * persisted reference to the parent of this execution.
198
     *
199
     * @see #getParent()
200
     */
201
    protected $parentId;
202
203
    /**
204
     * persisted reference to the super execution of this execution
205
     *
206
     * @See {@link #getSuperExecution()}
207
     * @see <code>setSuperExecution(ExecutionEntity)</code>
208
     */
209
    protected $superExecutionId;
210
211
    /**
212
     * persisted reference to the root process instance.
213
     *
214
     * @see #getRootProcessInstanceId()
215
     */
216
    protected $rootProcessInstanceId;
217
218
    /**
219
     * persisted reference to the super case execution of this execution
220
     *
221
     * @See {@link #getSuperCaseExecution()}
222
     * @see <code>setSuperCaseExecution(ExecutionEntity)</code>
223
     */
224
    protected $superCaseExecutionId;
225
226
    /**
227
     * Contains observers which are observe the execution.
228
     * @since 7.6
229
     */
230
    protected $executionObservers = [];
231
232
    protected $registeredVariableListeners = [];
233
234
    public function __construct()
235
    {
236
        $this->variableStore = new VariableStore($this, new ExecutionEntityReferencer($this));
237
        $this->suspensionState = SuspensionState::active()->getStateCode();
238
    }
239
240
    /**
241
     * creates a new execution. properties processDefinition, processInstance and
242
     * activity will be initialized.
243
     */
244
    public function createExecution(): ExecutionEntity
245
    {
246
        // create the new child execution
247
        $createdExecution = self::createNewExecution();
248
249
        // initialize sequence counter
250
        $createdExecution->setSequenceCounter($this->getSequenceCounter());
251
252
        // manage the bidirectional parent-child relation
253
        $createdExecution->setParent($this);
254
255
        // initialize the new execution
256
        $createdExecution->setProcessDefinition($this->getProcessDefinition());
257
        $createdExecution->setProcessInstance($this->getProcessInstance());
258
        $createdExecution->setActivity($this->getActivity());
259
        $createdExecution->setSuspensionState($this->getSuspensionState());
260
261
        // make created execution start in same activity instance
262
        $createdExecution->activityInstanceId = $this->activityInstanceId;
263
264
        // inherit the tenant id from parent execution
265
        if ($this->tenantId !== null) {
266
            $createdExecution->setTenantId($this->tenantId);
267
        }
268
269
        // with the fix of CAM-9249 we presume that the parent and the child have the same startContext
270
        $createdExecution->setStartContext($this->scopeInstantiationContext);
271
272
        $createdExecution->skipCustomListeners = $this->skipCustomListeners;
273
        $createdExecution->skipIoMapping = $this->skipIoMapping;
274
275
        //LOG.createChildExecution(createdExecution, this);
276
277
        return $createdExecution;
278
    }
279
280
    // sub process instance
281
    // /////////////////////////////////////////////////////////////
282
283
    public function createSubProcessInstance(PvmProcessDefinitionInterface $processDefinition, ?string $businessKey = null, ?string $caseInstanceId = null): ExecutionEntity
284
    {
285
        $this->shouldQueryForSubprocessInstance = true;
286
287
        $subProcessInstance = parent::createSubProcessInstance($processDefinition, $businessKey, $caseInstanceId);
288
289
        // inherit the tenant-id from the process definition
290
        $tenantId = $processDefinition->getTenantId();
0 ignored issues
show
Bug introduced by
The method getTenantId() does not exist on Jabe\Impl\Pvm\PvmProcessDefinitionInterface. It seems like you code against a sub-type of Jabe\Impl\Pvm\PvmProcessDefinitionInterface such as Jabe\Impl\Persistence\En...ProcessDefinitionEntity. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

290
        /** @scrutinizer ignore-call */ 
291
        $tenantId = $processDefinition->getTenantId();
Loading history...
291
        if ($tenantId !== null) {
292
            $subProcessInstance->setTenantId($tenantId);
293
        } else {
294
            // if process definition has no tenant id, inherit this process instance's tenant id
295
            $subProcessInstance->setTenantId($this->tenantId);
296
        }
297
298
        $this->fireHistoricActivityInstanceUpdate();
299
300
        return $subProcessInstance;
301
    }
302
303
    protected static function createNewExecution(): ExecutionEntity
304
    {
305
        $newExecution = new ExecutionEntity();
306
        self::initializeAssociations($newExecution);
307
        $newExecution->insert();
308
309
        return $newExecution;
310
    }
311
312
    protected function newExecution(): PvmExecutionImpl
313
    {
314
        return self::createNewExecution();
315
    }
316
317
    // sub case instance ////////////////////////////////////////////////////////
318
319
    /*public CaseExecutionEntity createSubCaseInstance(CmmnCaseDefinition caseDefinition) {
320
        return createSubCaseInstance(caseDefinition, null);
321
    }*/
322
323
    /*@Override
324
    public CaseExecutionEntity createSubCaseInstance(CmmnCaseDefinition caseDefinition, String businessKey) {
325
        CaseExecutionEntity subCaseInstance = (CaseExecutionEntity) caseDefinition.createCaseInstance(businessKey);
326
327
        // inherit the tenant-id from the case definition
328
        String tenantId = ((CaseDefinitionEntity) caseDefinition)->getTenantId();
329
        if (tenantId !== null) {
330
        subCaseInstance->setTenantId(tenantId);
331
        }
332
        else {
333
        // if case definition has no tenant id, inherit this process instance's tenant id
334
        subCaseInstance->setTenantId($this->tenantId);
335
        }
336
337
        // manage bidirectional super-process-sub-case-instances relation
338
        subCaseInstance->setSuperExecution($this);
339
        setSubCaseInstance(subCaseInstance);
340
341
        fireHistoricActivityInstanceUpdate();
342
343
        return subCaseInstance;
344
    }*/
345
346
    // helper ///////////////////////////////////////////////////////////////////
347
348
    public function fireHistoricActivityInstanceUpdate(): void
349
    {
350
        $configuration = Context::getProcessEngineConfiguration();
351
        $historyLevel = $configuration->getHistoryLevel();
0 ignored issues
show
Bug introduced by
The method getHistoryLevel() does not exist on Jabe\Impl\Cfg\ProcessEngineConfigurationImpl. Did you maybe mean getHistoryLevelCommand()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

351
        /** @scrutinizer ignore-call */ 
352
        $historyLevel = $configuration->getHistoryLevel();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
352
        if ($historyLevel->isHistoryEventProduced(HistoryEventTypes::activityInstanceUpdate(), $this)) {
353
        // publish update event for current activity instance (containing the id
354
        // of the sub process/case)
355
            $scope = $this;
356
            HistoryEventProcessor::processHistoryEvents(new class ($scope) extends HistoryEventCreator {
357
                private $scope;
358
359
                public function __construct(ExecutionEntity $scope)
360
                {
361
                    $this->scope = $scope;
362
                }
363
364
                public function createHistoryEvent(HistoryEventProducer $producer): HistoryEvent
365
                {
366
                    return $producer->createActivityInstanceUpdateEvt($this->scope);
367
                }
368
            });
369
        }
370
    }
371
372
    // scopes ///////////////////////////////////////////////////////////////////
373
374
    public function initialize(): void
375
    {
376
        //LOG.initializeExecution($this);
377
378
        $scope = $this->getScopeActivity();
379
        $this->ensureParentInitialized();
380
381
        $variableDeclarations = $scope->getProperty(BpmnParse::PROPERTYNAME_VARIABLE_DECLARATIONS);
382
        if ($variableDeclarations !== null) {
383
            foreach ($variableDeclarations as $variableDeclaration) {
384
                $variableDeclaration->initialize($this, $this->parent);
385
            }
386
        }
387
388
        if ($this->isProcessInstanceExecution()) {
389
            $initiatorVariableName = $this->processDefinition->getProperty(BpmnParse::PROPERTYNAME_INITIATOR_VARIABLE_NAME);
390
            if ($initiatorVariableName !== null) {
391
                $authenticatedUserId = Context::getCommandContext()->getAuthenticatedUserId();
392
                $this->setVariable($initiatorVariableName, $authenticatedUserId);
393
            }
394
        }
395
396
        // create event subscriptions for the current scope
397
        foreach (EventSubscriptionDeclaration::getDeclarationsForScope($scope) as $key => $declaration) {
398
            if (!$declaration->isStartEvent()) {
399
                $declaration->createSubscriptionForExecution($this);
400
            }
401
        }
402
    }
403
404
    public function initializeTimerDeclarations(): void
405
    {
406
        //LOG.initializeTimerDeclaration($this);
407
        $scope = $this->getScopeActivity();
408
        $this->createTimerInstances(array_values(TimerDeclarationImpl::getDeclarationsForScope($scope)));
409
        foreach (TimerDeclarationImpl::getTimeoutListenerDeclarationsForScope($scope) as $key => $timerDeclarations) {
410
            $this->createTimerInstances(array_values($timerDeclarations));
411
        }
412
    }
413
414
    protected function createTimerInstances(array $timerDeclarations): void
415
    {
416
        foreach ($timerDeclarations as $timerDeclaration) {
417
            $timerDeclaration->createTimerInstance($this);
418
        }
419
    }
420
421
    protected static function initializeAssociations(ExecutionEntity $execution): void
422
    {
423
        // initialize the lists of referenced objects (prevents db queries)
424
        $execution->executions = [];
425
        $execution->variableStore->setVariablesProvider(VariableCollectionProvider::emptyVariables());
426
        $execution->variableStore->forceInitialization();
427
        $execution->eventSubscriptions = [];
428
        $execution->jobs = [];
429
        $execution->tasks = [];
430
        $execution->externalTasks = [];
431
        $execution->incidents = [];
432
433
        // Cached entity-state initialized to null, all bits are zero, indicating NO
434
        // entities present
435
        $execution->cachedEntityState = 0;
436
    }
437
438
    public function start(array $variables, VariableMapInterface $formProperties): void
439
    {
440
        if ($this->getSuperExecution() === null) {
441
            $this->setRootProcessInstanceId($this->processInstanceId);
442
        } else {
443
            $superExecution = $this->getSuperExecution();
444
            $this->setRootProcessInstanceId($superExecution->getRootProcessInstanceId());
445
        }
446
447
        // determine tenant Id if null
448
        $this->provideTenantId($variables, $formProperties);
449
        parent::start($variables, $formProperties);
450
    }
451
452
    public function startWithoutExecuting(array $variables): void
453
    {
454
        $this->setRootProcessInstanceId($this->getProcessInstanceId());
455
        $this->provideTenantId($variables, null);
456
        parent::startWithoutExecuting($variables);
457
    }
458
459
    protected function provideTenantId(array $variables, ?VariableMap $properties = null): void
460
    {
461
        if ($this->tenantId === null) {
462
            $tenantIdProvider = Context::getProcessEngineConfiguration()->getTenantIdProvider();
0 ignored issues
show
Bug introduced by
The method getTenantIdProvider() does not exist on Jabe\Impl\Cfg\ProcessEngineConfigurationImpl. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

462
            $tenantIdProvider = Context::getProcessEngineConfiguration()->/** @scrutinizer ignore-call */ getTenantIdProvider();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
463
464
            if ($tenantIdProvider !== null) {
465
                $variableMap = Variables::fromMap($variables);
466
                if ($properties !== null && !$properties->isEmpty()) {
467
                    $variableMap->putAll($properties);
0 ignored issues
show
Bug introduced by
$properties of type Jabe\Impl\Persistence\Entity\VariableMap is incompatible with the type array expected by parameter $m of Jabe\Variable\Impl\VariableMapImpl::putAll(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

467
                    $variableMap->putAll(/** @scrutinizer ignore-type */ $properties);
Loading history...
468
                }
469
470
                $processDefinition = $this->getProcessDefinition();
471
472
                $ctx = null;
473
                if ($this->superExecutionId !== null) {
474
                    $ctx = new TenantIdProviderProcessInstanceContext($processDefinition, $variableMap, $this->getSuperExecution());
475
                } else {
476
                    $ctx = new TenantIdProviderProcessInstanceContext($processDefinition, $variableMap);
477
                }
478
                /*elseif ($this->superCaseExecutionId !== null) { ctx = new TenantIdProviderProcessInstanceContext(processDefinition, variableMap, getSuperCaseExecution());
479
                } */
480
481
                $this->tenantId = $tenantIdProvider->provideTenantIdForProcessInstance($ctx);
482
            }
483
        }
484
    }
485
486
    public function fireHistoricProcessStartEvent(): void
487
    {
488
        $configuration = Context::getProcessEngineConfiguration();
489
        $historyLevel = $configuration->getHistoryLevel();
490
        // TODO: This smells bad, as the rest of the history is done via the
491
        // ParseListener
492
        if ($historyLevel->isHistoryEventProduced(HistoryEventTypes::processInstanceStart(), $this->processInstance)) {
493
            $processInstance = $this->processInstance;
494
            HistoryEventProcessor::processHistoryEvents(new class ($processInstance) extends HistoryEventCreator {
495
                private $processInstance;
496
497
                public function __construct($processInstance)
498
                {
499
                    $this->processInstance = $processInstance;
500
                }
501
502
                public function createHistoryEvent(HistoryEventProducer $producer): HistoryEvent
503
                {
504
                    return $producer->createProcessInstanceStartEvt($this->processInstance);
505
                }
506
            });
507
        }
508
    }
509
510
    /**
511
     * Method used for destroying a scope in a way that the execution can be
512
     * removed afterwards.
513
     */
514
    public function destroy(): void
515
    {
516
        $this->ensureParentInitialized();
517
518
        // execute Output Mappings (if they exist).
519
        $this->ensureActivityInitialized();
520
        if ($this->activity !== null && $this->activity->getIoMapping() !== null && !$this->skipIoMapping) {
521
            $this->activity->getIoMapping()->executeOutputParameters($this);
522
        }
523
524
        $this->clearExecution();
525
526
        parent::destroy();
527
528
        $this->removeEventSubscriptionsExceptCompensation();
529
    }
530
531
    public function removeAllTasks(): void
532
    {
533
        // delete all the tasks
534
        $this->removeTasks(null);
535
536
        // delete external tasks
537
        $this->removeExternalTasks();
538
    }
539
540
    protected function clearExecution(): void
541
    {
542
        //call the onRemove method of the execution observers
543
        //so they can do some clean up before
544
        foreach ($this->executionObservers as $observer) {
545
            $observer->onClear($this);
546
        }
547
548
        // delete all the tasks and external tasks
549
        $this->removeAllTasks();
550
551
        // delete all the variable instances
552
        $this->removeVariablesLocalInternal();
553
554
        // remove all jobs
555
        $this->removeJobs();
556
557
        // remove all incidents
558
        $this->removeIncidents();
559
    }
560
561
    public function removeVariablesLocalInternal(): void
562
    {
563
        foreach ($this->variableStore->getVariables() as $variableInstance) {
564
            $this->invokeVariableLifecycleListenersDelete(
565
                $variableInstance,
566
                $this,
567
                [$this->getVariablePersistenceListener()]
568
            );
569
            $this->removeVariableInternal($variableInstance);
570
        }
571
    }
572
573
    public function interrupt(string $reason, bool $skipCustomListeners, bool $skipIoMappings, bool $externallyTerminated): void
574
    {
575
576
        // remove Jobs
577
        if ($this->preserveScope) {
578
            $this->removeActivityJobs($reason);
579
        } else {
580
            $this->removeJobs();
581
            $this->removeEventSubscriptionsExceptCompensation();
582
        }
583
584
        $this->removeTasks($reason);
585
586
        parent::interrupt($reason, $skipCustomListeners, $skipIoMappings, $externallyTerminated);
587
    }
588
589
    protected function removeActivityJobs(string $reason): void
590
    {
591
        if ($this->activityId !== null) {
592
            foreach ($this->getJobs() as $job) {
593
                if ($this->activityId == $job->getActivityId()) {
594
                    $job->delete();
595
                    $this->removeJob($job);
596
                }
597
            }
598
        }
599
    }
600
601
    // methods that translate to operations /////////////////////////////////////
602
603
    public function performOperation($operation): void
604
    {
605
        if ($operation instanceof AtomicOperation) {
606
            $async = !$this->isIgnoreAsync() && $this->executionOperation->isAsync($this);
607
608
            if (!$async && $this->requiresUnsuspendedExecution($this->executionOperation)) {
609
                $this->ensureNotSuspended();
610
            }
611
612
            Context::getCommandInvocationContext()
613
            ->performOperation($this->executionOperation, $this, $async);
614
        } else {
615
            parent::performOperation($operation);
616
        }
617
    }
618
619
    public function performOperationSync($operation): void
620
    {
621
        if ($operation instanceof AtomicOperation) {
622
            if ($this->requiresUnsuspendedExecution($this->executionOperation)) {
623
                $this->ensureNotSuspended();
624
            }
625
            Context::getCommandInvocationContext()->performOperation($this->executionOperation, $this);
626
        } else {
627
            parent::performOperationSync($operation);
628
        }
629
    }
630
631
    protected function ensureNotSuspended(): void
632
    {
633
        if ($this->isSuspended()) {
634
            //throw LOG.suspendedEntityException("Execution", id);
635
            throw new \Exception("Execution");
636
        }
637
    }
638
639
    protected function requiresUnsuspendedExecution(AtomicOperation $executionOperation): bool
640
    {
641
        if (
642
            $executionOperation != AtomicOperation::trasitionDestroyScope()
0 ignored issues
show
Bug introduced by
The method trasitionDestroyScope() does not exist on Jabe\Impl\Pvm\Runtime\AtomicOperation. Did you maybe mean transitionDestroyScope()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

642
            $executionOperation != AtomicOperation::/** @scrutinizer ignore-call */ trasitionDestroyScope()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
643
            && $executionOperation != AtomicOperation::transitionNotifyListenerTake()
644
            && $executionOperation != AtomicOperation::transitionNotifyListenerEnd()
645
            && $executionOperation != AtomicOperation::transitionCreateScope()
646
            && $executionOperation != AtomicOperation::transitionNotifyListenerStart()
647
            && $executionOperation != AtomicOperation::deleteCascade()
648
            && $executionOperation != AtomicOperation::deleteCascadeFireActivityEnd()
649
        ) {
650
            return true;
651
        }
652
653
        return false;
654
    }
655
656
    public function scheduleAtomicOperationAsync(AtomicOperationInvocation $executionOperationInvocation): void
657
    {
658
        $messageJobDeclaration = null;
659
660
        $messageJobDeclarations = $this->getActivity()->getProperty(BpmnParse::PROPERTYNAME_MESSAGE_JOB_DECLARATION);
661
        if (!empty($messageJobDeclarations)) {
662
            foreach ($messageJobDeclarations as $declaration) {
663
                if ($declaration->isApplicableForOperation($executionOperationInvocation->getOperation())) {
664
                    $messageJobDeclaration = $declaration;
665
                    break;
666
                }
667
            }
668
        }
669
670
        if ($messageJobDeclaration !== null) {
671
            $message = $messageJobDeclaration->createJobInstance($executionOperationInvocation);
672
            Context::getCommandContext()->getJobManager()->send($message);
673
        } else {
674
            //throw LOG.requiredAsyncContinuationException($this->getActivity()->getId());
675
        }
676
    }
677
678
    public function isActive(string $activityId): bool
679
    {
680
        return $this->findExecution($activityId) !== null;
681
    }
682
683
    public function inactivate(): void
684
    {
685
        $this->isActive = false;
686
    }
687
688
    // executions ///////////////////////////////////////////////////////////////
689
690
    public function addExecutionObserver(ExecutionObserverInterface $observer): void
691
    {
692
        $this->executionObservers[$observer];
693
    }
694
695
    public function removeExecutionObserver(ExecutionObserverInterface $observer): void
696
    {
697
        foreach ($this->executionObservers as $key => $value) {
698
            if ($value == $observer) {
699
                unset($this->executionObservers[$key]);
700
            }
701
        }
702
    }
703
704
    public function getExecutions(): array
705
    {
706
        $this->ensureExecutionsInitialized();
707
        return $this->executions;
708
    }
709
710
    public function getExecutionsAsCopy(): array
711
    {
712
        return $this->getExecutions();
713
    }
714
715
    protected function ensureExecutionsInitialized(): void
716
    {
717
        if (empty($this->executions)) {
718
            if ($this->isExecutionTreePrefetchEnabled()) {
719
                $this->ensureExecutionTreeInitialized();
720
            } else {
721
                $this->executions = Context::getCommandContext()->getExecutionManager()->findChildExecutionsByParentExecutionId($this->id);
722
            }
723
        }
724
    }
725
726
    /**
727
     * @return bool true if execution tree prefetching is enabled
728
     */
729
    protected function isExecutionTreePrefetchEnabled(): bool
730
    {
731
        return Context::getProcessEngineConfiguration()->isExecutionTreePrefetchEnabled();
0 ignored issues
show
Bug introduced by
The method isExecutionTreePrefetchEnabled() does not exist on Jabe\Impl\Cfg\ProcessEngineConfigurationImpl. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

731
        return Context::getProcessEngineConfiguration()->/** @scrutinizer ignore-call */ isExecutionTreePrefetchEnabled();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
732
    }
733
734
    public function setExecutions(array $executions): void
735
    {
736
        $this->executions = $executions;
737
    }
738
739
    // bussiness key ////////////////////////////////////////////////////////////
740
741
    public function getProcessBusinessKey(): ?string
742
    {
743
        return $this->getProcessInstance()->getBusinessKey();
744
    }
745
746
    // process definition ///////////////////////////////////////////////////////
747
748
    /** ensures initialization and returns the process definition. */
749
    public function getProcessDefinition(): ProcessDefinitionEntity
750
    {
751
        $this->ensureProcessDefinitionInitialized();
752
        return $this->processDefinition;
753
    }
754
755
    public function setProcessDefinitionId(string $processDefinitionId): void
756
    {
757
        $this->processDefinitionId = $processDefinitionId;
758
    }
759
760
    public function getProcessDefinitionId(): string
761
    {
762
        return $this->processDefinitionId;
763
    }
764
765
    /**
766
     * for setting the process definition, this setter must be used as subclasses
767
     * can override
768
     */
769
    protected function ensureProcessDefinitionInitialized(): void
770
    {
771
        if (($this->processDefinition === null) && ($this->processDefinitionId !== null)) {
772
            $deployedProcessDefinition = Context::getProcessEngineConfiguration()->getDeploymentCache()
0 ignored issues
show
Bug introduced by
The method getDeploymentCache() does not exist on Jabe\Impl\Cfg\ProcessEngineConfigurationImpl. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

772
            $deployedProcessDefinition = Context::getProcessEngineConfiguration()->/** @scrutinizer ignore-call */ getDeploymentCache()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
773
                ->findDeployedProcessDefinitionById($this->processDefinitionId);
774
            $this->setProcessDefinition($deployedProcessDefinition);
775
        }
776
    }
777
778
    public function setProcessDefinition(?ProcessDefinitionImpl $processDefinition): void
779
    {
780
        $this->processDefinition = $processDefinition;
781
        if ($processDefinition !== null) {
782
            $this->processDefinitionId = $processDefinition->getId();
783
        } else {
784
            $this->processDefinitionId = null;
785
        }
786
    }
787
788
    // process instance /////////////////////////////////////////////////////////
789
790
    /** ensures initialization and returns the process instance. */
791
    public function getProcessInstance(): ExecutionEntity
792
    {
793
        $this->ensureProcessInstanceInitialized();
794
        return $this->processInstance;
795
    }
796
797
    protected function ensureProcessInstanceInitialized(): void
798
    {
799
        if (($this->processInstance === null) && ($this->processInstanceId !== null)) {
800
            if ($this->id == $this->processInstanceId) {
801
                $this->processInstance = $this;
802
            } else {
803
                if ($this->isExecutionTreePrefetchEnabled()) {
804
                    $this->ensureExecutionTreeInitialized();
805
                } else {
806
                    $this->processInstance = Context::getCommandContext()->getExecutionManager()->findExecutionById($this->processInstanceId);
807
                }
808
            }
809
        }
810
    }
811
812
    public function setProcessInstance(PvmExecutionImpl $processInstance): void
813
    {
814
        $this->processInstance = $processInstance;
815
        if ($processInstance !== null) {
816
            $this->processInstanceId = $this->processInstance->getId();
817
        }
818
    }
819
820
    public function isProcessInstanceExecution(): bool
821
    {
822
        return $this->parentId === null;
823
    }
824
825
    public function isProcessInstanceStarting(): bool
826
    {
827
        // the process instance can only be starting if it is currently in main-memory already
828
        // we never have to access the database
829
        return $this->processInstance !== null && $this->processInstance->isStarting;
830
    }
831
832
    // activity /////////////////////////////////////////////////////////////////
833
834
    /** ensures initialization and returns the activity */
835
    public function getActivity(): ActivityImpl
836
    {
837
        $this->ensureActivityInitialized();
838
        return parent::getActivity();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::getActivity() could return the type null which is incompatible with the type-hinted return Jabe\Impl\Pvm\Process\ActivityImpl. Consider adding an additional type-check to rule them out.
Loading history...
839
    }
840
841
    public function getActivityId(): string
842
    {
843
        return $this->activityId;
844
    }
845
846
    /** must be called before the activity member field or getActivity() is called */
847
    protected function ensureActivityInitialized(): void
848
    {
849
        if (($this->activity === null) && ($this->activityId !== null)) {
850
            $this->setActivity($this->getProcessDefinition()->findActivity($this->activityId));
851
        }
852
    }
853
854
    public function setActivity(?PvmActivityInterface $activity = null): void
855
    {
856
        parent::setActivity($activity);
857
        if ($activity !== null) {
858
            $this->activityId = $activity->getId();
859
            $this->activityName = $activity->getProperty("name");
860
        } else {
861
            $this->activityId = null;
862
            $this->activityName = null;
863
        }
864
    }
865
866
    /**
867
     * generates an activity instance id
868
     */
869
    protected function generateActivityInstanceId(string $activityId): string
870
    {
871
872
        if ($activityId == $this->processDefinitionId) {
873
            return $this->processInstanceId;
874
        } else {
875
            $nextId = Context::getProcessEngineConfiguration()->getIdGenerator()->getNextId();
0 ignored issues
show
Bug introduced by
The method getIdGenerator() does not exist on Jabe\Impl\Cfg\ProcessEngineConfigurationImpl. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

875
            $nextId = Context::getProcessEngineConfiguration()->/** @scrutinizer ignore-call */ getIdGenerator()->getNextId();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
876
877
            $compositeId = $activityId . ":" . $nextId;
878
            if (count($compositeId) > 64) {
0 ignored issues
show
Bug introduced by
$compositeId of type string is incompatible with the type Countable|array expected by parameter $value of count(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

878
            if (count(/** @scrutinizer ignore-type */ $compositeId) > 64) {
Loading history...
879
                return $nextId;
880
            } else {
881
                return $compositeId;
882
            }
883
        }
884
    }
885
886
    // parent ///////////////////////////////////////////////////////////////////
887
888
    /** ensures initialization and returns the parent */
889
    public function getParent(): ?ExecutionEntity
890
    {
891
        $this->ensureParentInitialized();
892
        return $this->parent;
893
    }
894
895
    protected function ensureParentInitialized(): void
896
    {
897
        if ($this->parent === null && $this->parentId !== null) {
898
            if ($this->isExecutionTreePrefetchEnabled()) {
899
                $this->ensureExecutionTreeInitialized();
900
            } else {
901
                $this->parent = Context::getCommandContext()->getExecutionManager()->findExecutionById($this->parentId);
902
            }
903
        }
904
    }
905
906
    public function setParentExecution(PvmExecutionImpl $parent): void
907
    {
908
        $this->parent = $parent;
909
        if ($parent !== null) {
910
            $this->parentId = $parent->getId();
911
        } else {
912
            $this->parentId = null;
913
        }
914
    }
915
916
    // super- and subprocess executions /////////////////////////////////////////
917
918
    public function getSuperExecutionId(): ?string
919
    {
920
        return $this->superExecutionId;
921
    }
922
923
    public function getSuperExecution(): ?ExecutionEntity
924
    {
925
        $this->ensureSuperExecutionInitialized();
926
        return $this->superExecution;
927
    }
928
929
    public function setSuperExecution(PvmExecutionImpl $superExecution): void
930
    {
931
        if ($this->superExecutionId !== null) {
932
            $this->ensureSuperExecutionInitialized();
933
            $this->superExecution->setSubProcessInstance(null);
934
        }
935
936
        $this->superExecution = $superExecution;
937
938
        if ($superExecution !== null) {
939
            $this->superExecutionId = $superExecution->getId();
940
            $this->superExecution->setSubProcessInstance($this);
941
        } else {
942
            $this->superExecutionId = null;
943
        }
944
    }
945
946
    protected function ensureSuperExecutionInitialized(): void
947
    {
948
        if ($this->superExecution === null && $this->superExecutionId !== null) {
949
            $this->superExecution = Context::getCommandContext()->getExecutionManager()->findExecutionById($this->superExecutionId);
950
        }
951
    }
952
953
    public function getSubProcessInstance(): ?ExecutionEntity
954
    {
955
        $this->ensureSubProcessInstanceInitialized();
956
        return $this->subProcessInstance;
957
    }
958
959
    public function setSubProcessInstance(PvmExecutionImpl $subProcessInstance): void
960
    {
961
        $this->shouldQueryForSubprocessInstance = $subProcessInstance !== null;
962
        $this->subProcessInstance = $subProcessInstance;
963
    }
964
965
    protected function ensureSubProcessInstanceInitialized(): void
966
    {
967
        if ($this->shouldQueryForSubprocessInstance && $this->subProcessInstance === null) {
968
            $this->subProcessInstance = Context::getCommandContext()->getExecutionManager()->findSubProcessInstanceBySuperExecutionId($this->id);
969
        }
970
    }
971
972
    // super case executions ///////////////////////////////////////////////////
973
974
    /*public function getSuperCaseExecutionId(): ?string
975
    {
976
        return superCaseExecutionId;
977
    }
978
979
    public void setSuperCaseExecutionId(String superCaseExecutionId) {
980
        $this->superCaseExecutionId = superCaseExecutionId;
981
    }
982
983
    @Override
984
    public CaseExecutionEntity getSuperCaseExecution() {
985
        ensureSuperCaseExecutionInitialized();
986
        return superCaseExecution;
987
    }
988
989
    @Override
990
    public void setSuperCaseExecution(CmmnExecution superCaseExecution) {
991
        $this->superCaseExecution = (CaseExecutionEntity) superCaseExecution;
992
993
        if (superCaseExecution !== null) {
994
        $this->superCaseExecutionId = superCaseExecution->getId();
995
        $this->caseInstanceId = superCaseExecution->getCaseInstanceId();
996
        } else {
997
        $this->superCaseExecutionId = null;
998
        $this->caseInstanceId = null;
999
        }
1000
    }
1001
1002
    protected void ensureSuperCaseExecutionInitialized() {
1003
        if (superCaseExecution === null && superCaseExecutionId !== null) {
1004
        superCaseExecution = Context::getCommandContext()->getCaseExecutionManager().findCaseExecutionById(superCaseExecutionId);
1005
        }
1006
    }
1007
1008
    // sub case execution //////////////////////////////////////////////////////
1009
1010
    @Override
1011
    public CaseExecutionEntity getSubCaseInstance() {
1012
        ensureSubCaseInstanceInitialized();
1013
        return subCaseInstance;
1014
1015
    }
1016
1017
    @Override
1018
    public void setSubCaseInstance(CmmnExecution subCaseInstance) {
1019
        shouldQueryForSubCaseInstance = subCaseInstance !== null;
1020
        $this->subCaseInstance = (CaseExecutionEntity) subCaseInstance;
1021
    }
1022
1023
    protected void ensureSubCaseInstanceInitialized() {
1024
        if (shouldQueryForSubCaseInstance && subCaseInstance === null) {
1025
        subCaseInstance = Context::getCommandContext()->getCaseExecutionManager().findSubCaseInstanceBySuperExecutionId(id);
1026
        }
1027
    }*/
1028
1029
    // customized persistence behavior /////////////////////////////////////////
1030
1031
    public function remove(): void
1032
    {
1033
        parent::remove();
1034
1035
        // removes jobs, incidents and tasks, and
1036
        // clears the variable store
1037
        $this->clearExecution();
1038
1039
        // remove all event subscriptions for this scope, if the scope has event
1040
        // subscriptions:
1041
        $this->removeEventSubscriptions();
1042
1043
        // finally delete this execution
1044
        Context::getCommandContext()->getExecutionManager()->deleteExecution($this);
1045
    }
1046
1047
    protected function removeEventSubscriptionsExceptCompensation(): void
1048
    {
1049
        // remove event subscriptions which are not compensate event subscriptions
1050
        $eventSubscriptions = $this->getEventSubscriptions();
1051
        foreach ($eventSubscriptions as $eventSubscriptionEntity) {
1052
            if (!EventType::compensate()->name() == $eventSubscriptionEntity->getEventType()) {
1053
                $eventSubscriptionEntity->delete();
1054
            }
1055
        }
1056
    }
1057
1058
    public function removeEventSubscriptions(): void
1059
    {
1060
        foreach ($this->getEventSubscriptions() as $eventSubscription) {
1061
            if ($this->getReplacedBy() !== null) {
1062
                $eventSubscription->setExecution($this->getReplacedBy());
1063
            } else {
1064
                $eventSubscription->delete();
1065
            }
1066
        }
1067
    }
1068
1069
    private function removeJobs(): void
1070
    {
1071
        foreach ($this->getJobs() as $job) {
1072
            if ($this->isReplacedByParent()) {
1073
                $job->setExecution($this->getReplacedBy());
1074
            } else {
1075
                $job->delete();
1076
            }
1077
        }
1078
    }
1079
1080
    private function removeIncidents(): void
1081
    {
1082
        foreach ($this->getIncidents() as $incident) {
1083
            if ($this->isReplacedByParent()) {
1084
                $incident->setExecution($this->getReplacedBy());
1085
            } else {
1086
                $incidentContext = $this->createIncidentContext($incident->getConfiguration());
1087
                IncidentHandling::removeIncidents($incident->getIncidentType(), $incidentContext, false);
1088
            }
1089
        }
1090
1091
        foreach ($this->getIncidents() as $incident) {
1092
            // if the handler doesn't take care of it,
1093
            // make sure the incident is deleted nevertheless
1094
            $incident->delete();
1095
        }
1096
    }
1097
1098
    protected function removeTasks(?string $reason): void
1099
    {
1100
        if ($reason === null) {
1101
            $reason = TaskEntity::DELETE_REASON_DELETED;
1102
        }
1103
        foreach ($this->getTasks() as $task) {
1104
            if ($this->isReplacedByParent()) {
1105
                if ($task->getExecution() === null || $task->getExecution() != $this->replacedBy) {
1106
                    // All tasks should have been moved when "replacedBy" has been set.
1107
                    // Just in case tasks where added,
1108
                    // wo do an additional check here and move it
1109
                    $task->setExecution($this->replacedBy);
1110
                    $this->getReplacedBy()->addTask($task);
1111
                }
1112
            } else {
1113
                $task->delete($reason, false, $this->skipCustomListeners);
1114
            }
1115
        }
1116
    }
1117
1118
    protected function removeExternalTasks(): void
1119
    {
1120
        foreach ($this->getExternalTasks() as $externalTask) {
1121
            $externalTask->delete();
1122
        }
1123
    }
1124
1125
    public function getReplacedBy(): ?ExecutionEntity
1126
    {
1127
        return $this->replacedBy;
1128
    }
1129
1130
    public function resolveReplacedBy(): ?ExecutionEntity
1131
    {
1132
        return parent::resolveReplacedBy();
1133
    }
1134
1135
    public function replace(PvmExecutionImpl $execution): void
1136
    {
1137
        $replacedExecution = $execution;
1138
1139
        $this->setListenerIndex($replacedExecution->getListenerIndex());
1140
        $replacedExecution->setListenerIndex(0);
1141
1142
        // update the related tasks
1143
        $replacedExecution->moveTasksTo($this);
1144
1145
        $replacedExecution->moveExternalTasksTo($this);
1146
1147
        // update those jobs that are directly related to the argument execution's
1148
        // current activity
1149
        $replacedExecution->moveActivityLocalJobsTo($this);
1150
1151
        if (!$replacedExecution->isEnded()) {
1152
            // on compaction, move all variables
1153
            if ($replacedExecution->getParent() == $this) {
1154
                $replacedExecution->moveVariablesTo($this);
1155
            } else {
1156
                $replacedExecution->moveConcurrentLocalVariablesTo($this);
1157
            }
1158
        }
1159
1160
        // note: this method not move any event subscriptions since concurrent
1161
        // executions
1162
        // do not have event subscriptions (and either one of the executions
1163
        // involved in this
1164
        // operation is concurrent)
1165
1166
        parent::replace($replacedExecution);
1167
    }
1168
1169
    public function onConcurrentExpand(PvmExecutionImpl $scopeExecution): void
1170
    {
1171
        $scopeExecutionEntity = $scopeExecution;
1172
        $scopeExecutionEntity->moveConcurrentLocalVariablesTo($this);
1173
        parent::onConcurrentExpand($scopeExecutionEntity);
1174
    }
1175
1176
    protected function moveTasksTo(ExecutionEntity $other): void
1177
    {
1178
        // update the related tasks
1179
        foreach ($this->getTasksInternal() as $task) {
1180
            $task->setExecution($other);
1181
1182
            // update the related local task variables
1183
            $variables = $task->getVariablesInternal();
1184
1185
            foreach ($variables as $variable) {
1186
                $variable->setExecution($other);
1187
            }
1188
1189
            $other->addTask($task);
1190
        }
1191
        $this->tasks = [];
1192
    }
1193
1194
    protected function moveExternalTasksTo(ExecutionEntity $other): void
1195
    {
1196
        foreach ($this->getExternalTasksInternal() as $externalTask) {
1197
            $externalTask->setExecutionId($other->getId());
1198
            $externalTask->setExecution($other);
1199
1200
            $other->addExternalTask(externalTask);
0 ignored issues
show
Bug introduced by
The constant Jabe\Impl\Persistence\Entity\externalTask was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1201
        }
1202
1203
        $this->externalTasks = [];
1204
    }
1205
1206
    protected function moveActivityLocalJobsTo(ExecutionEntity $other): void
1207
    {
1208
        if ($this->activityId !== null) {
1209
            foreach ($this->getJobs() as $job) {
1210
                if ($this->activityId == $job->getActivityId()) {
1211
                    $this->removeJob($job);
1212
                    $job->setExecution($other);
1213
                }
1214
            }
1215
        }
1216
    }
1217
1218
    protected function moveVariablesTo(ExecutionEntity $other): void
1219
    {
1220
        $variables = $this->variableStore->getVariables();
1221
        $this->variableStore->removeVariables();
1222
1223
        foreach ($variables as $variable) {
1224
            $this->moveVariableTo($variable, $other);
1225
        }
1226
    }
1227
1228
    protected function moveVariableTo(VariableInstanceEntity $variable, ExecutionEntity $other): void
1229
    {
1230
        if ($other->variableStore->containsKey($variable->getName())) {
1231
            $existingInstance = $other->variableStore->getVariable($variable->getName());
1232
            $existingInstance->setValue($variable->getTypedValue(false));
1233
            $this->invokeVariableLifecycleListenersUpdate($existingInstance, $this);
1234
            $this->invokeVariableLifecycleListenersDelete(
1235
                $variable,
1236
                $this,
1237
                [$this->getVariablePersistenceListener()]
1238
            );
1239
        } else {
1240
            $other->variableStore->addVariable($variable);
1241
        }
1242
    }
1243
1244
    protected function moveConcurrentLocalVariablesTo(ExecutionEntity $other): void
1245
    {
1246
        $variables = $this->variableStore->getVariables();
1247
1248
        foreach ($variables as $variable) {
1249
            if ($variable->isConcurrentLocal()) {
1250
                $this->moveVariableTo($variable, $other);
1251
            }
1252
        }
1253
    }
1254
1255
    // variables ////////////////////////////////////////////////////////////////
1256
1257
    public function addVariableListener(VariableInstanceLifecycleListenerInterface $listener): void
1258
    {
1259
        $this->registeredVariableListeners[] = $listener;
1260
    }
1261
1262
    public function removeVariableListener(VariableInstanceLifecycleListenerInterface $listener): void
1263
    {
1264
        foreach ($this->registeredVariableListeners as $key => $value) {
1265
            if ($value == $listener) {
1266
                unset($this->registeredVariableListeners[$key]);
1267
            }
1268
        }
1269
    }
1270
1271
    public function isExecutingScopeLeafActivity(): bool
1272
    {
1273
        return $this->isActive && $this->getActivity() !== null && $this->getActivity()->isScope() && $this->activityInstanceId !== null
1274
            && !($this->getActivity()->getActivityBehavior() instanceof CompositeActivityBehaviorInterface);
1275
    }
1276
1277
    public function provideVariables(?array $variableNames = []): array
1278
    {
1279
        if (!empty($variableNames)) {
1280
            return Context::getCommandContext()->getVariableInstanceManager()->findVariableInstancesByExecutionIdAndVariableNames($this->id, $variableNames);
1281
        }
1282
        return Context::getCommandContext()->getVariableInstanceManager()->findVariableInstancesByExecutionId($this->id);
1283
    }
1284
1285
    /**
1286
     * Fetch all the executions inside the same process instance as list and then
1287
     * reconstruct the complete execution tree.
1288
     *
1289
     * In many cases this is an optimization over fetching the execution tree
1290
     * lazily. Usually we need all executions anyway and it is preferable to fetch
1291
     * more data in a single query (maybe even too much data) then to run multiple
1292
     * queries, each returning a fraction of the data.
1293
     *
1294
     * The most important consideration here is network roundtrip: If the process
1295
     * engine and database run on separate hosts, network roundtrip has to be
1296
     * added to each query. Economizing on the number of queries economizes on
1297
     * network roundtrip. The tradeoff here is network roundtrip vs. throughput:
1298
     * multiple roundtrips carrying small chucks of data vs. a single roundtrip
1299
     * carrying more data.
1300
     *
1301
     */
1302
    protected function ensureExecutionTreeInitialized(): void
1303
    {
1304
        $executions = Context::getCommandContext()
1305
        ->getExecutionManager()
1306
        ->findExecutionsByProcessInstanceId($this->processInstanceId);
1307
1308
        $processInstance = $this->isProcessInstanceExecution() ? $this : null;
1309
1310
        if ($processInstance === null) {
1311
            foreach ($executions as $execution) {
1312
                if ($execution->isProcessInstanceExecution()) {
1313
                    $processInstance = $execution;
1314
                }
1315
            }
1316
        }
1317
1318
        $processInstance->restoreProcessInstance($executions, null, null, null, null, null, null);
1319
    }
1320
1321
    /**
1322
     * Restores a complete process instance tree including referenced entities.
1323
     *
1324
     * @param executions
1325
     *   the list of all executions that are part of this process instance.
1326
     *   Cannot be null, must include the process instance execution itself.
1327
     * @param eventSubscriptions
1328
     *   the list of all event subscriptions that are linked to executions which is part of this process instance
1329
     *   If null, event subscriptions are not initialized and lazy loaded on demand
1330
     * @param variables
1331
     *   the list of all variables that are linked to executions which are part of this process instance
1332
     *   If null, variables are not initialized and are lazy loaded on demand
1333
     * @param jobs
1334
     * @param tasks
1335
     * @param incidents
1336
     */
1337
    public function restoreProcessInstance(
1338
        array $executions,
1339
        ?array $eventSubscriptions,
1340
        ?array $variables,
1341
        ?array $tasks,
1342
        ?array $jobs,
1343
        ?array $incidents,
1344
        ?array $externalTasks
1345
    ): void {
1346
1347
        if (!$this->isProcessInstanceExecution()) {
1348
            //throw LOG.restoreProcessInstanceException($this);
1349
        }
1350
1351
        // index executions by id
1352
        $executionsMap = [];
1353
        foreach ($executions as $execution) {
1354
            $executionsMap[$execution->getId()] = $execution;
1355
        }
1356
1357
        $variablesByScope = [];
1358
        if ($variables !== null) {
0 ignored issues
show
introduced by
The condition $variables !== null is always true.
Loading history...
1359
            foreach ($variables as $variable) {
1360
                CollectionUtil::addToMapOfLists($variablesByScope, $variable->getVariableScopeId(), $variable);
1361
            }
1362
        }
1363
1364
        // restore execution tree
1365
        foreach ($executions as $execution) {
1366
            if (empty($execution->executions)) {
1367
                $execution->executions = [];
1368
            }
1369
            if (empty($execution->eventSubscriptions) && $eventSubscriptions !== null) {
1370
                $execution->eventSubscriptions = [];
1371
            }
1372
            if ($variables !== null) {
1373
                $execution->variableStore->setVariablesProvider(
1374
                    new VariableCollectionProvider($variablesByScope->get($execution->id))
1375
                );
1376
            }
1377
            $parentId = $execution->getParentId();
1378
            $parent = null;
1379
            if (array_key_exists($parentId, $executionsMap)) {
1380
                $parent = $executionsMap[$parentId];
1381
            }
1382
            if (!$execution->isProcessInstanceExecution()) {
1383
                if ($parent === null) {
1384
                    //throw LOG.resolveParentOfExecutionFailedException(parentId, execution->getId());
1385
                    throw new \Exception("Execution");
1386
                }
1387
                $execution->processInstance = $this;
1388
                $execution->parent = $parent;
1389
                if ($parent->executions === null) {
1390
                    $parent->executions = [];
1391
                }
1392
                $parent->executions[] = $execution;
1393
            } else {
1394
                $execution->processInstance = $execution;
1395
            }
1396
        }
1397
1398
        if ($eventSubscriptions !== null) {
0 ignored issues
show
introduced by
The condition $eventSubscriptions !== null is always true.
Loading history...
1399
            // add event subscriptions to the right executions in the tree
1400
            foreach ($eventSubscriptions as $eventSubscription) {
1401
                $executionEntity = null;
1402
                if (array_key_exists($eventSubscription->getExecutionId(), $executionsMap)) {
1403
                    $executionEntity = $executionsMap[$eventSubscription->getExecutionId()];
1404
                }
1405
                if ($executionEntity !== null) {
1406
                    $executionEntity->addEventSubscription($eventSubscription);
1407
                } else {
1408
                    //throw LOG.executionNotFoundException(eventSubscription->getExecutionId());
1409
                    throw new \Exception("Execution");
1410
                }
1411
            }
1412
        }
1413
1414
        if ($jobs !== null) {
0 ignored issues
show
introduced by
The condition $jobs !== null is always true.
Loading history...
1415
            foreach ($jobs as $job) {
1416
                $execution = null;
1417
                if (array_key_exists($job->getExecutionId(), $executionsMap)) {
1418
                    $execution = $executionsMap[$job->getExecutionId()];
1419
                }
1420
                $job->setExecution($execution);
1421
            }
1422
        }
1423
1424
        if ($tasks !== null) {
0 ignored issues
show
introduced by
The condition $tasks !== null is always true.
Loading history...
1425
            foreach ($tasks as $task) {
1426
                $execution = null;
1427
                if (array_key_exists($task->getExecutionId(), $executionsMap)) {
1428
                    $execution = $executionsMap[$task->getExecutionId()];
1429
                }
1430
                $task->setExecution($execution);
1431
                $execution->addTask($task);
1432
1433
                if ($variables !== null) {
1434
                    $task->variableStore->setVariablesProvider(new VariableCollectionProvider($variablesByScope->get($task->id)));
1435
                }
1436
            }
1437
        }
1438
1439
1440
        if ($incidents !== null) {
0 ignored issues
show
introduced by
The condition $incidents !== null is always true.
Loading history...
1441
            foreach ($incidents as $incident) {
1442
                $execution = null;
1443
                if (array_key_exists($incident->getExecutionId(), $executionsMap)) {
1444
                    $execution = $executionsMap[$incident->getExecutionId()];
1445
                }
1446
                $incident->setExecution($execution);
1447
            }
1448
        }
1449
1450
        if ($externalTasks !== null) {
0 ignored issues
show
introduced by
The condition $externalTasks !== null is always true.
Loading history...
1451
            foreach ($externalTasks as $externalTask) {
1452
                $execution = null;
1453
                if (array_key_exists($externalTask->getExecutionId(), $executionsMap)) {
1454
                    $execution = $executionsMap[$externalTask->getExecutionId()];
1455
                }
1456
                $externalTask->setExecution($execution);
1457
                $execution->addExternalTask($externalTask);
1458
            }
1459
        }
1460
    }
1461
1462
1463
    // persistent state /////////////////////////////////////////////////////////
1464
1465
    public function getPersistentState()
1466
    {
1467
        $persistentState = [];
1468
        $persistentState["processDefinitionId"] = $this->processDefinitionId;
1469
        $persistentState["businessKey"] = $this->businessKey;
1470
        $persistentState["activityId"] = $this->activityId;
1471
        $persistentState["activityInstanceId"] = $this->activityInstanceId;
1472
        $persistentState["isActive"] = $this->isActive;
1473
        $persistentState["isConcurrent"] = $this->isConcurrent;
1474
        $persistentState["isScope"] = $this->isScope;
1475
        $persistentState["isEventScope"] = $this->isEventScope;
1476
        $persistentState["parentId"] = $this->parentId;
1477
        $persistentState["superExecution"] = $this->superExecutionId;
1478
        $persistentState["superCaseExecutionId"] = $this->superCaseExecutionId;
1479
        $persistentState["caseInstanceId"] = $this->caseInstanceId;
1480
        $persistentState["suspensionState"] = $this->suspensionState;
1481
        $persistentState["cachedEntityState"] = $this->getCachedEntityState();
1482
        $persistentState["sequenceCounter"] = $this->getSequenceCounter();
1483
        return $persistentState;
1484
    }
1485
1486
    public function insert(): void
1487
    {
1488
        Context::getCommandContext()->getExecutionManager()->insertExecution($this);
1489
    }
1490
1491
    public function getRevisionNext(): int
1492
    {
1493
        return $this->revision + 1;
1494
    }
1495
1496
    public function forceUpdate(): void
1497
    {
1498
        Context::getCommandContext()->getDbEntityManager()->forceUpdate($this);
1499
    }
1500
1501
    // toString /////////////////////////////////////////////////////////////////
1502
1503
    public function __toString()
1504
    {
1505
        if ($this->isProcessInstanceExecution()) {
1506
            return "ProcessInstance[" . $this->getToStringIdentity() . "]";
1507
        } else {
1508
            return ($this->isConcurrent ? "Concurrent" : "") . ($this->isScope ? "Scope" : "") . "Execution[" . $this->getToStringIdentity() . "]";
1509
        }
1510
    }
1511
1512
    protected function getToStringIdentity(): string
1513
    {
1514
        return $this->id;
1515
    }
1516
1517
    // event subscription support //////////////////////////////////////////////
1518
1519
    public function getEventSubscriptionsInternal(): array
1520
    {
1521
        $this->ensureEventSubscriptionsInitialized();
1522
        return $this->eventSubscriptions;
1523
    }
1524
1525
    public function getEventSubscriptions(): array
1526
    {
1527
        return $this->getEventSubscriptionsInternal();
1528
    }
1529
1530
    public function getCompensateEventSubscriptions(?string $activityId = null): array
1531
    {
1532
        if ($activityId === null) {
1533
            $eventSubscriptions = $this->getEventSubscriptionsInternal();
1534
            $result = [];
1535
            foreach ($eventSubscriptions as $eventSubscriptionEntity) {
1536
                if ($eventSubscriptionEntity->isSubscriptionForEventType(EventType::compensate())) {
1537
                    $result[] = $eventSubscriptionEntity;
1538
                }
1539
            }
1540
            return $result;
1541
        } else {
1542
            $eventSubscriptions = $this->getEventSubscriptionsInternal();
1543
            $result = [];
1544
            foreach ($eventSubscriptions as $eventSubscriptionEntity) {
1545
                if (
1546
                    $eventSubscriptionEntity->isSubscriptionForEventType(EventType::compensate())
1547
                    && $this->activityId == $eventSubscriptionEntity->getActivityId()
1548
                ) {
1549
                    $result[] = $eventSubscriptionEntity;
1550
                }
1551
            }
1552
            return $result;
1553
        }
1554
    }
1555
1556
    protected function ensureEventSubscriptionsInitialized(): void
1557
    {
1558
        if (empty($this->eventSubscriptions)) {
1559
            $this->eventSubscriptions = Context::getCommandContext()->getEventSubscriptionManager()->findEventSubscriptionsByExecution($this->id);
1560
        }
1561
    }
1562
1563
    public function addEventSubscription(EventSubscriptionEntity $eventSubscriptionEntity): void
1564
    {
1565
        $eventSubscriptionsInternal = $this->getEventSubscriptionsInternal();
1566
        $exists = false;
1567
        foreach ($eventSubscriptionsInternal as $value) {
1568
            if ($value == $eventSubscriptionEntity) {
1569
                $exists = true;
1570
                break;
1571
            }
1572
        }
1573
        if (!$exists) {
1574
            $this->eventSubscriptions[] = $eventSubscriptionEntity;
1575
        }
1576
    }
1577
1578
    public function removeEventSubscription(EventSubscriptionEntity $eventSubscriptionEntity): void
1579
    {
1580
        foreach ($this->eventSubscriptions as $key => $value) {
1581
            if ($value == $eventSubscriptionEntity) {
1582
                unset($this->eventSubscriptions[$key]);
1583
            }
1584
        }
1585
    }
1586
1587
    // referenced job entities //////////////////////////////////////////////////
1588
1589
    protected function ensureJobsInitialized(): void
1590
    {
1591
        if ($this->jobs === null) {
1592
            $this->jobs = Context::getCommandContext()->getJobManager()->findJobsByExecutionId($this->id);
1593
        }
1594
    }
1595
1596
    protected function getJobsInternal(): array
1597
    {
1598
        $this->ensureJobsInitialized();
1599
        return $this->jobs;
1600
    }
1601
1602
    public function getJobs(): array
1603
    {
1604
        return $this->getJobsInternal();
1605
    }
1606
1607
    public function addJob(JobEntity $jobEntity): void
1608
    {
1609
        $jobsInternal = $this->getJobsInternal();
1610
        $exists = false;
1611
        foreach ($jobsInternal as $value) {
1612
            if ($value == $jobEntity) {
1613
                $exists = true;
1614
                break;
1615
            }
1616
        }
1617
        if (!$exists) {
1618
            $this->jobs[] = $jobEntity;
1619
        }
1620
    }
1621
1622
    public function removeJob(JobEntity $job): void
1623
    {
1624
        foreach ($this->jobs as $key => $value) {
1625
            if ($value == $job) {
1626
                unset($this->jobs[$key]);
1627
            }
1628
        }
1629
    }
1630
1631
    // referenced incidents entities
1632
    // //////////////////////////////////////////////
1633
1634
    protected function ensureIncidentsInitialized(): void
1635
    {
1636
        if ($this->incidents === null) {
1637
            $this->incidents = Context::getCommandContext()->getIncidentManager()->findIncidentsByExecution($this->id);
1638
        }
1639
    }
1640
1641
    protected function getIncidentsInternal(): array
1642
    {
1643
        $this->ensureIncidentsInitialized();
1644
        return $this->incidents;
1645
    }
1646
1647
    public function getIncidents(): array
1648
    {
1649
        return $this->getIncidentsInternal();
1650
    }
1651
1652
    public function addIncident(IncidentEntity $incident): void
1653
    {
1654
        $incidentsInternal = $this->getIncidentsInternal();
1655
        $exists = false;
1656
        foreach ($incidentsInternal as $value) {
1657
            if ($value == $incident) {
1658
                $exists = true;
1659
                break;
1660
            }
1661
        }
1662
        if (!$exists) {
1663
            $this->incidents[] = $incident;
1664
        }
1665
    }
1666
1667
    public function removeIncident(IncidentEntity $incident): void
1668
    {
1669
        foreach ($this->incidents as $key => $value) {
1670
            if ($value == $incident) {
1671
                unset($this->incidents[$key]);
1672
            }
1673
        }
1674
    }
1675
1676
    public function getIncidentByCauseIncidentId(string $causeIncidentId): ?IncidentEntity
1677
    {
1678
        foreach ($this->getIncidents() as $incident) {
1679
            if ($incident->getCauseIncidentId() !== null && $incident->getCauseIncidentId() == $causeIncidentId) {
1680
                return $incident;
1681
            }
1682
        }
1683
        return null;
1684
    }
1685
1686
    // referenced task entities
1687
    // ///////////////////////////////////////////////////
1688
1689
    protected function ensureTasksInitialized(): void
1690
    {
1691
        if ($this->tasks === null) {
1692
            $this->tasks = Context::getCommandContext()->getTaskManager()->findTasksByExecutionId($this->id);
1693
        }
1694
    }
1695
1696
    protected function getTasksInternal(): array
1697
    {
1698
        $this->ensureTasksInitialized();
1699
        return $this->tasks;
1700
    }
1701
1702
    public function getTasks(): array
1703
    {
1704
        return $this->getTasksInternal();
1705
    }
1706
1707
    public function addTask(TaskEntity $taskEntity): void
1708
    {
1709
        $tasksInternal = $this->getTasksInternal();
1710
        $exists = false;
1711
        foreach ($tasksInternal as $value) {
1712
            if ($value == $taskEntity) {
1713
                $exists = true;
1714
                break;
1715
            }
1716
        }
1717
        if (!$exists) {
1718
            $this->tasks[] = $taskEntity;
1719
        }
1720
    }
1721
1722
    public function removeTask(TaskEntity $task): void
1723
    {
1724
        foreach ($this->tasks as $key => $value) {
1725
            if ($value == $task) {
1726
                unset($this->tasks[$key]);
1727
            }
1728
        }
1729
    }
1730
1731
    // external tasks
1732
1733
    protected function ensureExternalTasksInitialized(): void
1734
    {
1735
        if ($this->externalTasks === null) {
1736
            $this->externalTasks = Context::getCommandContext()->getExternalTaskManager()->findExternalTasksByExecutionId($this->id);
1737
        }
1738
    }
1739
1740
    protected function getExternalTasksInternal(): array
1741
    {
1742
        $this->ensureExternalTasksInitialized();
1743
        return $this->externalTasks;
1744
    }
1745
1746
    public function addExternalTask(ExternalTaskEntity $externalTask): void
1747
    {
1748
        $externalTasks = $this->getExternalTasksInternal();
1749
        $exists = false;
1750
        foreach ($externalTasks as $value) {
1751
            if ($value == $externalTask) {
1752
                $exists = true;
1753
                break;
1754
            }
1755
        }
1756
        if (!$exists) {
1757
            $this->externalTasks[] = $externalTask;
1758
        }
1759
    }
1760
1761
    public function removeExternalTask(ExternalTaskEntity $externalTask): void
1762
    {
1763
        foreach ($this->externalTasks as $key => $value) {
1764
            if ($value == $externalTask) {
1765
                unset($this->externalTasks[$key]);
1766
            }
1767
        }
1768
    }
1769
1770
    public function getExternalTasks(): array
1771
    {
1772
        return $this->getExternalTasksInternal();
1773
    }
1774
1775
    // variables /////////////////////////////////////////////////////////
1776
1777
    protected function getVariableStore(): VariableStore
1778
    {
1779
        return $this->variableStore;
1780
    }
1781
1782
    protected function getVariableInstanceFactory(): VariableInstanceFactoryInterface
1783
    {
1784
        return VariableInstanceEntityFactory::instance();
1785
    }
1786
1787
    protected function getVariableInstanceLifecycleListeners(): array
1788
    {
1789
        $listeners = [];
1790
1791
        $listeners[] = $this->getVariablePersistenceListener();
1792
        $listeners[] = new VariableInstanceConcurrentLocalInitializer($this);
1793
        $listeners[] = VariableInstanceSequenceCounterListener::instance();
1794
1795
        $listeners[] = VariableInstanceHistoryListener::instance();
1796
1797
        $listeners[] = new VariableListenerInvocationListener($this);
1798
1799
        $listeners = array_merge($listeners, $this->registeredVariableListeners);
1800
1801
        return $listeners;
1802
    }
1803
1804
    public function getVariablePersistenceListener(): VariableInstanceLifecycleListenerInterface
1805
    {
1806
        return VariableInstanceEntityPersistenceListener::instance();
1807
    }
1808
1809
    public function getVariablesInternal(): array
1810
    {
1811
        return $this->variableStore->getVariables();
1812
    }
1813
1814
    public function removeVariableInternal(VariableInstanceEntity $variable): void
1815
    {
1816
        if ($this->variableStore->containsValue($variable)) {
1817
            $this->variableStore->removeVariable($variable->getName());
1818
        }
1819
    }
1820
1821
    public function addVariableInternal(VariableInstanceEntity $variable): void
1822
    {
1823
        if ($this->variableStore->containsKey($variable->getName())) {
1824
            $existingVariable = $this->variableStore->getVariable($variable->getName());
1825
            $existingVariable->setValue($variable->getTypedValue());
1826
            $variable->delete();
1827
        } else {
1828
            $this->variableStore->addVariable($variable);
1829
        }
1830
    }
1831
1832
    public function handleConditionalEventOnVariableChange(VariableEvent $variableEvent): void
1833
    {
1834
        $subScriptions = $this->getEventSubscriptions();
1835
        foreach ($subScriptions as $subscription) {
1836
            if (EventType::conditional()->name() == $subscription->getEventType()) {
1837
                $subscription->processEventSync($variableEvent);
1838
            }
1839
        }
1840
    }
1841
1842
    public function dispatchEvent(VariableEvent $variableEvent): void
1843
    {
1844
        $execs = [];
1845
        $scope = new \stdClass();
1846
        $scope->execs = $execs;
1847
        (new ExecutionTopDownWalker($this))->addPreVisitor(new class ($scope) implements TreeVisitorInterface {
1848
            private $scope;
1849
1850
            public function __construct($scope)
1851
            {
1852
                $this->scope = $scope;
1853
            }
1854
1855
            public function visit($obj): void
1856
            {
1857
                if (
1858
                    !empty($obj->getEventSubscriptions())
1859
                    && ($obj->isInState(ActivityInstanceState::default()) || (!$obj->getActivity()->isScope()))
1860
                ) { // state is default or tree is compacted
1861
                    $this->scope->execs[] = $obj;
1862
                }
1863
            }
1864
        })->walkUntil();
1865
        foreach ($scope->execs as $execution) {
1866
            $execution->handleConditionalEventOnVariableChange($variableEvent);
1867
        }
1868
    }
1869
1870
1871
1872
    // getters and setters //////////////////////////////////////////////////////
1873
1874
    public function setCachedEntityState(int $cachedEntityState): void
1875
    {
1876
        $this->cachedEntityState = $cachedEntityState;
1877
1878
        // Check for flags that are down. These lists can be safely initialized as
1879
        // empty, preventing
1880
        // additional queries that end up in an empty list anyway
1881
        if ($this->jobs === null && !BitMaskUtil::isBitOn($this->cachedEntityState, self::JOBS_STATE_BIT)) {
1882
            $this->jobs = [];
1883
        }
1884
        if ($this->tasks === null && !BitMaskUtil::isBitOn($this->cachedEntityState, self::TASKS_STATE_BIT)) {
1885
            $this->tasks = [];
1886
        }
1887
        if ($this->eventSubscriptions === null && !BitMaskUtil::isBitOn($this->cachedEntityState, self::EVENT_SUBSCRIPTIONS_STATE_BIT)) {
1888
            $this->eventSubscriptions = [];
1889
        }
1890
        if ($this->incidents === null && !BitMaskUtil::isBitOn($this->cachedEntityState, self::INCIDENT_STATE_BIT)) {
1891
            $this->incidents = [];
1892
        }
1893
        if (!$this->variableStore->isInitialized() && !BitMaskUtil::isBitOn($this->cachedEntityState, self::VARIABLES_STATE_BIT)) {
1894
            $this->variableStore->setVariablesProvider(VariableCollectionProvider::emptyVariables());
1895
            $this->variableStore->forceInitialization();
1896
        }
1897
        if ($this->externalTasks === null && !BitMaskUtil::isBitOn($this->cachedEntityState, self::EXTERNAL_TASKS_BIT)) {
1898
            $this->externalTasks = [];
1899
        }
1900
        $this->shouldQueryForSubprocessInstance = BitMaskUtil::isBitOn($this->cachedEntityState, self::SUB_PROCESS_INSTANCE_STATE_BIT);
1901
        $this->shouldQueryForSubCaseInstance = BitMaskUtil::isBitOn($this->cachedEntityState, self::SUB_CASE_INSTANCE_STATE_BIT);
0 ignored issues
show
Bug Best Practice introduced by
The property shouldQueryForSubCaseInstance does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1902
    }
1903
1904
    public function getCachedEntityState(): int
1905
    {
1906
        $this->cachedEntityState = 0;
1907
1908
        // Only mark a flag as false when the list is not-null and empty. If null,
1909
        // we can't be sure there are no entries in it since
1910
        // the list hasn't been initialized/queried yet.
1911
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::TASKS_STATE_BIT, ($this->tasks === null || count($this->tasks) > 0));
1912
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::EVENT_SUBSCRIPTIONS_STATE_BIT, ($this->eventSubscriptions === null || count($this->eventSubscriptions) > 0));
1913
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::JOBS_STATE_BIT, ($this->jobs === null || count($this->jobs) > 0));
1914
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::INCIDENT_STATE_BIT, ($this->incidents === null || count($this->incidents) > 0));
1915
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::VARIABLES_STATE_BIT, (!$this->variableStore->isInitialized() || !$this->variableStore->isEmpty()));
1916
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::SUB_PROCESS_INSTANCE_STATE_BIT, $this->shouldQueryForSubprocessInstance);
1917
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::SUB_CASE_INSTANCE_STATE_BIT, $this->shouldQueryForSubCaseInstance);
1918
        $this->cachedEntityState = BitMaskUtil::setBit($this->cachedEntityState, self::EXTERNAL_TASKS_BIT, ($this->externalTasks === null || count($this->externalTasks) > 0));
1919
1920
        return $this->cachedEntityState;
1921
    }
1922
1923
    public function getCachedEntityStateRaw(): int
1924
    {
1925
        return $this->cachedEntityState;
1926
    }
1927
1928
    public function getRootProcessInstanceId(): string
1929
    {
1930
        if ($this->isProcessInstanceExecution()) {
1931
            return $this->rootProcessInstanceId;
1932
        } else {
1933
            $processInstance = $this->getProcessInstance();
1934
            return $processInstance->rootProcessInstanceId;
1935
        }
1936
    }
1937
1938
    public function getRootProcessInstanceIdRaw(): string
1939
    {
1940
        return $this->rootProcessInstanceId;
1941
    }
1942
1943
    public function setRootProcessInstanceId(string $rootProcessInstanceId): void
1944
    {
1945
        $this->rootProcessInstanceId = $rootProcessInstanceId;
1946
    }
1947
1948
    public function getProcessInstanceId(): string
1949
    {
1950
        return $this->processInstanceId;
1951
    }
1952
1953
    public function setProcessInstanceId(string $processInstanceId): void
1954
    {
1955
        $this->processInstanceId = $processInstanceId;
1956
1957
        if ($this->id == $processInstanceId) {
1958
            $this->processInstance = $this;
1959
        }
1960
    }
1961
1962
    public function getParentId(): ?string
1963
    {
1964
        return $this->parentId;
1965
    }
1966
1967
    public function setParentId(string $parentId): void
1968
    {
1969
        $this->parentId = $parentId;
1970
    }
1971
1972
    public function getRevision(): int
1973
    {
1974
        return $this->revision;
1975
    }
1976
1977
    public function setRevision(int $revision): void
1978
    {
1979
        $this->revision = $revision;
1980
    }
1981
1982
    public function setActivityId(string $activityId): void
1983
    {
1984
        $this->activityId = $activityId;
1985
    }
1986
1987
    public function setSuperExecutionId(string $superExecutionId): void
1988
    {
1989
        $this->superExecutionId = $superExecutionId;
1990
    }
1991
1992
    public function getReferencedEntityIds(): array
1993
    {
1994
        $referenceIds = [];
1995
1996
        if ($this->superExecutionId !== null) {
1997
            $referenceIds[] = $this->superExecutionId;
1998
        }
1999
        if ($this->parentId !== null) {
2000
            $referenceIds[] = $this->parentId;
2001
        }
2002
2003
        return $referenceIds;
2004
    }
2005
2006
    public function getReferencedEntitiesIdAndClass(): array
2007
    {
2008
        $referenceIdAndClass = [];
2009
2010
        if ($this->superExecutionId !== null) {
2011
            $referenceIdAndClass[$this->superExecutionId] = ExecutionEntity::class;
2012
        }
2013
        if ($this->parentId !== null) {
2014
            $referenceIdAndClass[$this->parentId] = ExecutionEntity::class;
2015
        }
2016
        if ($this->processInstanceId !== null) {
2017
            $referenceIdAndClass[$this->processInstanceId] = ExecutionEntity::class;
2018
        }
2019
        if ($this->processDefinitionId !== null) {
2020
            $referenceIdAndClass[$this->processDefinitionId] = ProcessDefinitionEntity::class;
2021
        }
2022
2023
        return $referenceIdAndClass;
2024
    }
2025
2026
    public function getSuspensionState(): int
2027
    {
2028
        return $this->suspensionState;
2029
    }
2030
2031
    public function setSuspensionState(int $suspensionState): void
2032
    {
2033
        $this->suspensionState = $suspensionState;
2034
    }
2035
2036
    public function isSuspended(): bool
2037
    {
2038
        return $this->suspensionState == SuspensionState::suspended()->getStateCode();
2039
    }
2040
2041
    public function getCurrentActivityId(): ?string
2042
    {
2043
        return $this->activityId;
2044
    }
2045
2046
    public function getCurrentActivityName(): ?string
2047
    {
2048
        return $this->activityName;
2049
    }
2050
2051
    public function getBpmnModelElementInstance(): FlowElementInterface
2052
    {
2053
        $bpmnModelInstance = $this->getBpmnModelInstance();
2054
        if ($bpmnModelInstance !== null) {
2055
            $modelElementInstance = null;
2056
            if (ExecutionListenerInterface::EVENTNAME_TAKE == $this->eventName) {
2057
                $modelElementInstance = $bpmnModelInstance->getModelElementById($this->transition->getId());
2058
            } else {
2059
                $modelElementInstance = $bpmnModelInstance->getModelElementById($this->activityId);
2060
            }
2061
2062
            try {
2063
                return $modelElementInstance;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $modelElementInstance returns the type null which is incompatible with the type-hinted return Bpmn\Instance\FlowElementInterface.
Loading history...
2064
            } catch (\Exception $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Exception $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2065
                $elementType = $modelElementInstance->getElementType();
2066
                //throw LOG.castModelInstanceException(modelElementInstance, "FlowElement", elementType->getTypeName(),
2067
                //elementType->getTypeNamespace(), e);
2068
                throw $e;
2069
            }
2070
        } else {
2071
            return null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return null returns the type null which is incompatible with the type-hinted return Bpmn\Instance\FlowElementInterface.
Loading history...
2072
        }
2073
    }
2074
2075
    public function getBpmnModelInstance(): ?BpmnModelInstanceInterface
2076
    {
2077
        if ($this->processDefinitionId !== null) {
2078
            return Context::getProcessEngineConfiguration()->getDeploymentCache()->findBpmnModelInstanceForProcessDefinition($this->processDefinitionId);
2079
        } else {
2080
            return null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return null returns the type null which is incompatible with the return type mandated by Jabe\Delegate\BpmnModelE...:getBpmnModelInstance() of Bpmn\BpmnModelInstanceInterface.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
2081
        }
2082
    }
2083
2084
    public function getProcessEngineServices(): ProcessEngineServicesInterface
2085
    {
2086
        return Context::getProcessEngineConfiguration()->getProcessEngine();
0 ignored issues
show
Bug introduced by
The method getProcessEngine() does not exist on Jabe\Impl\Cfg\ProcessEngineConfigurationImpl. Did you maybe mean getProcessEngineBootstrapCommand()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2086
        return Context::getProcessEngineConfiguration()->/** @scrutinizer ignore-call */ getProcessEngine();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2087
    }
2088
2089
    public function getProcessEngine(): ProcessEngineInterface
2090
    {
2091
        return Context::getProcessEngineConfiguration()->getProcessEngine();
2092
    }
2093
2094
    public function getProcessDefinitionTenantId(): ?string
2095
    {
2096
        return $this->getProcessDefinition()->getTenantId();
2097
    }
2098
}
2099