Workflow
extends Facade
in package
This class provides coroutine specific access to active WorkflowContext. It is safe to use this Facade inside your helper classes.
This is main class you can use in your workflow code.
Tags
Table of Contents
Constants
- DEFAULT_VERSION = -1
Methods
- allHandlersFinished() : bool
- Whether update and signal handlers have finished executing.
- async() : CancellationScopeInterface
- The method calls an asynchronous task and returns a promise with additional properties/methods.
- asyncDetached() : CancellationScopeInterface
- Creates a child task that is not affected by parent task interruption, cancellation, or completion.
- await() : PromiseInterface
- Moves to the next step if the expression evaluates to `true`.
- awaitWithTimeout() : PromiseInterface<string|int, bool>
- Checks if any conditions were met or the timeout was reached.
- continueAsNew() : PromiseInterface
- Completes the current workflow execution atomically and starts a new execution with the same Workflow Id.
- executeActivity() : PromiseInterface<string|int, mixed>
- Calls an activity by its name and gets the result of its execution.
- executeChildWorkflow() : PromiseInterface
- Calls an external workflow without stopping the current one.
- getCurrentContext() : ScopeContext
- getInfo() : WorkflowInfo
- Returns information about current workflow execution.
- getInput() : ValuesInterface
- Returns workflow execution input arguments.
- getLastCompletionResult() : mixed
- Returns value of last completion result, if any.
- getStackTrace() : string
- Returns a complete trace of the last calls (for debugging).
- getUpdateContext() : UpdateContext|null
- getVersion() : PromiseInterface<string|int, int>
- Updates the behavior of an existing workflow to resolve inconsistency errors during replay.
- isReplaying() : bool
- Checks if the code is under a workflow.
- newActivityStub() : T
- The method returns a proxy over the class containing the activity, which allows you to conveniently and beautifully call all methods within the passed class.
- newChildWorkflowStub() : T
- Creates a proxy for a workflow class to execute as a child workflow.
- newContinueAsNewStub() : T
- Creates a proxy for a workflow class to continue as new.
- newExternalWorkflowStub() : T
- This method allows you to create a "proxy" for an existing and running workflow by fqn class name of the existing workflow.
- newUntypedActivityStub() : ActivityStubInterface
- The method creates and returns a proxy class with the specified settings that allows to call an activities with the passed options.
- newUntypedChildWorkflowStub() : ChildWorkflowStubInterface
- Creates a proxy for a workflow by name to execute as a child workflow.
- newUntypedExternalWorkflowStub() : ExternalWorkflowStubInterface
- Allows to create a "proxy" for an existing and running workflow by name (type) of the existing workflow.
- now() : DateTimeInterface
- Returns current datetime.
- registerQuery() : ScopedContextInterface
- Register a Query handler in the Workflow.
- registerSignal() : ScopedContextInterface
- Registers a Signal handler in the Workflow.
- registerUpdate() : ScopedContextInterface
- Registers an Update method in the Workflow.
- runLocked() : CancellationScopeInterface<string|int, T>
- Run a function when the mutex is released.
- sideEffect() : PromiseInterface<string|int, TReturn>
- Isolates non-pure data to ensure consistent results during workflow replays.
- timer() : PromiseInterface<string|int, null>
- Stops workflow execution work for a specified period.
- upsertSearchAttributes() : void
- Upsert search attributes
- uuid() : PromiseInterface<string|int, UuidInterface>
- Generate a UUID.
- uuid4() : PromiseInterface<string|int, UuidInterface>
- Generate a UUID version 4 (random).
- uuid7() : PromiseInterface<string|int, UuidInterface>
- Generate a UUID version 7 (Unix Epoch time).
Constants
DEFAULT_VERSION
public
mixed
DEFAULT_VERSION
= -1
Methods
allHandlersFinished()
Whether update and signal handlers have finished executing.
public
static allHandlersFinished() : bool
Consider waiting on this condition before workflow return or continue-as-new, to prevent interruption of in-progress handlers by workflow exit:
yield Workflow.await(static fn() => Workflow::allHandlersFinished());
Return values
bool —True if all handlers have finished executing.
async()
The method calls an asynchronous task and returns a promise with additional properties/methods.
public
static async(callable $task) : CancellationScopeInterface
You can use this method to call and manipulate a group of methods.
For example:
#[WorkflowMethod]
public function handler()
{
// Create the new "group" of executions
$promise = Workflow::async(function() {
$first = yield Workflow::executeActivity('first');
$second = yield Workflow::executeActivity('second');
return yield Promise::all([$first, $second]);
});
// Waiting for the execution result
yield $promise;
// Or cancel all group requests (activity executions)
$promise->cancel();
// Or get information about the execution of the group
$promise->isCancelled();
}
You can see more information about the capabilities of the child asynchronous task in CancellationScopeInterface interface.
Parameters
- $task : callable
Tags
Return values
CancellationScopeInterfaceasyncDetached()
Creates a child task that is not affected by parent task interruption, cancellation, or completion.
public
static asyncDetached(callable $task) : CancellationScopeInterface
The method is similar to the Workflow::async(), however, unlike it, it creates a child task, the execution of which is not affected by interruption, cancellation or completion of the parent task.
Default behaviour through Workflow::async():
$parent = Workflow::async(fn() =>
$child = Workflow::async(fn() =>
// ...
)
);
$parent->cancel();
// In this case, the "$child" promise will also be canceled:
$child->isCancelled(); // true
When creating a detaching task using Workflow::asyncDetached() inside the parent, it will not be stopped when the parent context finishes working:
$parent = Workflow::async(fn() =>
$child = Workflow::asyncDetached(fn() =>
// ...
)
);
$parent->cancel();
// In this case, the "$child" promise will NOT be canceled:
$child->isCancelled(); // false
Use asyncDetached to handle cleanup and compensation logic.
Parameters
- $task : callable
Tags
Return values
CancellationScopeInterfaceawait()
Moves to the next step if the expression evaluates to `true`.
public
static await(callable|PromiseInterface|Mutex ...$conditions) : PromiseInterface
Please note that a state change should ONLY occur if the internal workflow conditions are met.
#[WorkflowMethod]
public function handler()
{
yield Workflow::await(
Workflow::executeActivity('shouldByContinued')
);
// ...do something
}
Or in the case of an explicit signal method execution of the specified workflow.
private bool $continued = false;
#[WorkflowMethod]
public function handler()
{
yield Workflow::await(fn() => $this->continued);
// ...continue execution
}
#[SignalMethod]
public function continue()
{
$this->continued = true;
}
Parameters
- $conditions : callable|PromiseInterface|Mutex
Return values
PromiseInterfaceawaitWithTimeout()
Checks if any conditions were met or the timeout was reached.
public
static awaitWithTimeout(DateIntervalValue $interval, callable|PromiseInterface|Mutex ...$conditions) : PromiseInterface<string|int, bool>
Returns true if any of conditions were fired and false if timeout was reached.
This method is similar to Workflow::await(), but in any case it will proceed to the next step either if the internal workflow conditions are met, or after the specified timer interval expires.
#[WorkflowMethod]
public function handler()
{
// Continue after 42 seconds or when bool "continued" will be true.
yield Workflow::awaitWithTimeout(42, fn() => $this->continued);
// ...continue execution
}
Parameters
- $interval : DateIntervalValue
- $conditions : callable|PromiseInterface|Mutex
Return values
PromiseInterface<string|int, bool>continueAsNew()
Completes the current workflow execution atomically and starts a new execution with the same Workflow Id.
public
static continueAsNew(string $type[, array<string|int, mixed> $args = [] ][, ContinueAsNewOptions|null $options = null ]) : PromiseInterface
Method atomically completes the current workflow execution and starts a new execution of the Workflow with the same Workflow Id. The new execution will not carry over any history from the old execution.
#[WorkflowMethod]
public function handler()
{
return yield Workflow::continueAsNew('AnyAnotherWorkflow');
}
Parameters
- $type : string
- $args : array<string|int, mixed> = []
- $options : ContinueAsNewOptions|null = null
Tags
Return values
PromiseInterfaceexecuteActivity()
Calls an activity by its name and gets the result of its execution.
public
static executeActivity(string $type[, array<string|int, mixed> $args = [] ][, ActivityOptions|null $options = null ][, Type|string|null|ReflectionClass|ReflectionType $returnType = null ]) : PromiseInterface<string|int, mixed>
#[WorkflowMethod]
public function handler(string $existingWorkflowId)
{
$result1 = yield Workflow::executeActivity('activityName');
$result2 = yield Workflow::executeActivity('anotherActivityName');
}
In addition to this method of calling, you can use alternative methods of working with the result using Promise API (PromiseInterface).
#[WorkflowMethod]
public function handler(string $existingWorkflowId)
{
Workflow::executeActivity('activityName')
->then(function ($result) {
// Execution result
})
->otherwise(function (\Throwable $error) {
// Execution error
})
;
}
Parameters
- $type : string
- $args : array<string|int, mixed> = []
- $options : ActivityOptions|null = null
- $returnType : Type|string|null|ReflectionClass|ReflectionType = null
Tags
Return values
PromiseInterface<string|int, mixed>executeChildWorkflow()
Calls an external workflow without stopping the current one.
public
static executeChildWorkflow(string $type[, array<string|int, mixed> $args = [] ][, ChildWorkflowOptions|null $options = null ][, Type|string|ReflectionType|ReflectionClass|null $returnType = null ]) : PromiseInterface
Method for calling an external workflow without stopping the current one. It is similar to Workflow::continueAsNew(), but does not terminate the current workflow execution.
#[WorkflowMethod]
public function handler()
{
$result = yield Workflow::executeChildWorkflow('AnyAnotherWorkflow');
// Do something else
}
Please note that due to the fact that PHP does not allow defining the type on Generator, you sometimes need to specify the type of the child workflow result explicitly.
// External child workflow handler method with Generator return type-hint
public function handle(): \Generator
{
yield Workflow::executeActivity('example');
return 42; // Generator which returns int type (Type::TYPE_INT)
}
// Child workflow execution
#[WorkflowMethod]
public function handler()
{
$result = yield Workflow::executeChildWorkflow(
type: 'ChildWorkflow',
returnType: Type::TYPE_INT,
);
// Do something else
}
Parameters
- $type : string
- $args : array<string|int, mixed> = []
- $options : ChildWorkflowOptions|null = null
- $returnType : Type|string|ReflectionType|ReflectionClass|null = null
Tags
Return values
PromiseInterfacegetCurrentContext()
public
static getCurrentContext() : ScopeContext
Get current workflow context.
Return values
ScopeContextgetInfo()
Returns information about current workflow execution.
public
static getInfo() : WorkflowInfo
Tags
Return values
WorkflowInfogetInput()
Returns workflow execution input arguments.
public
static getInput() : ValuesInterface
The data is equivalent to what is passed to the workflow handler.
For example:
#[WorkflowInterface]
interface ExampleWorkflowInterface
{
#[WorkflowMethod]
public function handle(int $first, string $second);
}
And
// ...
$arguments = Workflow::getInput();
// Contains the value passed as the first argument to the workflow
$first = $arguments->getValue(0, Type::TYPE_INT);
// Contains the value passed as the second argument to the workflow
$second = $arguments->getValue(1, Type::TYPE_STRING);
Tags
Return values
ValuesInterfacegetLastCompletionResult()
Returns value of last completion result, if any.
public
static getLastCompletionResult([Type|TypeEnum|mixed $type = null ]) : mixed
Parameters
- $type : Type|TypeEnum|mixed = null
Tags
getStackTrace()
Returns a complete trace of the last calls (for debugging).
public
static getStackTrace() : string
Tags
Return values
stringgetUpdateContext()
public
static getUpdateContext() : UpdateContext|null
Tags
Return values
UpdateContext|nullgetVersion()
Updates the behavior of an existing workflow to resolve inconsistency errors during replay.
public
static getVersion(string $changeId, int $minSupported, int $maxSupported) : PromiseInterface<string|int, int>
The method is used to update the behavior (code) of an existing workflow which was already implemented earlier in order to get rid of errors of inconsistency of workflow replay and existing new code.
#[WorkflowMethod]
public function handler()
{
$version = yield Workflow::getVersion('new-activity-added', 1, 2);
$result = yield match($version) {
1 => Workflow::executeActivity('before'), // Old behaviour
2 => Workflow::executeActivity('after'), // New behaviour
}
}
Parameters
- $changeId : string
- $minSupported : int
- $maxSupported : int
Tags
Return values
PromiseInterface<string|int, int>isReplaying()
Checks if the code is under a workflow.
public
static isReplaying() : bool
Returns false if not under workflow code.
In the case that the workflow is started for the first time, the true value will be returned.
Tags
Return values
boolnewActivityStub()
The method returns a proxy over the class containing the activity, which allows you to conveniently and beautifully call all methods within the passed class.
public
static newActivityStub(T> $class[, ActivityOptionsInterface|null $options = null ]) : T
#[ActivityInterface]
class ExampleActivityClass
{
public function firstActivity() { ... }
public function secondActivity() { ... }
public function thirdActivity() { ... }
}
// Execution
#[WorkflowMethod]
public function handler(string $existingWorkflowId)
{
$activities = Workflow::newActivityStub(ExampleActivityClass::class);
// Activity methods execution
yield $activities->firstActivity();
yield $activities->secondActivity();
}
Parameters
- $class : T>
- $options : ActivityOptionsInterface|null = null
Tags
Return values
TnewChildWorkflowStub()
Creates a proxy for a workflow class to execute as a child workflow.
public
static newChildWorkflowStub(T> $class[, ChildWorkflowOptions|null $options = null ]) : T
This method is equivalent to Workflow::executeChildWorkflow(), but it takes the workflow class as the first argument, and the further api is built on the basis of calls to the methods of the passed workflow. For starting abandon child workflow Workflow::newUntypedChildWorkflowStub().
// Any workflow interface example:
#[WorkflowInterface]
interface ChildWorkflowExample
{
#[WorkflowMethod]
public function handle(int $value);
}
// Workflow::newChildWorkflowStub usage example:
#[WorkflowMethod]
public function handler()
{
// ExampleWorkflow proxy
$proxy = Workflow::newChildWorkflowStub(ChildWorkflowExample::class);
// Executes ChildWorkflowExample::handle(int $value)
$result = yield $proxy->handle(42);
// etc ...
}
Parameters
- $class : T>
- $options : ChildWorkflowOptions|null = null
Tags
Return values
TnewContinueAsNewStub()
Creates a proxy for a workflow class to continue as new.
public
static newContinueAsNewStub(T> $class[, ContinueAsNewOptions|null $options = null ]) : T
This method is equivalent to Workflow::continueAsNew(), but it takes the workflow class as the first argument, and the further api is built on the basis of calls to the methods of the passed workflow.
// Any workflow interface example:
#[WorkflowInterface]
interface ExampleWorkflow
{
#[WorkflowMethod]
public function handle(int $value);
}
// Workflow::newContinueAsNewStub usage example:
#[WorkflowMethod]
public function handler()
{
// ExampleWorkflow proxy
$proxy = Workflow::newContinueAsNewStub(ExampleWorkflow::class);
// Executes ExampleWorkflow::handle(int $value)
return yield $proxy->handle(42);
}
Parameters
- $class : T>
- $options : ContinueAsNewOptions|null = null
Tags
Return values
TnewExternalWorkflowStub()
This method allows you to create a "proxy" for an existing and running workflow by fqn class name of the existing workflow.
public
static newExternalWorkflowStub(T> $class, WorkflowExecution $execution) : T
#[WorkflowMethod]
public function handler(string $existingWorkflowId)
{
$externalWorkflow = Workflow::newExternalWorkflowStub(ClassName::class,
new WorkflowExecution($existingWorkflowId)
);
// The method "signalMethod" from the class "ClassName" will be called:
yield $externalWorkflow->signalMethod();
}
Parameters
- $class : T>
- $execution : WorkflowExecution
Tags
Return values
TnewUntypedActivityStub()
The method creates and returns a proxy class with the specified settings that allows to call an activities with the passed options.
public
static newUntypedActivityStub([ActivityOptionsInterface|null $options = null ]) : ActivityStubInterface
#[WorkflowMethod]
public function handler(string $existingWorkflowId)
{
$options = ActivityOptions::new()
->withTaskQueue('custom-task-queue')
;
$activities = Workflow::newUntypedActivityStub($options);
// Executes an activity named "activity"
$result = yield $activities->execute('activity');
}
Parameters
- $options : ActivityOptionsInterface|null = null
Tags
Return values
ActivityStubInterfacenewUntypedChildWorkflowStub()
Creates a proxy for a workflow by name to execute as a child workflow.
public
static newUntypedChildWorkflowStub(string $name[, ChildWorkflowOptions|null $options = null ]) : ChildWorkflowStubInterface
This method is equivalent to Workflow::newChildWorkflowStub(), but it takes the workflow name (instead of class name) as the first argument.
#[WorkflowMethod]
public function handler()
{
// ExampleWorkflow proxy
$workflow = Workflow::newUntypedChildWorkflowStub('WorkflowName');
// Executes workflow
$workflow->execute(42);
// Executes workflow signal named "name"
$workflow->signal('name');
// etc ...
}
To start abandoned child workflow use yield
and method start()
:
#[WorkflowMethod]
public function handler()
{
// ExampleWorkflow proxy
$workflow = Workflow::newUntypedChildWorkflowStub(
'WorkflowName',
ChildWorkflowOptions::new()->withParentClosePolicy(ParentClosePolicy::Abandon)
);
// Start child workflow
yield $workflow->start(42);
}
Parameters
- $name : string
- $options : ChildWorkflowOptions|null = null
Tags
Return values
ChildWorkflowStubInterfacenewUntypedExternalWorkflowStub()
Allows to create a "proxy" for an existing and running workflow by name (type) of the existing workflow.
public
static newUntypedExternalWorkflowStub(WorkflowExecution $execution) : ExternalWorkflowStubInterface
#[WorkflowMethod]
public function handler(string $existingWorkflowId)
{
$externalWorkflow = Workflow::newUntypedExternalWorkflowStub(
new WorkflowExecution($existingWorkflowId)
);
// Executes signal named "name"
$externalWorkflow->signal('name');
// Stops the external workflow
$externalWorkflow->cancel();
}
Parameters
- $execution : WorkflowExecution
Tags
Return values
ExternalWorkflowStubInterfacenow()
Returns current datetime.
public
static now() : DateTimeInterface
Unlike "real" system time, this method returns the time at which the given workflow task started at a certain point in time.
Thus, in the case of an execution error and when the workflow has been restarted (Workflow::isReplaying()), the result of this method will return exactly the date and time at which this workflow task was first started, which eliminates the problems of side effects.
Please, use this method Workflow::now() instead of:
- time() function.
- DateTime constructor.
- DateTimeImmutable constructor.
And each other like this.
Tags
Return values
DateTimeInterfaceregisterQuery()
Register a Query handler in the Workflow.
public
static registerQuery(string|class-string $queryType, callable $handler) : ScopedContextInterface
Workflow::registerQuery('query', function(string $argument) {
echo sprintf('Executed query "query" with argument "%s"', $argument);
});
The same method (WorkflowStubInterface::query()) should be used to call such query handlers as in the case of ordinary query methods.
Parameters
- $queryType : string|class-string
- $handler : callable
Tags
Return values
ScopedContextInterfaceregisterSignal()
Registers a Signal handler in the Workflow.
public
static registerSignal(non-empty-string $queryType, callable $handler) : ScopedContextInterface
Workflow::registerSignal('signal', function(string $argument) {
echo sprintf('Executed signal "signal" with argument "%s"', $argument);
});
The same method (WorkflowStubInterface::signal()) should be used to call such signal handlers as in the case of ordinary signal methods.
Parameters
- $queryType : non-empty-string
- $handler : callable
Tags
Return values
ScopedContextInterfaceregisterUpdate()
Registers an Update method in the Workflow.
public
static registerUpdate(non-empty-string $name, callable $handler[, callable|null $validator = null ]) : ScopedContextInterface
Workflow::registerUpdate(
'pushTask',
fn(Task $task) => $this->queue->push($task),
);
Register an Update method with a validator:
Workflow::registerUpdate(
'pushTask',
fn(Task $task) => $this->queue->push($task),
fn(Task $task) => $this->isValidTask($task) or throw new \InvalidArgumentException('Invalid task'),
);
Parameters
- $name : non-empty-string
- $handler : callable
-
Handler function to execute the update.
- $validator : callable|null = null
-
Validator function to check the input. It should throw an exception if the input is invalid. Note that the validator must have the same parameters as the handler.
Tags
Return values
ScopedContextInterfacerunLocked()
Run a function when the mutex is released.
public
static runLocked(Mutex $mutex, callable(): T $callable) : CancellationScopeInterface<string|int, T>
The mutex is locked for the duration of the function.
Parameters
- $mutex : Mutex
-
Mutex name or instance.
- $callable : callable(): T
-
Function to run.
Tags
Return values
CancellationScopeInterface<string|int, T>sideEffect()
Isolates non-pure data to ensure consistent results during workflow replays.
public
static sideEffect(callable(): TReturn $value) : PromiseInterface<string|int, TReturn>
This method serves to isolate any non-pure data. When the workflow is replayed (for example, in case of an error), such isolated data will return the result of the previous replay.
#[WorkflowMethod]
public function handler()
{
// ❌ Bad: Each call to workflow, the data will change.
$time = hrtime(true);
// ✅ Good: The calculation of the data with the side-effect
// will be performed once.
$time = yield Workflow::sideEffect(fn() => hrtime(true));
}
Parameters
- $value : callable(): TReturn
Tags
Return values
PromiseInterface<string|int, TReturn>timer()
Stops workflow execution work for a specified period.
public
static timer(DateIntervalValue $interval) : PromiseInterface<string|int, null>
The first argument can take implementation of the DateInterval, string Carbon format () or a positive number, which is equivalent to the seconds for which the workflow should be suspended.
#[WorkflowMethod]
public function handler()
{
// Wait 10 seconds
yield Workflow::timer(10);
// Wait 42 hours
yield Workflow::timer(new \DateInterval('PT42H'));
// Wait 23 months
yield Workflow::timer('23 months');
}
Parameters
- $interval : DateIntervalValue
Tags
Return values
PromiseInterface<string|int, null>upsertSearchAttributes()
Upsert search attributes
public
static upsertSearchAttributes(array<string, mixed> $searchAttributes) : void
Parameters
- $searchAttributes : array<string, mixed>
uuid()
Generate a UUID.
public
static uuid() : PromiseInterface<string|int, UuidInterface>
Return values
PromiseInterface<string|int, UuidInterface>uuid4()
Generate a UUID version 4 (random).
public
static uuid4() : PromiseInterface<string|int, UuidInterface>
Return values
PromiseInterface<string|int, UuidInterface>uuid7()
Generate a UUID version 7 (Unix Epoch time).
public
static uuid7([DateTimeInterface|null $dateTime = null ]) : PromiseInterface<string|int, UuidInterface>
Parameters
- $dateTime : DateTimeInterface|null = null
-
An optional date/time from which to create the version 7 UUID. If not provided, the UUID is generated using the current date/time.