KMP PHP API Reference

DefaultWorkflowEngine
in package
implements WorkflowEngineInterface

Default workflow execution engine.

Executes workflow graphs by traversing nodes, invoking actions/conditions from the registries, and managing instance lifecycle state.

Transaction strategy: durable public entry points (startWorkflow, resumeWorkflow, cancelWorkflow, fireIntermediateApprovalActions) wrap work in a single database transaction via ConnectionManager::get('default')->transactional(). Ephemeral startWorkflow executions skip the engine transaction so synchronous domain actions can own their own rollback behavior. Recursive calls (e.g., subworkflow nodes calling startWorkflow, or child completion calling resumeWorkflow) share the outer transaction instead of opening a nested one. The $isInTransaction flag tracks this.

Action nodes may invoke services (e.g., WarrantManager) that manage their own internal transactions. CakePHP's Connection handles nested transactional() calls via savepoints, so these are safe and will participate correctly in the outer transaction's commit/rollback.

When a workflow hits a WAITING state (approval gate, delay node, subworkflow), the transaction commits at that point. A later resumeWorkflow() call starts a fresh transaction.

Table of Contents

Interfaces

WorkflowEngineInterface
Contract for the workflow execution engine.

Constants

MAX_EXECUTION_DEPTH  : mixed = 200
Maximum node execution depth to prevent infinite recursion.

Properties

$container  : ContainerInterface
$ephemeral  : bool
Whether the current execution is ephemeral (in-memory, no persistence).
$executionDepth  : int
Current execution depth counter.
$isInTransaction  : bool
Whether we are currently inside a database transaction.
$visitedNodes  : array<string, bool>
Tracks visited nodes during a single execution pass to detect cycles.

Methods

__construct()  : mixed
Constructor.
cancelHumanTask()  : ServiceResult
Cancel a pending human task without resuming the workflow.
cancelWorkflow()  : ServiceResult
Cancel a running or waiting workflow instance.
completeHumanTask()  : ServiceResult
Complete a human task and resume the workflow.
dispatchTrigger()  : array<string|int, ServiceResult>
Dispatch a trigger event and start any matching workflows.
fireIntermediateApprovalActions()  : ServiceResult
Fire actions connected to an approval node's on_each_approval port.
getInstanceState()  : array<string|int, mixed>|null
Get the current state of a workflow instance.
resumeWorkflow()  : ServiceResult
Resume a waiting workflow instance from a specific node output.
startWorkflow()  : ServiceResult
Start a new workflow instance from a definition slug.
advanceToOutputs()  : void
Advance execution to all targets of a node's output port.
collectDescendants()  : void
Collect all descendant node IDs reachable from a starting node.
createExecutionLog()  : WorkflowExecutionLog|null
Create an execution log entry. Returns null for ephemeral workflows.
executeActionNode()  : void
Execute an action node via the WorkflowActionRegistry.
executeApprovalNode()  : void
Execute an approval node — creates approval record, sets instance to WAITING.
executeConditionNode()  : void
Execute a condition node and follow the matching output port.
executeDelayNode()  : void
Execute a delay node — sets instance to WAITING for later resumption.
executeEndNode()  : void
Execute an end node — completes this path, finishes instance if no active nodes remain.
executeForEachNode()  : void
Execute a forEach node — iterates over a collection, executing child nodes per item.
executeForkNode()  : void
Execute a fork node — marks complete and executes all output targets.
executeHumanTaskNode()  : void
Execute a humanTask node — creates a task record and pauses the workflow.
executeJoinNode()  : void
Execute a join node — waits for all input paths before advancing.
executeLoopNode()  : void
Execute a loop node — iterates until max count or exit condition.
executeNode()  : void
Execute a single workflow node and advance to connected outputs.
executeStateMachineNode()  : void
Execute a state machine node — validates and applies a state transition with configurable rules, status resolution, and audit logging.
executeSubworkflowNode()  : void
Execute a subworkflow node — starts a child workflow, sets instance to WAITING.
findContextValueByKey()  : mixed
Recursively find the first non-empty value for a key in workflow context.
findIncomingSource()  : string
Find which source node most recently completed execution leading into a join.
findNodesByType()  : array<string|int, mixed>
Find all nodes of a given type in the definition.
getAllOutputTargets()  : array<string|int, string>
Get all output targets for a node regardless of port.
getNodeInputSources()  : array<string|int, string>
Get all source node IDs that have edges leading into the given node.
getNodeOutputTargets()  : array<string|int, string>
Get target node IDs for a given node's output port.
getTriggerEntityIdField()  : string|null
Resolve the trigger-configured entity ID field name for a workflow.
hydrateInstanceEntityMetadata()  : void
Persist the workflow entity_id once it becomes available in context.
parseDeadline()  : DateTime|null
Parse a deadline duration string (e.g., "14d", "24h", "7d") into a future DateTime.
portsMatch()  : bool
Check if two port names are equivalent.
removeFromActiveNodes()  : void
Remove a node from the instance's active_nodes list.
resolveContextValue()  : mixed
Resolve a context value using a dot-path (e.g., '$.trigger.officer.id').
resolveInputData()  : array<string|int, mixed>
Resolve input data for a node from context using configured mappings.
resolveParamValue()  : mixed
Resolve a parameter value that may be a plain scalar, a $.path string, or a value descriptor object {type: '...', ...}.
resolveRequiredCount()  : int
Resolve a required count value that may be an integer or a config object.
saveLog()  : void
Save an execution log update. No-op if log is null (ephemeral mode).
setContextValue()  : void
Set a value in the context at the given dot-path.
updateInstance()  : void
Save instance changes to the database.
applyActionContextUpdates()  : mixed
Apply action-requested context updates and remove private payload data from output.
beginExecution()  : array<string, mixed>
Reset per-execution state and return the previous state for restoration.
executeActionService()  : mixed
Execute a workflow action service while suppressing model-trigger loops.
executeInTransaction()  : ServiceResult
Execute a callable inside a single database transaction, avoiding nesting.
markInstanceFailed()  : void
Mark a workflow instance as FAILED after a transaction rollback.
nodeHasOutput()  : bool
Check whether a node declares a connected output port.
normalizeActionResult()  : array<string, mixed>
Normalize action provider output into the standard result envelope.
refreshInstanceExecutionState()  : void
Synchronize an in-flight entity after a nested callback updated its row.
restoreExecution()  : void
Restore per-execution state after a public engine entry point completes.
startWorkflowInternal()  : ServiceResult
Start a workflow with optional parent callback metadata.

Constants

MAX_EXECUTION_DEPTH

Maximum node execution depth to prevent infinite recursion.

private mixed MAX_EXECUTION_DEPTH = 200

Properties

$ephemeral

Whether the current execution is ephemeral (in-memory, no persistence).

private bool $ephemeral = false

Set per-execution based on the workflow definition's execution_mode.

$isInTransaction

Whether we are currently inside a database transaction.

private bool $isInTransaction = false

Prevents nested transaction wrapping when methods like startWorkflow() or resumeWorkflow() are called recursively (e.g., subworkflow nodes, child completion callbacks).

$visitedNodes

Tracks visited nodes during a single execution pass to detect cycles.

private array<string, bool> $visitedNodes = []

Reset at the start of each startWorkflow/resumeWorkflow call.

Methods

__construct()

Constructor.

public __construct(ContainerInterface $container) : mixed
Parameters
$container : ContainerInterface

Service container

cancelHumanTask()

Cancel a pending human task without resuming the workflow.

public cancelHumanTask(int $taskId[, string|null $reason = null ]) : ServiceResult
Parameters
$taskId : int

The workflow_tasks.id to cancel

$reason : string|null = null

Optional cancellation reason

Return values
ServiceResult

cancelWorkflow()

Cancel a running or waiting workflow instance.

public cancelWorkflow(int $instanceId[, string|null $reason = null ]) : ServiceResult
Parameters
$instanceId : int

The workflow instance ID

$reason : string|null = null

Optional cancellation reason

Tags
inheritDoc
Return values
ServiceResult

completeHumanTask()

Complete a human task and resume the workflow.

public completeHumanTask(int $taskId, array<string|int, mixed> $formData, int $completedBy) : ServiceResult

Validates required form fields, saves form data to the task record, merges values into the workflow context via contextMapping, and resumes the workflow from the humanTask node's default output.

Parameters
$taskId : int

The workflow_tasks.id to complete

$formData : array<string|int, mixed>

Submitted form field values

$completedBy : int

Member ID of the user completing the task

Return values
ServiceResult

dispatchTrigger()

Dispatch a trigger event and start any matching workflows.

public dispatchTrigger(string $eventName[, array<string|int, mixed> $eventData = [] ][, int|null $triggeredBy = null ]) : array<string|int, ServiceResult>
Parameters
$eventName : string

The event name to match against triggers

$eventData : array<string|int, mixed> = []

Data associated with the event

$triggeredBy : int|null = null

Member ID who triggered the event

Tags
inheritDoc
Return values
array<string|int, ServiceResult>

Results for each started workflow

fireIntermediateApprovalActions()

Fire actions connected to an approval node's on_each_approval port.

public fireIntermediateApprovalActions(int $instanceId, string $nodeId, array<string|int, mixed> $approvalData[, string $outputPort = 'on_each_approval' ]) : ServiceResult
Parameters
$instanceId : int

The workflow instance ID

$nodeId : string

The approval node ID

$approvalData : array<string|int, mixed>

Approval progress data (approverId, decision, comment, nextApproverId)

$outputPort : string = 'on_each_approval'

Approval node output port to follow

Tags
inheritDoc
Return values
ServiceResult

getInstanceState()

Get the current state of a workflow instance.

public getInstanceState(int $instanceId) : array<string|int, mixed>|null
Parameters
$instanceId : int

The workflow instance ID

Tags
inheritDoc
Return values
array<string|int, mixed>|null

Instance state array or null if not found

resumeWorkflow()

Resume a waiting workflow instance from a specific node output.

public resumeWorkflow(int $instanceId, string $nodeId, string $outputPort[, array<string|int, mixed> $additionalData = [] ]) : ServiceResult
Parameters
$instanceId : int

The workflow instance ID

$nodeId : string

The node to resume from

$outputPort : string

The output port to follow

$additionalData : array<string|int, mixed> = []

Extra data to merge into context

Tags
inheritDoc
Return values
ServiceResult

startWorkflow()

Start a new workflow instance from a definition slug.

public startWorkflow(string $workflowSlug[, array<string|int, mixed> $triggerData = [] ][, int|null $startedBy = null ][, string|null $entityType = null ][, int|null $entityId = null ]) : ServiceResult
Parameters
$workflowSlug : string

The workflow definition slug

$triggerData : array<string|int, mixed> = []

Data passed by the trigger event

$startedBy : int|null = null

Member ID who initiated the workflow

$entityType : string|null = null

Optional entity type this workflow operates on

$entityId : int|null = null

Optional entity ID this workflow operates on

Tags
inheritDoc
Return values
ServiceResult

Contains instanceId on success

advanceToOutputs()

Advance execution to all targets of a node's output port.

protected advanceToOutputs(WorkflowInstance $instance, string $nodeId, string $port, array<string|int, mixed> $definition) : void
Parameters
$instance : WorkflowInstance
$nodeId : string
$port : string
$definition : array<string|int, mixed>

collectDescendants()

Collect all descendant node IDs reachable from a starting node.

protected collectDescendants(array<string|int, mixed> $definition, string $nodeId, array<string|int, mixed> &$collected) : void
Parameters
$definition : array<string|int, mixed>
$nodeId : string
$collected : array<string|int, mixed>

createExecutionLog()

Create an execution log entry. Returns null for ephemeral workflows.

protected createExecutionLog(WorkflowInstance $instance, string $nodeId, string $nodeType, int $attempt[, array<string|int, mixed>|null $inputData = null ][, array<string|int, mixed>|null $outputData = null ]) : WorkflowExecutionLog|null
Parameters
$instance : WorkflowInstance

Current instance

$nodeId : string

Node identifier

$nodeType : string

Node type

$attempt : int

Attempt number

$inputData : array<string|int, mixed>|null = null

Input data for the node

$outputData : array<string|int, mixed>|null = null

Output data (for completed-on-create logs like triggers)

Return values
WorkflowExecutionLog|null

executeForEachNode()

Execute a forEach node — iterates over a collection, executing child nodes per item.

protected executeForEachNode(WorkflowInstance $instance, string $nodeId, array<string|int, mixed> $node, WorkflowExecutionLog|null $log, array<string|int, mixed> $definition) : void

Output ports: 'iterate' (per item), 'complete' (after all), 'error' (on failure).

Parameters
$instance : WorkflowInstance
$nodeId : string
$node : array<string|int, mixed>
$log : WorkflowExecutionLog|null
$definition : array<string|int, mixed>

executeHumanTaskNode()

Execute a humanTask node — creates a task record and pauses the workflow.

protected executeHumanTaskNode(WorkflowInstance $instance, string $nodeId, array<string|int, mixed> $node, WorkflowExecutionLog|null $log) : void

The workflow remains in WAITING status until completeHumanTask() is called with the submitted form data.

Parameters
$instance : WorkflowInstance
$nodeId : string
$node : array<string|int, mixed>
$log : WorkflowExecutionLog|null

executeNode()

Execute a single workflow node and advance to connected outputs.

protected executeNode(WorkflowInstance $instance, string $nodeId, array<string|int, mixed> $definition) : void
Parameters
$instance : WorkflowInstance

The workflow instance

$nodeId : string

The node ID to execute

$definition : array<string|int, mixed>

The workflow definition graph

executeStateMachineNode()

Execute a state machine node — validates and applies a state transition with configurable rules, status resolution, and audit logging.

protected executeStateMachineNode(WorkflowInstance $instance, string $nodeId, array<string|int, mixed> $node, WorkflowExecutionLog|null $log, array<string|int, mixed> $definition) : void
Parameters
$instance : WorkflowInstance
$nodeId : string
$node : array<string|int, mixed>
$log : WorkflowExecutionLog|null
$definition : array<string|int, mixed>

executeSubworkflowNode()

Execute a subworkflow node — starts a child workflow, sets instance to WAITING.

protected executeSubworkflowNode(WorkflowInstance $instance, string $nodeId, array<string|int, mixed> $node, WorkflowExecutionLog|null $log) : void

Passes parent instance/node info so the child can resume the parent on completion.

Parameters
$instance : WorkflowInstance
$nodeId : string
$node : array<string|int, mixed>
$log : WorkflowExecutionLog|null

findContextValueByKey()

Recursively find the first non-empty value for a key in workflow context.

protected findContextValueByKey(mixed $value, string $targetKey) : mixed
Parameters
$value : mixed

Context value to inspect

$targetKey : string

Key to locate

findIncomingSource()

Find which source node most recently completed execution leading into a join.

protected findIncomingSource(array<string|int, mixed> $definition, string $joinNodeId, WorkflowInstance $instance[, array<string|int, mixed> $alreadyCompletedInputs = [] ]) : string
Parameters
$definition : array<string|int, mixed>

The workflow definition

$joinNodeId : string

The join node ID

$instance : WorkflowInstance

The workflow instance

$alreadyCompletedInputs : array<string|int, mixed> = []
Return values
string

The most recently completed source node ID

findNodesByType()

Find all nodes of a given type in the definition.

protected findNodesByType(array<string|int, mixed> $definition, string $type) : array<string|int, mixed>
Parameters
$definition : array<string|int, mixed>

The workflow definition

$type : string

Node type to find

Return values
array<string|int, mixed>

Matching nodes keyed by node ID

getAllOutputTargets()

Get all output targets for a node regardless of port.

protected getAllOutputTargets(array<string|int, mixed> $definition, string $nodeId) : array<string|int, string>
Parameters
$definition : array<string|int, mixed>

The workflow definition

$nodeId : string

Source node ID

Return values
array<string|int, string>

Target node IDs

getNodeInputSources()

Get all source node IDs that have edges leading into the given node.

protected getNodeInputSources(array<string|int, mixed> $definition, string $nodeId) : array<string|int, string>
Parameters
$definition : array<string|int, mixed>

The workflow definition

$nodeId : string

The target node ID

Return values
array<string|int, string>

Source node IDs

getNodeOutputTargets()

Get target node IDs for a given node's output port.

protected getNodeOutputTargets(array<string|int, mixed> $definition, string $nodeId, string $port) : array<string|int, string>
Parameters
$definition : array<string|int, mixed>

The workflow definition

$nodeId : string

Source node ID

$port : string

Output port name

Return values
array<string|int, string>

Target node IDs

getTriggerEntityIdField()

Resolve the trigger-configured entity ID field name for a workflow.

protected getTriggerEntityIdField(array<string|int, mixed> $definition) : string|null
Parameters
$definition : array<string|int, mixed>

Workflow definition graph

Return values
string|null

hydrateInstanceEntityMetadata()

Persist the workflow entity_id once it becomes available in context.

protected hydrateInstanceEntityMetadata(WorkflowInstance $instance, array<string|int, mixed> $definition) : void

Some durable workflows create the underlying entity inside an action node, after the workflow instance has already been inserted. When the trigger node declares an entityIdField, scan the accumulated context for that key and promote it onto the workflow instance as soon as it appears.

Parameters
$instance : WorkflowInstance

Current workflow instance

$definition : array<string|int, mixed>

Workflow definition graph

parseDeadline()

Parse a deadline duration string (e.g., "14d", "24h", "7d") into a future DateTime.

protected parseDeadline(string $deadline) : DateTime|null
Parameters
$deadline : string
Return values
DateTime|null

portsMatch()

Check if two port names are equivalent.

protected portsMatch(string $a, string $b) : bool

Treats "next" and "default" as equivalent for regular action/trigger outputs.

Parameters
$a : string
$b : string
Return values
bool

resolveContextValue()

Resolve a context value using a dot-path (e.g., '$.trigger.officer.id').

protected resolveContextValue(array<string|int, mixed> $context, string $path) : mixed
Parameters
$context : array<string|int, mixed>

The workflow context

$path : string

Dot-separated path, optionally prefixed with '$.'

resolveInputData()

Resolve input data for a node from context using configured mappings.

protected resolveInputData(array<string|int, mixed> $context, array<string|int, mixed> $nodeConfig) : array<string|int, mixed>
Parameters
$context : array<string|int, mixed>

The workflow context

$nodeConfig : array<string|int, mixed>

The node's configuration

Return values
array<string|int, mixed>

Resolved input data

resolveParamValue()

Resolve a parameter value that may be a plain scalar, a $.path string, or a value descriptor object {type: '...', ...}.

protected resolveParamValue(mixed $value, array<string|int, mixed> $context[, mixed $default = null ]) : mixed
Parameters
$value : mixed

The raw parameter value from workflow config

$context : array<string|int, mixed>

The workflow instance context

$default : mixed = null

Fallback if resolution fails

Return values
mixed

The resolved value

resolveRequiredCount()

Resolve a required count value that may be an integer or a config object.

protected resolveRequiredCount(mixed $value, array<string|int, mixed> $context) : int

Delegates to resolveParamValue() for universal resolution, then ensures the result is an integer >= 1.

Parameters
$value : mixed
$context : array<string|int, mixed>
Return values
int

setContextValue()

Set a value in the context at the given dot-path.

protected setContextValue(array<string|int, mixed> &$context, string $path, mixed $value) : void
Parameters
$context : array<string|int, mixed>

The workflow context (by reference)

$path : string

Dot-separated path

$value : mixed

The value to set

updateInstance()

Save instance changes to the database.

protected updateInstance(WorkflowInstance $instance, array<string|int, mixed> $changes) : void
Parameters
$instance : WorkflowInstance

The instance to save

$changes : array<string|int, mixed>

Additional field changes to apply

applyActionContextUpdates()

Apply action-requested context updates and remove private payload data from output.

private applyActionContextUpdates(array<string|int, mixed> &$context, mixed $result) : mixed
Parameters
$context : array<string|int, mixed>

Workflow context, updated by reference

$result : mixed

Raw action result

Return values
mixed

Public action result

beginExecution()

Reset per-execution state and return the previous state for restoration.

private beginExecution(bool $ephemeral) : array<string, mixed>
Parameters
$ephemeral : bool

Whether this execution should avoid persistence

Return values
array<string, mixed>

executeActionService()

Execute a workflow action service while suppressing model-trigger loops.

private executeActionService(object $service, string $serviceMethod, array<string, mixed> $context, array<string, mixed> $nodeConfig) : mixed

Workflow actions often save the same entities that can emit workflow triggers. Suppression prevents recursive duplicate workflows while the workflow engine is already applying an intentional state transition.

Parameters
$service : object

Workflow action service instance.

$serviceMethod : string

Method to invoke.

$context : array<string, mixed>

Workflow context.

$nodeConfig : array<string, mixed>

Resolved node config.

executeInTransaction()

Execute a callable inside a single database transaction, avoiding nesting.

private executeInTransaction(callable $work, string $methodName[, int|null $existingInstanceId = null ]) : ServiceResult

When already inside a transaction (e.g., subworkflow calling startWorkflow, or child completion calling resumeWorkflow), the callable runs directly without a new transactional() wrapper — all work shares the outer transaction.

CakePHP supports nested transactional() calls via savepoints, so services invoked by action nodes (e.g., WarrantManager) that open their own transactional() blocks are safe and participate in the outer transaction.

On failure at the top level the transaction is rolled back automatically. If $existingInstanceId is provided, the instance is marked FAILED in a separate post-rollback save so the database reflects the terminal state.

Parameters
$work : callable

The work to execute inside the transaction

$methodName : string

Method name for error logging

$existingInstanceId : int|null = null

Instance ID to mark FAILED on rollback

Return values
ServiceResult

markInstanceFailed()

Mark a workflow instance as FAILED after a transaction rollback.

private markInstanceFailed(int $instanceId, string $errorMessage) : void

Runs outside any transaction so the FAILED status persists even though the main transaction was rolled back.

Parameters
$instanceId : int

The instance to mark

$errorMessage : string

The error that caused the failure

nodeHasOutput()

Check whether a node declares a connected output port.

private nodeHasOutput(array<string|int, mixed> $node, string $outputPort) : bool
Parameters
$node : array<string|int, mixed>

Workflow node definition

$outputPort : string

Port name

Return values
bool

normalizeActionResult()

Normalize action provider output into the standard result envelope.

private normalizeActionResult(mixed $result) : array<string, mixed>
Parameters
$result : mixed

Raw action provider result

Return values
array<string, mixed>

restoreExecution()

Restore per-execution state after a public engine entry point completes.

private restoreExecution(array<string, mixed> $state) : void
Parameters
$state : array<string, mixed>

State returned by beginExecution()

startWorkflowInternal()

Start a workflow with optional parent callback metadata.

private startWorkflowInternal(string $workflowSlug[, array<string|int, mixed> $triggerData = [] ][, int|null $startedBy = null ][, string|null $entityType = null ][, int|null $entityId = null ][, int|null $parentInstanceId = null ][, string|null $parentNodeId = null ]) : ServiceResult
Parameters
$workflowSlug : string
$triggerData : array<string|int, mixed> = []
$startedBy : int|null = null
$entityType : string|null = null
$entityId : int|null = null
$parentInstanceId : int|null = null
$parentNodeId : string|null = null
Return values
ServiceResult
On this page

Search results