/** * Have the engine start a new process instance. * * @param ProcessDefinition $definition * @param Node $startNode * @param string $businessKey * @param array $variables */ public function __construct(ProcessDefinition $definition, Node $startNode, $businessKey = NULL, array $variables = []) { $this->definitionId = $definition->getId(); $this->startNodeId = $startNode->getId(); $this->businessKey = $businessKey === NULL ? NULL : (string) $businessKey; $this->variables = serialize($variables); }
/** * Create a new persisted event subscription. * * @param string $name Name of the subscription type: "signal", "message", etc. * @param VirtualExecution $execution Target execution. * @param string $activityId ID of the activity that created the event subscription. * @param Node $node Target node to receive the delegated signal or NULL in order to use the activity node. * @param boolean $boundaryEvent Is this a subscription for a boundary event? */ public function __construct($name, VirtualExecution $execution, $activityId, Node $node = NULL, $boundaryEvent = false) { $this->name = (string) $name; $this->executionId = $execution->getId(); $this->activityId = (string) $activityId; $this->nodeId = $node === NULL ? NULL : (string) $node->getId(); $this->boundaryEvent = $boundaryEvent ? true : false; }
protected function isReachable(Node $source, Node $target, Execution $execution, \SplObjectStorage $visited) { if ($source === $target) { return true; } $model = $execution->getProcessModel(); $out = $model->findOutgoingTransitions($source->getId()); $visited->attach($source); foreach ($out as $transition) { $tmp = $model->findNode($transition->getTo()); if (!$visited->contains($tmp) && $this->isReachable($tmp, $target, $execution, $visited)) { return true; } } return false; }
/** * Perform logic needed to actually execute the node's behavior. * * @param Node $node * @param Execution $execution */ protected function executeNode(Node $node, Execution $execution) { $node->getBehavior()->execute($execution); }
/** * Take all given transitions (or every transition when given NULL) recycling the given * executions in the process. * * @param array<string> $transitions * @param array<Execution> $recycle * * @throws \RuntimeException When the execution has been terminated. */ public function takeAll(array $transitions = NULL, array $recycle = []) { if ($this->isTerminated()) { throw new \RuntimeException(sprintf('Cannot take transition in terminated %s', $this)); } $this->engine->pushCommand(new CallbackCommand(function () use($transitions, $recycle) { if ($this->isConcurrent()) { if (1 === count($this->findConcurrentExecutions())) { foreach ($recycle as $rec) { foreach ($recycle as $rec) { if ($rec !== $this) { $rec->terminate(false); } } } foreach ($this->findChildExecutions() as $child) { $this->parentExecution->registerChildExecution($child); } $this->parentExecution->node = $this->node; $this->parentExecution->transition = $this->transition; $this->parentExecution->setActive(true); $this->engine->debug('Merged concurrent {execution} into {root}', ['execution' => (string) $this, 'root' => (string) $this->parentExecution]); $this->terminate(false); return $this->parentExecution->takeAll($transitions); } } if ($transitions === NULL) { $transitions = $this->getProcessModel()->findOutgoingTransitions($this->node->getId()); } else { $transitions = array_map(function ($trans) { return $trans instanceof Transition ? $trans : $this->getProcessModel()->findTransition($trans); }, $transitions); } $transitions = array_filter($transitions, function (Transition $trans) { return $trans->isEnabled($this); }); if (!in_array($this, $recycle, true)) { array_unshift($recycle, $this); } if (empty($transitions)) { foreach ($recycle as $rec) { if ($rec !== $this) { $rec->terminate(false); } } if ($this->isConcurrent() && 0 == count($this->findConcurrentExecutions())) { $this->parentExecution->setActive(true); $this->terminate(false); return $this->parentExecution->terminate(); } $this->terminate(); return; } $root = $this->isConcurrent() ? $this->parentExecution : $this; $merge = count($root->findChildExecutions()) == count($root->findChildExecutions($this->node)); $active = []; foreach ($root->findChildExecutions() as $exec) { if ($exec->isActive()) { $active[] = $exec; } } $recycle = array_filter($recycle, function (Execution $execution) use($root) { return $execution !== $root; }); if (count($transitions) == 1 && empty($active) && $merge) { $terminated = 0; foreach ($recycle as $rec) { if (!$rec->isTerminated()) { $rec->terminate(false); $terminated++; } } $this->setState(self::STATE_CONCURRENT, false); $root->setNode($this->node); $root->setActive(true); if ($terminated > 0) { $this->engine->debug('Merged {count} concurrent executions into {execution}', ['count' => $terminated, 'execution' => (string) $root]); } $this->markModified(true); return $root->take(array_shift($transitions)); } $outgoing = []; while (!empty($transitions)) { $transition = array_shift($transitions); if (empty($recycle)) { $exec = $root->createExecution(true); } else { $exec = array_shift($recycle); } $exec->setActive(true); $exec->setNode($this->node); $exec->setState(self::STATE_CONCURRENT, true); $outgoing[] = [$exec, $transition]; } $root->setActive(false); foreach ($recycle as $rec) { $rec->terminate(false); } foreach ($outgoing as $out) { $out[0]->take($out[1]); } })); }
/** * Perform logic to actually signal the execution. * * @param Node $node * @param Execution $execution * @param array $vars * @param array $delegation */ protected function singalExecution(Node $node, Execution $execution, array $vars, array $delegation) { $behavior = $node->getBehavior(); if ($behavior instanceof SignalableBehaviorInterface) { $behavior->signal($execution, $this->signal, $vars, $delegation); } }