/** * Wake the given execution up using the given signal / variables. * * @param Execution $execution * @param string $signal * @param array $variables * @param array $delegation */ public function __construct(Execution $execution, $signal = NULL, array $variables = [], array $delegation = []) { $this->executionId = (string) $execution->getId(); $this->signal = $signal === NULL ? NULL : (string) $signal; $this->variables = serialize($variables); $this->delegation = serialize($delegation); }
/** * {@inheritdoc} */ public function execute(Execution $execution) { $engine = $execution->getEngine(); $activityId = $execution->getNode()->getId(); $engine->notify(new ActivityStartedEvent($activityId, $execution, $engine)); parent::execute($execution); $engine->notify(new ActivityCompletedEvent($activityId, $execution, $engine)); }
/** * {@inheritdoc} */ public function signal(Execution $execution, $signal, array $variables = [], array $delegation = []) { foreach ($variables as $k => $v) { $execution->setVariable($k, $v); } if ($signal === NULL) { return $execution->takeAll(NULL, [$execution]); } return $execution->take($signal); }
protected function collectExecutions(Execution $execution, Node $node = NULL) { $executions = [$execution]; foreach ($execution->findChildExecutions($node) as $child) { foreach ($this->collectExecutions($child, $node) as $exec) { $executions[] = $exec; } } return $executions; }
/** * {@inheritdoc} */ public function execute(Execution $execution) { $execution->getEngine()->notify(new ActivityStartedEvent($this->activityId, $execution, $execution->getEngine())); $execution->getEngine()->info('ENTER: scope <{scope}> at level {level} using {execution}', ['scope' => $this->activityId, 'level' => $execution->getExecutionDepth(), 'execution' => (string) $execution]); $root = $execution->createNestedExecution($execution->getProcessModel(), $execution->getNode(), true); $root->waitForSignal(); $this->createEventSubscriptions($root, $this->activityId, $execution->getNode()); $this->enter($root->createExecution(false)); }
public function signal(Execution $execution, $signal, array $variables = [], array $delegation = []) { $sub = $execution->getEngine()->findExecution($delegation['executionId']); if (!$sub instanceof Execution) { throw new \RuntimeException('Missing reference to nested execution'); } foreach ($this->outputs as $target => $source) { if ($sub->hasVariable($source)) { $execution->setVariable($target, $sub->getVariable($source)); } } $execution->takeAll(NULL, [$execution]); }
/** * {@inheritdoc} */ public function execute(Execution $execution) { foreach ($execution->getProcessModel()->findOutgoingTransitions($execution->getNode()->getId()) as $trans) { if ($trans->getId() === $this->defaultTransition) { continue; } if ($trans->isEnabled($execution)) { return $execution->take($trans); } } if ($this->defaultTransition !== NULL) { return $execution->take($this->defaultTransition); } $message = sprintf('Execution %s about to get stuck in exclusive choice within node "%s"', $execution->getId(), $execution->getNode()->getId()); throw new StuckException($message); }
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; }
/** * {@inheritdoc} */ public function execute(Execution $execution) { $execution->setActive(false); $numberExecutions = count($execution->getProcessModel()->findIncomingTransitions($execution->getNode()->getId())); // Collect recycled executions, initialize with current execution: $recycle = [$execution->getTransition()->getId() => $execution]; foreach ($execution->findInactiveConcurrentExecutions($execution->getNode()) as $concurrent) { // Collect at most 1 execution per incoming transition. $transId = $concurrent->getTransition()->getId(); if (empty($recycle[$transId]) || $concurrent->getTimestamp() < $recycle[$transId]->getTimestamp()) { $recycle[$transId] = $concurrent; } } if (count($recycle) !== $numberExecutions) { return; } return $execution->takeAll(NULL, $recycle); }
protected function computeVarDelta(Execution $execution, array $syncData) { $result = [0 => [], 1 => []]; $data = $execution->getSyncData(); $vars = empty($data['variables']) ? [] : $data['variables']; $syncData = empty($syncData['variables']) ? [] : $syncData['variables']; foreach ($vars as $k => $v) { if (!array_key_exists($k, $syncData)) { $result[0][$k] = true; continue; } if ($v !== $syncData[$k]) { $result[1][$k] = true; } unset($syncData[$k]); } foreach ($syncData as $k => $v) { $result[1][$k] = true; } return [Execution::SYNC_STATE_REMOVED => array_unique(array_keys(array_merge($result[0], $result[1]))), Execution::SYNC_STATE_MODIFIED => array_keys($result[1])]; return array_map('array_keys', $result); }
/** * {@inheritdoc} */ public function execute(Execution $execution) { $execution->takeAll(); }
public function isEnabled(Execution $execution) { return call_user_func($this->expression, $execution->getExpressionContext()) ? true : false; }
/** * 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]); } })); }
/** * {@inheritdoc} */ public function execute(Execution $execution) { $execution->getEngine()->notify(new ActivityStartedEvent($execution->getNode()->getId(), $execution, $execution->getEngine())); $this->createEventSubscriptions($execution, $execution->getNode()->getId()); $this->enter($execution); }
/** * Get the BPMN process engine instance. * * @return ProcessEngine */ public function getEngine() { return parent::getEngine(); }
protected function syncRemovedExecution(Execution $execution) { unset($this->executions[(string) $execution->getId()]); $this->debug('Sync removed {execution}', ['execution' => (string) $execution]); }
/** * Have the given execution execute execute the node's behavior. * * @param Execution $execution * @param Node $node */ public function __construct(Execution $execution, Node $node) { $this->executionId = $execution->getId(); $this->nodeId = (string) $node->getId(); }
/** * Find the outgoing transition to be taken by the given execution. * * @param Execution $execution * @throws \RuntimeException * @return Transition */ protected function findTransition(Execution $execution) { $out = (array) $execution->getProcessModel()->findOutgoingTransitions($execution->getNode()->getId()); $trans = NULL; if ($this->transitionId === NULL) { if (count($out) != 1) { throw new \RuntimeException(sprintf('No single outgoing transition found at node "%s"', $execution->getNode()->getId())); } return array_pop($out); } foreach ($out as $tmp) { if ($tmp->getId() === $this->transitionId) { $trans = $tmp; break; } } if ($trans === NULL) { throw new \RuntimeException(sprintf('Transition "%s" not connected to node "%s"', $this->transitionId, $execution->getNode()->getId())); } return $trans; }