Skip to content

Commit

Permalink
Merge pull request #556: expose Workflow Init Method
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk authored Feb 4, 2025
2 parents 3a61fca + 0a5bbc2 commit 3e12fef
Show file tree
Hide file tree
Showing 17 changed files with 294 additions and 101 deletions.
37 changes: 0 additions & 37 deletions .php_cs.dist

This file was deleted.

18 changes: 11 additions & 7 deletions src/Internal/Declaration/Dispatcher/AutowiredPayloads.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@
*/
class AutowiredPayloads extends Dispatcher
{
public function dispatchValues(object $ctx, ValuesInterface $values): mixed
public function dispatch(object $ctx, array $arguments): mixed
{
try {
return parent::dispatch($ctx, $arguments);
} catch (\TypeError $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}

public function resolveArguments(ValuesInterface $values): array
{
$arguments = [];
try {
Expand All @@ -30,11 +39,6 @@ public function dispatchValues(object $ctx, ValuesInterface $values): mixed
} catch (\Throwable $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}

try {
return parent::dispatch($ctx, $arguments);
} catch (\TypeError $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
return $arguments;
}
}
24 changes: 4 additions & 20 deletions src/Internal/Declaration/Instance.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,12 @@

namespace Temporal\Internal\Declaration;

use Temporal\DataConverter\ValuesInterface;
use Temporal\Exception\InstantiationException;
use Temporal\Internal\Declaration\Dispatcher\AutowiredPayloads;
use Temporal\Internal\Declaration\Prototype\Prototype;

/**
* @psalm-import-type DispatchableHandler from InstanceInterface
*/
abstract class Instance implements InstanceInterface, Destroyable
{
/**
* @var \Closure(ValuesInterface): mixed
*/
private \Closure $handler;
private MethodHandler $handler;

public function __construct(
Prototype $prototype,
Expand All @@ -47,7 +39,7 @@ public function getContext(): object
return $this->context;
}

public function getHandler(): callable
public function getHandler(): MethodHandler
{
return $this->handler;
}
Expand All @@ -57,16 +49,8 @@ public function destroy(): void
unset($this->handler, $this->context);
}

/**
* @return \Closure(ValuesInterface): mixed
*
* @psalm-return DispatchableHandler
*/
protected function createHandler(\ReflectionFunctionAbstract $func): \Closure
protected function createHandler(\ReflectionFunctionAbstract $func): MethodHandler
{
$valueMapper = new AutowiredPayloads($func);

$context = $this->context;
return static fn(ValuesInterface $values): mixed => $valueMapper->dispatchValues($context, $values);
return new MethodHandler($this->context, $func);
}
}
10 changes: 1 addition & 9 deletions src/Internal/Declaration/InstanceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,9 @@

namespace Temporal\Internal\Declaration;

use Temporal\DataConverter\ValuesInterface;

/**
* @psalm-type DispatchableHandler = \Closure(ValuesInterface): mixed
*/
interface InstanceInterface
{
/**
* @return DispatchableHandler
*/
public function getHandler(): callable;
public function getHandler(): MethodHandler;

public function getContext(): object;
}
37 changes: 37 additions & 0 deletions src/Internal/Declaration/MethodHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Temporal\Internal\Declaration;

use Temporal\DataConverter\ValuesInterface;
use Temporal\Internal\Declaration\Dispatcher\AutowiredPayloads;

/**
* @internal
*/
final class MethodHandler
{
private readonly AutowiredPayloads $dispatcher;

public function __construct(
private readonly object $instance,
\ReflectionFunctionAbstract $reflection,
) {
$this->dispatcher = new AutowiredPayloads($reflection);
}

/**
* Resolve arguments for the method.
*/
public function resolveArguments(ValuesInterface $values): array
{
return $this->dispatcher->resolveArguments($values);
}

public function __invoke(ValuesInterface $values): mixed
{
$arguments = $this->resolveArguments($values);
return $this->dispatcher->dispatch($this->instance, $arguments);
}
}
27 changes: 19 additions & 8 deletions src/Internal/Declaration/WorkflowInstance.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
use Temporal\Internal\Declaration\WorkflowInstance\SignalQueue;
use Temporal\Internal\Interceptor;
use Temporal\Workflow\InitMethod;

/**
* @psalm-import-type DispatchableHandler from InstanceInterface
* @psalm-type QueryHandler = \Closure(QueryInput): mixed
* @psalm-type UpdateHandler = \Closure(UpdateInput, Deferred): PromiseInterface
* @psalm-type ValidateUpdateHandler = \Closure(UpdateInput): void
* @psalm-type QueryExecutor = \Closure(QueryInput, callable(ValuesInterface): mixed): mixed
* @psalm-type UpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed, Deferred): PromiseInterface
* @psalm-type ValidateUpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): mixed
* @psalm-type UpdateValidator = \Closure(UpdateInput, UpdateHandler): void
*
* @internal
*/
final class WorkflowInstance extends Instance implements WorkflowInstanceInterface
{
Expand All @@ -39,7 +41,7 @@ final class WorkflowInstance extends Instance implements WorkflowInstanceInterfa
private array $queryHandlers = [];

/**
* @var array<non-empty-string, DispatchableHandler>
* @var array<non-empty-string, MethodHandler>
*/
private array $signalHandlers = [];

Expand Down Expand Up @@ -140,11 +142,23 @@ public function setUpdateValidator(\Closure $validator): self
/**
* Trigger constructor in Process context.
*/
public function initConstructor(): void
public function init(array $arguments = []): void
{
if (\method_exists($this->context, '__construct')) {
if (!\method_exists($this->context, '__construct')) {
return;
}

if ($arguments === []) {
$this->context->__construct();
return;
}

// Check InitMethod attribute
$reflection = new \ReflectionMethod($this->context, '__construct');
$attributes = $reflection->getAttributes(InitMethod::class);
$attributes === []
? $this->context->__construct()
: $this->context->__construct(...$arguments);
}

public function getSignalQueue(): SignalQueue
Expand Down Expand Up @@ -288,12 +302,9 @@ public function getPrototype(): WorkflowPrototype
/**
* Make a Closure from a callable.
*
* @return \Closure(ValuesInterface): mixed
* @throws \ReflectionException
*
* @psalm-return DispatchableHandler
*/
protected function createCallableHandler(callable $handler): \Closure
protected function createCallableHandler(callable $handler): MethodHandler
{
return $this->createHandler(
new \ReflectionFunction($handler instanceof \Closure ? $handler : \Closure::fromCallable($handler)),
Expand Down
7 changes: 6 additions & 1 deletion src/Internal/Declaration/WorkflowInstanceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;

/**
* @internal
*/
interface WorkflowInstanceInterface extends InstanceInterface
{
/**
* Trigger constructor in Process context.
* If the constructor is tagged with {@see \Temporal\Workflow\InitMethod} attribute,
* it will be executed with the {@see \Temporal\Workflow\WorkflowMethod} arguments.
*/
public function initConstructor(): void;
public function init(array $arguments = []): void;

/**
* @param non-empty-string $name
Expand Down
7 changes: 4 additions & 3 deletions src/Internal/Workflow/Process/DeferredGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Temporal\Internal\Workflow\Process;

use Temporal\DataConverter\ValuesInterface;
use Temporal\Internal\Declaration\MethodHandler;

/**
* A wrapper around a generator that doesn't start the wrapped generator ASAP.
Expand All @@ -23,15 +24,15 @@ final class DeferredGenerator implements \Iterator
/** @var array<\Closure(\Throwable): mixed> */
private array $catchers = [];

private \Closure $handler;
private \Closure|MethodHandler $handler;
private ValuesInterface $values;

private function __construct() {}

/**
* @param \Closure(ValuesInterface): mixed $handler
* @param MethodHandler|\Closure(ValuesInterface): mixed $handler
*/
public static function fromHandler(\Closure $handler, ValuesInterface $values): self
public static function fromHandler(MethodHandler|\Closure $handler, ValuesInterface $values): self
{
$self = new self();
$self->handler = $handler;
Expand Down
17 changes: 13 additions & 4 deletions src/Internal/Workflow/Process/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
use JetBrains\PhpStorm\Pure;
use React\Promise\Deferred;
use React\Promise\PromiseInterface;
use Temporal\DataConverter\EncodedValues;
use Temporal\DataConverter\ValuesInterface;
use Temporal\Exception\DestructMemorizedInstanceException;
use Temporal\Exception\Failure\CanceledFailure;
use Temporal\Interceptor\WorkflowInbound\QueryInput;
use Temporal\Interceptor\WorkflowInbound\SignalInput;
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
use Temporal\Interceptor\WorkflowInboundCallsInterceptor;
use Temporal\Internal\Declaration\MethodHandler;
use Temporal\Internal\Declaration\WorkflowInstance;
use Temporal\Internal\Declaration\WorkflowInstanceInterface;
use Temporal\Internal\ServiceContainer;
Expand Down Expand Up @@ -173,14 +175,21 @@ function (\Throwable $e): void {
}

/**
* @param \Closure(ValuesInterface): mixed $handler
* @param MethodHandler|\Closure(ValuesInterface): mixed $handler
*/
public function start(\Closure $handler, ?ValuesInterface $values, bool $deferred): void
public function start(MethodHandler|\Closure $handler, ValuesInterface $values, bool $deferred): void
{
try {
$this->makeCurrent();
$this->context->getWorkflowInstance()->initConstructor();
parent::start($handler, $values, $deferred);

// Prepare typed input
\assert($handler instanceof MethodHandler);
$arguments = $handler->resolveArguments($values);

// Manage init method
$this->context->getWorkflowInstance()->init($arguments);

parent::start($handler, EncodedValues::fromValues($arguments), $deferred);
} catch (\Throwable $e) {
$this->complete($e);
} finally {
Expand Down
9 changes: 5 additions & 4 deletions src/Internal/Workflow/Process/Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Temporal\Exception\InvalidArgumentException;
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
use Temporal\Internal\Declaration\Destroyable;
use Temporal\Internal\Declaration\MethodHandler;
use Temporal\Internal\ServiceContainer;
use Temporal\Internal\Transport\Request\Cancel;
use Temporal\Internal\Workflow\ScopeContext;
Expand Down Expand Up @@ -124,12 +125,12 @@ public function getContext(): WorkflowContext
}

/**
* @param \Closure(ValuesInterface): mixed $handler
* @param MethodHandler|\Closure(ValuesInterface): mixed $handler
*/
public function start(\Closure $handler, ?ValuesInterface $values, bool $deferred): void
public function start(MethodHandler|\Closure $handler, ValuesInterface $values, bool $deferred): void
{
// Create a coroutine generator
$this->coroutine = DeferredGenerator::fromHandler($handler, $values ?? EncodedValues::empty())
$this->coroutine = DeferredGenerator::fromHandler($handler, $values)
->catch($this->onException(...));

$deferred
Expand Down Expand Up @@ -234,7 +235,7 @@ public function cancel(?\Throwable $reason = null): void
public function startScope(callable $handler, bool $detached, ?string $layer = null): CancellationScopeInterface
{
$scope = $this->createScope($detached, $layer);
$scope->start($handler, null, false);
$scope->start($handler(...), EncodedValues::empty(), false);

return $scope;
}
Expand Down
24 changes: 24 additions & 0 deletions src/Workflow/InitMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* This file is part of Temporal package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Temporal\Workflow;

use Doctrine\Common\Annotations\Annotation\Target;

/**
* Marks a Workflow constructor to be executed with the {@see WorkflowMethod} arguments.
* The Init Method is executed before the Workflow Method, Signal, and Update handlers.
*
* @Annotation
* @Target({ "METHOD" })
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
final class InitMethod {}
Loading

0 comments on commit 3e12fef

Please sign in to comment.