Skip to main content

Hooks

The engine emits lifecycle events through an in-process HookDispatcher (a container singleton). The package uses it internally for execution logging and for resuming workflows waiting on a child. You can attach your own listeners for observability — metrics, tracing, alerting.

use Qanna\WorkflowEngine\Engine\Enums\ExecutionStatus;
use Qanna\WorkflowEngine\Engine\HookDispatcher;
use Qanna\WorkflowEngine\Engine\Hooks\WorkflowFinished;

public function boot(): void
{
$hooks = $this->app->make(HookDispatcher::class);

$hooks->on(WorkflowFinished::NAME, function (WorkflowFinished $event) {
if ($event->status === ExecutionStatus::Failed) {
// notify, record a metric, …
report(new \RuntimeException("Workflow {$event->workflowId} failed: {$event->errorMessage}"));
}
});
}

on($event, $callback, $priority = 100, $once = false) — lower priority runs first. once() registers a one-shot. off(), flush(), has(), count(), events() round out the API. A listener returning false halts further listeners for that event.

Listeners can be closures, [$object, 'method'] arrays, or invokable objects.

Listener parameters​

Each hook carries an event object with the data listed below. The engine fills a listener's parameters by type or by name, not by position:

  • A parameter typed as the event class (or its base class Qanna\WorkflowEngine\Engine\Hooks\HookEvent) receives the event object.
  • Any other parameter receives the event property with the same name.

So these listeners are equivalent, and parameters can appear in any order:

use Qanna\WorkflowEngine\Engine\Hooks\NodeRetrying;

$hooks->on(NodeRetrying::NAME, function (NodeRetrying $event) {
Log::warning("Retrying {$event->nodeId}, attempt {$event->attempt} of {$event->maxAttempts}");
});

$hooks->on(NodeRetrying::NAME, function (int $attempt, int $maxAttempts, string $nodeId) {
Log::warning("Retrying {$nodeId}, attempt {$attempt} of {$maxAttempts}");
});

Ask only for what you need — unused properties are simply not passed. A parameter that matches neither the event type nor a property keeps its default value if it has one. If it has no default, dispatching the hook throws an InvalidArgumentException naming the missing parameter — and because hooks are dispatched while a workflow runs, that happens mid-execution, so check listener parameter names against the table below.

Events​

Every event class lives under Qanna\WorkflowEngine\Engine\Hooks and exposes its hook name as a NAME constant, so WorkflowFinished::NAME and 'workflow.finished' are interchangeable.

HookEvent classProperties
workflow.startingWorkflowStartingstring $workflowId, string $executionId
workflow.startedWorkflowStartedstring $workflowId, string $executionId, mixed $triggerOutput
workflow.resumedWorkflowResumedstring $workflowId, string $executionId
workflow.node-executingNodeExecutingstring $workflowId, string $executionId, string $nodeId
workflow.node-executedNodeExecutedstring $workflowId, string $executionId, string $nodeId, NodeResult $result
workflow.node-retryingNodeRetryingstring $workflowId, string $executionId, string $nodeId, int $attempt, int $maxAttempts
workflow.node-slowNodeSlowstring $workflowId, string $executionId, string $nodeId, float $elapsed (seconds), int $timeout (seconds), string $message
workflow.finishedWorkflowFinishedstring $workflowId, string $executionId, ExecutionStatus $status, ?string $errorMessage, Execution $execution
workflow.completedWorkflowCompletedstring $workflowId, string $executionId, WorkflowExecution $execution
workflow.trigger-ignoredTriggerIgnoredstring $workflowId, array $payload, ?string $reason
  • workflow.node-executed fires for successful, failed, and errored node results alike — inspect the NodeResult.
  • workflow.node-retrying's $attempt is the attempt about to run, counting the first run as attempt 1.
  • workflow.finished fires whenever an execution stops running — succeeded, failed, cancelled, or suspended.
  • workflow.completed fires once a top-level run or resume is fully finalised (including scheduling any resume), with the stored WorkflowExecution record.

Upgrading: listeners used to receive the values above as positional arguments. Positions no longer matter — rename parameters to match the property names (for example $error → $errorMessage on workflow.finished) or type a single parameter as the event class. workflow.node-retrying and workflow.node-slow no longer pass an array $meta; use $attempt / $maxAttempts and $elapsed / $timeout / $message instead.

These event names and payloads are not yet a formally frozen public API; treat them as stable-in-practice but verify after upgrades. For reacting to failures specifically, querying WorkflowExecution::where('status', ExecutionStatus::Failed) is the more conservative choice.