src/Execution.php
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]);
            }
        }));
    }
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]);
            }
        }));
    }
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.