Documentation

Workflow extends Facade
in package

FinalYes

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
psalm-import-type

TypeEnum from Type

psalm-import-type

DateIntervalValue from DateInterval

see
DateInterval
template-extends

Facade<ScopedContextInterface>

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
CancellationScopeInterface

asyncDetached()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
CancellationScopeInterface

await()

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
PromiseInterface

awaitWithTimeout()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
PromiseInterface

executeActivity()

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|ReflectionClass|ReflectionType|null $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|ReflectionClass|ReflectionType|null = null
Tags
throws
OutOfContextException

in the absence of the workflow execution context.

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
PromiseInterface

getCurrentContext()

public static getCurrentContext() : ScopeContext

Get current workflow context.

Return values
ScopeContext

getInput()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
ValuesInterface

getLastCompletionResult()

Returns value of last completion result, if any.

public static getLastCompletionResult([Type|TypeEnum|mixed $type = null ]) : mixed
Parameters
$type : Type|TypeEnum|mixed = null
Tags
throws
OutOfContextException

in the absence of the workflow execution context.

getStackTrace()

Returns a complete trace of the last calls (for debugging).

public static getStackTrace() : string
Tags
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
string

getVersion()

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
throws
OutOfContextException

in the absence of the workflow execution context.

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
bool

newActivityStub()

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
psalm-template

T of object

throws
OutOfContextException

in the absence of the workflow execution context.

Return values
T

newChildWorkflowStub()

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
psalm-template

T of object

throws
OutOfContextException

in the absence of the workflow execution context.

Return values
T

newContinueAsNewStub()

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
psalm-template

T of object

throws
OutOfContextException

in the absence of the workflow execution context.

Return values
T

newExternalWorkflowStub()

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
psalm-template

T of object

throws
OutOfContextException

in the absence of the workflow execution context.

Return values
T

newUntypedActivityStub()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
ActivityStubInterface

newUntypedChildWorkflowStub()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
ChildWorkflowStubInterface

newUntypedExternalWorkflowStub()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
ExternalWorkflowStubInterface

now()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
DateTimeInterface

registerQuery()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
ScopedContextInterface

registerSignal()

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
throws
OutOfContextException

in the absence of the workflow execution context.

Return values
ScopedContextInterface

registerUpdate()

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
throws
OutOfContextException

in the absence of the workflow execution context.

since
2.11.0
Return values
ScopedContextInterface

runLocked()

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
template

T

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
template

TReturn

throws
OutOfContextException

in the absence of the workflow execution context.

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
throws
OutOfContextException

in the absence of the workflow execution context.

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.

Return values
PromiseInterface<string|int, UuidInterface>

        
On this page

Search results