Skip to content

Commit 337f6ee

Browse files
authored
Merge pull request #443: fix Workflow hang in cases where a non-promise value is yielded
2 parents e6d9588 + 49c3f52 commit 337f6ee

File tree

20 files changed

+115
-158
lines changed

20 files changed

+115
-158
lines changed

psalm-baseline.xml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -509,9 +509,6 @@
509509
<InvalidDocblock>
510510
<code><![CDATA[final class ActivityInstantiator extends Instantiator]]></code>
511511
</InvalidDocblock>
512-
<PossiblyNullArgument>
513-
<code><![CDATA[$this->getInstance($prototype)]]></code>
514-
</PossiblyNullArgument>
515512
</file>
516513
<file src="src/Internal/Declaration/Instantiator/Instantiator.php">
517514
<MissingTemplateParam>
@@ -527,12 +524,6 @@
527524
<InvalidDocblock>
528525
<code><![CDATA[final class WorkflowInstantiator extends Instantiator]]></code>
529526
</InvalidDocblock>
530-
<PossiblyNullArgument>
531-
<code><![CDATA[$this->getInstance($prototype)]]></code>
532-
</PossiblyNullArgument>
533-
<RedundantCondition>
534-
<code><![CDATA[$class !== null]]></code>
535-
</RedundantCondition>
536527
</file>
537528
<file src="src/Internal/Declaration/Prototype/Prototype.php">
538529
<InvalidReturnStatement>
@@ -595,12 +586,10 @@
595586
</InvalidPropertyAssignmentValue>
596587
<MissingClosureReturnType>
597588
<code><![CDATA[function (QueryInput $input) use ($fn) {]]></code>
598-
<code><![CDATA[function (UpdateInput $input) use ($fn) {]]></code>
599589
</MissingClosureReturnType>
600590
<MoreSpecificImplementedParamType>
601591
<code><![CDATA[$handler]]></code>
602592
<code><![CDATA[$handler]]></code>
603-
<code><![CDATA[$name]]></code>
604593
</MoreSpecificImplementedParamType>
605594
<PropertyNotSetInConstructor>
606595
<code><![CDATA[$queryExecutor]]></code>
@@ -615,7 +604,6 @@
615604
<file src="src/Internal/Declaration/WorkflowInstance/SignalQueue.php">
616605
<ArgumentTypeCoercion>
617606
<code><![CDATA[$signal]]></code>
618-
<code><![CDATA[$signal]]></code>
619607
</ArgumentTypeCoercion>
620608
<MissingConstructor>
621609
<code><![CDATA[$onSignal]]></code>
@@ -752,13 +740,9 @@
752740
</PossiblyFalseArgument>
753741
</file>
754742
<file src="src/Internal/Marshaller/Type/ArrayType.php">
755-
<DocblockTypeContradiction>
756-
<code><![CDATA[\is_array($value)]]></code>
757-
</DocblockTypeContradiction>
758743
<MoreSpecificImplementedParamType>
759744
<code><![CDATA[$current]]></code>
760745
<code><![CDATA[$value]]></code>
761-
<code><![CDATA[$value]]></code>
762746
</MoreSpecificImplementedParamType>
763747
</file>
764748
<file src="src/Internal/Marshaller/Type/DateIntervalType.php">
@@ -1144,10 +1128,6 @@
11441128
<code><![CDATA[findValidateUpdateHandler]]></code>
11451129
<code><![CDATA[getUpdateHandlerNames]]></code>
11461130
</UndefinedInterfaceMethod>
1147-
<UnusedVariable>
1148-
<code><![CDATA[$resolver]]></code>
1149-
<code><![CDATA[$resolver]]></code>
1150-
</UnusedVariable>
11511131
</file>
11521132
<file src="src/Internal/Transport/Router/StartWorkflow.php">
11531133
<MissingClosureReturnType>

src/Internal/Declaration/Dispatcher/AutowiredPayloads.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@
1616

1717
/**
1818
* @psalm-type FunctionExecutor = \Closure(object|null, array): mixed
19+
* @internal
1920
*/
2021
class AutowiredPayloads extends Dispatcher
2122
{
22-
/**
23-
* @param object|null $ctx
24-
* @param ValuesInterface $values
25-
* @return mixed
26-
*/
27-
public function dispatchValues(?object $ctx, ValuesInterface $values)
23+
public function dispatchValues(object $ctx, ValuesInterface $values): mixed
2824
{
2925
$arguments = [];
3026
for ($i = 0; $i < $values->count(); $i++) {

src/Internal/Declaration/Dispatcher/Dispatcher.php

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use ReflectionType;
1616

1717
/**
18-
* @psalm-type FunctionExecutor = \Closure(object|null, array): mixed
18+
* @psalm-type FunctionExecutor = \Closure(object, array): mixed
1919
*/
2020
class Dispatcher implements DispatcherInterface
2121
{
@@ -30,8 +30,8 @@ class Dispatcher implements DispatcherInterface
3030
public const SCOPE_STATIC = 0x02;
3131

3232
/**
33+
* @var \Closure(object, array): mixed
3334
* @psalm-var FunctionExecutor
34-
* @var \Closure
3535
*/
3636
private \Closure $executor;
3737

@@ -95,12 +95,7 @@ public function getArgumentTypes(): array
9595
return $this->types;
9696
}
9797

98-
/**
99-
* @param object|null $ctx
100-
* @param array $arguments
101-
* @return mixed
102-
*/
103-
public function dispatch(?object $ctx, array $arguments)
98+
public function dispatch(object $ctx, array $arguments): mixed
10499
{
105100
return ($this->executor)($ctx, $arguments);
106101
}
@@ -119,13 +114,13 @@ private function scopeMatches(int $scope): bool
119114
* @psalm-return FunctionExecutor
120115
*
121116
* @param \ReflectionMethod $fun
122-
* @return \Closure
117+
* @return \Closure(object, array): mixed
123118
*/
124119
private function createExecutorFromMethod(\ReflectionMethod $fun): \Closure
125120
{
126-
return static function (?object $ctx, array $arguments) use ($fun) {
121+
return static function (object $object, array $arguments) use ($fun) {
127122
try {
128-
return $fun->invokeArgs($ctx, $arguments);
123+
return $fun->invokeArgs($object, $arguments);
129124
} catch (\ReflectionException $e) {
130125
throw new \BadMethodCallException($e->getMessage(), $e->getCode(), $e);
131126
}
@@ -136,15 +131,11 @@ private function createExecutorFromMethod(\ReflectionMethod $fun): \Closure
136131
* @psalm-return FunctionExecutor
137132
*
138133
* @param \ReflectionFunction $fun
139-
* @return \Closure
134+
* @return \Closure(object, array): mixed
140135
*/
141136
private function createExecutorFromFunction(\ReflectionFunction $fun): \Closure
142137
{
143-
return static function (?object $ctx, array $arguments) use ($fun) {
144-
if ($ctx === null) {
145-
return $fun->invoke(...$arguments);
146-
}
147-
138+
return static function (object $ctx, array $arguments) use ($fun) {
148139
$closure = $fun->getClosure();
149140

150141
try {

src/Internal/Declaration/Dispatcher/DispatcherInterface.php

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

1212
namespace Temporal\Internal\Declaration\Dispatcher;
1313

14+
/**
15+
* @internal
16+
*/
1417
interface DispatcherInterface
1518
{
16-
/**
17-
* @param object|null $ctx
18-
* @param array $arguments
19-
* @return mixed
20-
*/
21-
public function dispatch(?object $ctx, array $arguments);
19+
public function dispatch(object $ctx, array $arguments): mixed;
2220

2321
/**
2422
* @return array<\ReflectionType>

src/Internal/Declaration/Instance.php

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,15 @@
2121
*/
2222
abstract class Instance implements InstanceInterface
2323
{
24-
protected object $context;
2524
/**
2625
* @var \Closure(ValuesInterface): mixed
2726
*/
2827
private \Closure $handler;
2928

30-
/**
31-
* @param Prototype $prototype
32-
* @param object $context
33-
*/
34-
public function __construct(Prototype $prototype, object $context)
35-
{
29+
public function __construct(
30+
Prototype $prototype,
31+
protected readonly object $context,
32+
) {
3633
$handler = $prototype->getHandler();
3734

3835
if ($handler === null) {
@@ -42,14 +39,10 @@ public function __construct(Prototype $prototype, object $context)
4239
));
4340
}
4441

45-
$this->context = $context;
4642
$this->handler = $this->createHandler($handler);
4743
}
4844

49-
/**
50-
* @return object|null
51-
*/
52-
public function getContext(): ?object
45+
public function getContext(): object
5346
{
5447
return $this->context;
5548
}

src/Internal/Declaration/InstanceInterface.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,5 @@ interface InstanceInterface
2323
*/
2424
public function getHandler(): callable;
2525

26-
/**
27-
* @return object|null
28-
*/
29-
public function getContext(): ?object;
26+
public function getContext(): object;
3027
}

src/Internal/Declaration/Instantiator/Instantiator.php

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,11 @@ abstract class Instantiator implements InstantiatorInterface
1717
{
1818
/**
1919
* @param PrototypeInterface $prototype
20-
* @return \ReflectionClass|null
21-
*/
22-
protected function getClass(PrototypeInterface $prototype): ?\ReflectionClass
23-
{
24-
return $prototype->getClass();
25-
}
26-
27-
/**
28-
* @param PrototypeInterface $prototype
29-
* @return object|null
20+
* @return object
3021
* @throws \ReflectionException
3122
*/
32-
protected function getInstance(PrototypeInterface $prototype): ?object
23+
protected function getInstance(PrototypeInterface $prototype): object
3324
{
34-
if ($class = $this->getClass($prototype)) {
35-
return $class->newInstance();
36-
}
37-
38-
return null;
25+
return $prototype->getClass()->newInstance();
3926
}
4027
}

src/Internal/Declaration/Instantiator/WorkflowInstantiator.php

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Temporal\Internal\Declaration\Instantiator;
1313

1414
use Temporal\Exception\InstantiationException;
15+
use Temporal\Interceptor\PipelineProvider;
1516
use Temporal\Interceptor\WorkflowInboundCallsInterceptor;
1617
use Temporal\Internal\Declaration\Prototype\PrototypeInterface;
1718
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
@@ -23,7 +24,7 @@
2324
final class WorkflowInstantiator extends Instantiator
2425
{
2526
public function __construct(
26-
private \Temporal\Interceptor\PipelineProvider $interceptorProvider,
27+
private PipelineProvider $interceptorProvider,
2728
) {
2829
}
2930

@@ -43,26 +44,16 @@ public function instantiate(PrototypeInterface $prototype): WorkflowInstance
4344

4445
/**
4546
* @param PrototypeInterface $prototype
46-
* @return object|null
47+
* @return object
4748
* @throws \ReflectionException
4849
*/
49-
protected function getInstance(PrototypeInterface $prototype): ?object
50+
protected function getInstance(PrototypeInterface $prototype): object
5051
{
51-
$handler = $prototype->getHandler();
52+
$handler = $prototype->getHandler() ?? throw new InstantiationException(\sprintf(
53+
'Unable to instantiate workflow "%s" without handler method',
54+
$prototype->getID(),
55+
));
5256

53-
if ($handler === null) {
54-
throw new InstantiationException(\sprintf(
55-
'Unable to instantiate workflow "%s" without handler method',
56-
$prototype->getID(),
57-
));
58-
}
59-
60-
$class = $handler->getDeclaringClass();
61-
62-
if ($class !== null) {
63-
return $class->newInstanceWithoutConstructor();
64-
}
65-
66-
return null;
57+
return $handler->getDeclaringClass()->newInstanceWithoutConstructor();
6758
}
6859
}

src/Internal/Declaration/WorkflowInstance.php

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

1212
namespace Temporal\Internal\Declaration;
1313

14+
use React\Promise\PromiseInterface;
1415
use Temporal\DataConverter\ValuesInterface;
1516
use Temporal\Interceptor\WorkflowInbound\QueryInput;
1617
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
1718
use Temporal\Interceptor\WorkflowInboundCallsInterceptor;
1819
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
1920
use Temporal\Internal\Declaration\WorkflowInstance\SignalQueue;
20-
use Temporal\Internal\Declaration\WorkflowInstance\UpdateQueue;
2121
use Temporal\Internal\Interceptor;
2222

2323
/**
2424
* @psalm-import-type DispatchableHandler from InstanceInterface
2525
* @psalm-type QueryHandler = \Closure(QueryInput): mixed
26-
* @psalm-type UpdateHandler = \Closure(UpdateInput): mixed
26+
* @psalm-type UpdateHandler = \Closure(UpdateInput): PromiseInterface
2727
* @psalm-type ValidateUpdateHandler = \Closure(UpdateInput): void
2828
* @psalm-type QueryExecutor = \Closure(QueryInput, callable(ValuesInterface): mixed): mixed
29-
* @psalm-type UpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): mixed
29+
* @psalm-type UpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): PromiseInterface
3030
* @psalm-type ValidateUpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): mixed
3131
* @psalm-type UpdateValidator = \Closure(UpdateInput, UpdateHandler): void
3232
*/
@@ -65,7 +65,7 @@ final class WorkflowInstance extends Instance implements WorkflowInstanceInterfa
6565

6666
/**
6767
* @param WorkflowPrototype $prototype
68-
* @param object $context
68+
* @param object $context Workflow object
6969
* @param Interceptor\Pipeline<WorkflowInboundCallsInterceptor, mixed> $pipeline
7070
*/
7171
public function __construct(
@@ -141,7 +141,7 @@ public function setUpdateValidator(\Closure $validator): self
141141
*/
142142
public function initConstructor(): void
143143
{
144-
if (method_exists($this->context, '__construct')) {
144+
if (\method_exists($this->context, '__construct')) {
145145
$this->context->__construct();
146146
}
147147
}
@@ -156,8 +156,8 @@ public function getSignalQueue(): SignalQueue
156156

157157
/**
158158
* @param non-empty-string $name
159-
* @return null|\Closure(ValuesInterface):mixed
160159
*
160+
* @return null|\Closure(QueryInput): mixed
161161
* @psalm-return QueryHandler|null
162162
*/
163163
public function findQueryHandler(string $name): ?\Closure
@@ -166,8 +166,10 @@ public function findQueryHandler(string $name): ?\Closure
166166
}
167167

168168
/**
169-
* @param string $name
170-
* @return \Closure
169+
* @param non-empty-string $name
170+
*
171+
* @return null|\Closure(UpdateInput): PromiseInterface
172+
* @psalm-return UpdateHandler|null
171173
*/
172174
public function findUpdateHandler(string $name): ?\Closure
173175
{
@@ -176,6 +178,9 @@ public function findUpdateHandler(string $name): ?\Closure
176178

177179
/**
178180
* @param non-empty-string $name
181+
*
182+
* @return null|\Closure(UpdateInput): void
183+
* @psalm-return ValidateUpdateHandler|null
179184
*/
180185
public function findValidateUpdateHandler(string $name): ?\Closure
181186
{
@@ -234,10 +239,6 @@ public function getUpdateHandlerNames(): array
234239
return \array_keys($this->updateHandlers);
235240
}
236241

237-
/**
238-
* @param string $name
239-
* @return \Closure
240-
*/
241242
public function getSignalHandler(string $name): \Closure
242243
{
243244
return fn (ValuesInterface $values) => $this->signalQueue->push($name, $values);

src/Internal/Declaration/WorkflowInstance/SignalQueue.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ final class SignalQueue
3636
private $onSignal;
3737

3838
/**
39-
* @param string $signal
39+
* @param non-empty-string $signal
4040
* @param ValuesInterface $values
4141
*/
4242
public function push(string $signal, ValuesInterface $values): void

0 commit comments

Comments
 (0)