Skip to content

Commit 3e12fef

Browse files
authored
Merge pull request #556: expose Workflow Init Method
2 parents 3a61fca + 0a5bbc2 commit 3e12fef

File tree

17 files changed

+294
-101
lines changed

17 files changed

+294
-101
lines changed

.php_cs.dist

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/Internal/Declaration/Dispatcher/AutowiredPayloads.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@
2020
*/
2121
class AutowiredPayloads extends Dispatcher
2222
{
23-
public function dispatchValues(object $ctx, ValuesInterface $values): mixed
23+
public function dispatch(object $ctx, array $arguments): mixed
24+
{
25+
try {
26+
return parent::dispatch($ctx, $arguments);
27+
} catch (\TypeError $e) {
28+
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
29+
}
30+
}
31+
32+
public function resolveArguments(ValuesInterface $values): array
2433
{
2534
$arguments = [];
2635
try {
@@ -30,11 +39,6 @@ public function dispatchValues(object $ctx, ValuesInterface $values): mixed
3039
} catch (\Throwable $e) {
3140
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
3241
}
33-
34-
try {
35-
return parent::dispatch($ctx, $arguments);
36-
} catch (\TypeError $e) {
37-
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
38-
}
42+
return $arguments;
3943
}
4044
}

src/Internal/Declaration/Instance.php

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,12 @@
1111

1212
namespace Temporal\Internal\Declaration;
1313

14-
use Temporal\DataConverter\ValuesInterface;
1514
use Temporal\Exception\InstantiationException;
16-
use Temporal\Internal\Declaration\Dispatcher\AutowiredPayloads;
1715
use Temporal\Internal\Declaration\Prototype\Prototype;
1816

19-
/**
20-
* @psalm-import-type DispatchableHandler from InstanceInterface
21-
*/
2217
abstract class Instance implements InstanceInterface, Destroyable
2318
{
24-
/**
25-
* @var \Closure(ValuesInterface): mixed
26-
*/
27-
private \Closure $handler;
19+
private MethodHandler $handler;
2820

2921
public function __construct(
3022
Prototype $prototype,
@@ -47,7 +39,7 @@ public function getContext(): object
4739
return $this->context;
4840
}
4941

50-
public function getHandler(): callable
42+
public function getHandler(): MethodHandler
5143
{
5244
return $this->handler;
5345
}
@@ -57,16 +49,8 @@ public function destroy(): void
5749
unset($this->handler, $this->context);
5850
}
5951

60-
/**
61-
* @return \Closure(ValuesInterface): mixed
62-
*
63-
* @psalm-return DispatchableHandler
64-
*/
65-
protected function createHandler(\ReflectionFunctionAbstract $func): \Closure
52+
protected function createHandler(\ReflectionFunctionAbstract $func): MethodHandler
6653
{
67-
$valueMapper = new AutowiredPayloads($func);
68-
69-
$context = $this->context;
70-
return static fn(ValuesInterface $values): mixed => $valueMapper->dispatchValues($context, $values);
54+
return new MethodHandler($this->context, $func);
7155
}
7256
}

src/Internal/Declaration/InstanceInterface.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,9 @@
1111

1212
namespace Temporal\Internal\Declaration;
1313

14-
use Temporal\DataConverter\ValuesInterface;
15-
16-
/**
17-
* @psalm-type DispatchableHandler = \Closure(ValuesInterface): mixed
18-
*/
1914
interface InstanceInterface
2015
{
21-
/**
22-
* @return DispatchableHandler
23-
*/
24-
public function getHandler(): callable;
16+
public function getHandler(): MethodHandler;
2517

2618
public function getContext(): object;
2719
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Temporal\Internal\Declaration;
6+
7+
use Temporal\DataConverter\ValuesInterface;
8+
use Temporal\Internal\Declaration\Dispatcher\AutowiredPayloads;
9+
10+
/**
11+
* @internal
12+
*/
13+
final class MethodHandler
14+
{
15+
private readonly AutowiredPayloads $dispatcher;
16+
17+
public function __construct(
18+
private readonly object $instance,
19+
\ReflectionFunctionAbstract $reflection,
20+
) {
21+
$this->dispatcher = new AutowiredPayloads($reflection);
22+
}
23+
24+
/**
25+
* Resolve arguments for the method.
26+
*/
27+
public function resolveArguments(ValuesInterface $values): array
28+
{
29+
return $this->dispatcher->resolveArguments($values);
30+
}
31+
32+
public function __invoke(ValuesInterface $values): mixed
33+
{
34+
$arguments = $this->resolveArguments($values);
35+
return $this->dispatcher->dispatch($this->instance, $arguments);
36+
}
37+
}

src/Internal/Declaration/WorkflowInstance.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@
2020
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
2121
use Temporal\Internal\Declaration\WorkflowInstance\SignalQueue;
2222
use Temporal\Internal\Interceptor;
23+
use Temporal\Workflow\InitMethod;
2324

2425
/**
25-
* @psalm-import-type DispatchableHandler from InstanceInterface
2626
* @psalm-type QueryHandler = \Closure(QueryInput): mixed
2727
* @psalm-type UpdateHandler = \Closure(UpdateInput, Deferred): PromiseInterface
2828
* @psalm-type ValidateUpdateHandler = \Closure(UpdateInput): void
2929
* @psalm-type QueryExecutor = \Closure(QueryInput, callable(ValuesInterface): mixed): mixed
3030
* @psalm-type UpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed, Deferred): PromiseInterface
3131
* @psalm-type ValidateUpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): mixed
3232
* @psalm-type UpdateValidator = \Closure(UpdateInput, UpdateHandler): void
33+
*
34+
* @internal
3335
*/
3436
final class WorkflowInstance extends Instance implements WorkflowInstanceInterface
3537
{
@@ -39,7 +41,7 @@ final class WorkflowInstance extends Instance implements WorkflowInstanceInterfa
3941
private array $queryHandlers = [];
4042

4143
/**
42-
* @var array<non-empty-string, DispatchableHandler>
44+
* @var array<non-empty-string, MethodHandler>
4345
*/
4446
private array $signalHandlers = [];
4547

@@ -140,11 +142,23 @@ public function setUpdateValidator(\Closure $validator): self
140142
/**
141143
* Trigger constructor in Process context.
142144
*/
143-
public function initConstructor(): void
145+
public function init(array $arguments = []): void
144146
{
145-
if (\method_exists($this->context, '__construct')) {
147+
if (!\method_exists($this->context, '__construct')) {
148+
return;
149+
}
150+
151+
if ($arguments === []) {
146152
$this->context->__construct();
153+
return;
147154
}
155+
156+
// Check InitMethod attribute
157+
$reflection = new \ReflectionMethod($this->context, '__construct');
158+
$attributes = $reflection->getAttributes(InitMethod::class);
159+
$attributes === []
160+
? $this->context->__construct()
161+
: $this->context->__construct(...$arguments);
148162
}
149163

150164
public function getSignalQueue(): SignalQueue
@@ -288,12 +302,9 @@ public function getPrototype(): WorkflowPrototype
288302
/**
289303
* Make a Closure from a callable.
290304
*
291-
* @return \Closure(ValuesInterface): mixed
292305
* @throws \ReflectionException
293-
*
294-
* @psalm-return DispatchableHandler
295306
*/
296-
protected function createCallableHandler(callable $handler): \Closure
307+
protected function createCallableHandler(callable $handler): MethodHandler
297308
{
298309
return $this->createHandler(
299310
new \ReflectionFunction($handler instanceof \Closure ? $handler : \Closure::fromCallable($handler)),

src/Internal/Declaration/WorkflowInstanceInterface.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@
1818
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
1919
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
2020

21+
/**
22+
* @internal
23+
*/
2124
interface WorkflowInstanceInterface extends InstanceInterface
2225
{
2326
/**
2427
* Trigger constructor in Process context.
28+
* If the constructor is tagged with {@see \Temporal\Workflow\InitMethod} attribute,
29+
* it will be executed with the {@see \Temporal\Workflow\WorkflowMethod} arguments.
2530
*/
26-
public function initConstructor(): void;
31+
public function init(array $arguments = []): void;
2732

2833
/**
2934
* @param non-empty-string $name

src/Internal/Workflow/Process/DeferredGenerator.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Temporal\Internal\Workflow\Process;
66

77
use Temporal\DataConverter\ValuesInterface;
8+
use Temporal\Internal\Declaration\MethodHandler;
89

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

26-
private \Closure $handler;
27+
private \Closure|MethodHandler $handler;
2728
private ValuesInterface $values;
2829

2930
private function __construct() {}
3031

3132
/**
32-
* @param \Closure(ValuesInterface): mixed $handler
33+
* @param MethodHandler|\Closure(ValuesInterface): mixed $handler
3334
*/
34-
public static function fromHandler(\Closure $handler, ValuesInterface $values): self
35+
public static function fromHandler(MethodHandler|\Closure $handler, ValuesInterface $values): self
3536
{
3637
$self = new self();
3738
$self->handler = $handler;

src/Internal/Workflow/Process/Process.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
use JetBrains\PhpStorm\Pure;
1515
use React\Promise\Deferred;
1616
use React\Promise\PromiseInterface;
17+
use Temporal\DataConverter\EncodedValues;
1718
use Temporal\DataConverter\ValuesInterface;
1819
use Temporal\Exception\DestructMemorizedInstanceException;
1920
use Temporal\Exception\Failure\CanceledFailure;
2021
use Temporal\Interceptor\WorkflowInbound\QueryInput;
2122
use Temporal\Interceptor\WorkflowInbound\SignalInput;
2223
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
2324
use Temporal\Interceptor\WorkflowInboundCallsInterceptor;
25+
use Temporal\Internal\Declaration\MethodHandler;
2426
use Temporal\Internal\Declaration\WorkflowInstance;
2527
use Temporal\Internal\Declaration\WorkflowInstanceInterface;
2628
use Temporal\Internal\ServiceContainer;
@@ -173,14 +175,21 @@ function (\Throwable $e): void {
173175
}
174176

175177
/**
176-
* @param \Closure(ValuesInterface): mixed $handler
178+
* @param MethodHandler|\Closure(ValuesInterface): mixed $handler
177179
*/
178-
public function start(\Closure $handler, ?ValuesInterface $values, bool $deferred): void
180+
public function start(MethodHandler|\Closure $handler, ValuesInterface $values, bool $deferred): void
179181
{
180182
try {
181183
$this->makeCurrent();
182-
$this->context->getWorkflowInstance()->initConstructor();
183-
parent::start($handler, $values, $deferred);
184+
185+
// Prepare typed input
186+
\assert($handler instanceof MethodHandler);
187+
$arguments = $handler->resolveArguments($values);
188+
189+
// Manage init method
190+
$this->context->getWorkflowInstance()->init($arguments);
191+
192+
parent::start($handler, EncodedValues::fromValues($arguments), $deferred);
184193
} catch (\Throwable $e) {
185194
$this->complete($e);
186195
} finally {

src/Internal/Workflow/Process/Scope.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Temporal\Exception\InvalidArgumentException;
2222
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
2323
use Temporal\Internal\Declaration\Destroyable;
24+
use Temporal\Internal\Declaration\MethodHandler;
2425
use Temporal\Internal\ServiceContainer;
2526
use Temporal\Internal\Transport\Request\Cancel;
2627
use Temporal\Internal\Workflow\ScopeContext;
@@ -124,12 +125,12 @@ public function getContext(): WorkflowContext
124125
}
125126

126127
/**
127-
* @param \Closure(ValuesInterface): mixed $handler
128+
* @param MethodHandler|\Closure(ValuesInterface): mixed $handler
128129
*/
129-
public function start(\Closure $handler, ?ValuesInterface $values, bool $deferred): void
130+
public function start(MethodHandler|\Closure $handler, ValuesInterface $values, bool $deferred): void
130131
{
131132
// Create a coroutine generator
132-
$this->coroutine = DeferredGenerator::fromHandler($handler, $values ?? EncodedValues::empty())
133+
$this->coroutine = DeferredGenerator::fromHandler($handler, $values)
133134
->catch($this->onException(...));
134135

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

239240
return $scope;
240241
}

0 commit comments

Comments
 (0)