diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 045712fb..e6ccb4cd 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -509,9 +509,6 @@
-
- getInstance($prototype)]]>
-
@@ -527,12 +524,6 @@
-
- getInstance($prototype)]]>
-
-
-
-
@@ -595,12 +586,10 @@
-
-
@@ -615,7 +604,6 @@
-
@@ -752,13 +740,9 @@
-
-
-
-
@@ -1144,10 +1128,6 @@
-
-
-
-
diff --git a/src/Internal/Declaration/Dispatcher/AutowiredPayloads.php b/src/Internal/Declaration/Dispatcher/AutowiredPayloads.php
index f7a5f11e..e659c933 100644
--- a/src/Internal/Declaration/Dispatcher/AutowiredPayloads.php
+++ b/src/Internal/Declaration/Dispatcher/AutowiredPayloads.php
@@ -16,15 +16,11 @@
/**
* @psalm-type FunctionExecutor = \Closure(object|null, array): mixed
+ * @internal
*/
class AutowiredPayloads extends Dispatcher
{
- /**
- * @param object|null $ctx
- * @param ValuesInterface $values
- * @return mixed
- */
- public function dispatchValues(?object $ctx, ValuesInterface $values)
+ public function dispatchValues(object $ctx, ValuesInterface $values): mixed
{
$arguments = [];
for ($i = 0; $i < $values->count(); $i++) {
diff --git a/src/Internal/Declaration/Dispatcher/Dispatcher.php b/src/Internal/Declaration/Dispatcher/Dispatcher.php
index e50bd8ef..2db1af61 100644
--- a/src/Internal/Declaration/Dispatcher/Dispatcher.php
+++ b/src/Internal/Declaration/Dispatcher/Dispatcher.php
@@ -15,7 +15,7 @@
use ReflectionType;
/**
- * @psalm-type FunctionExecutor = \Closure(object|null, array): mixed
+ * @psalm-type FunctionExecutor = \Closure(object, array): mixed
*/
class Dispatcher implements DispatcherInterface
{
@@ -30,8 +30,8 @@ class Dispatcher implements DispatcherInterface
public const SCOPE_STATIC = 0x02;
/**
+ * @var \Closure(object, array): mixed
* @psalm-var FunctionExecutor
- * @var \Closure
*/
private \Closure $executor;
@@ -95,12 +95,7 @@ public function getArgumentTypes(): array
return $this->types;
}
- /**
- * @param object|null $ctx
- * @param array $arguments
- * @return mixed
- */
- public function dispatch(?object $ctx, array $arguments)
+ public function dispatch(object $ctx, array $arguments): mixed
{
return ($this->executor)($ctx, $arguments);
}
@@ -119,13 +114,13 @@ private function scopeMatches(int $scope): bool
* @psalm-return FunctionExecutor
*
* @param \ReflectionMethod $fun
- * @return \Closure
+ * @return \Closure(object, array): mixed
*/
private function createExecutorFromMethod(\ReflectionMethod $fun): \Closure
{
- return static function (?object $ctx, array $arguments) use ($fun) {
+ return static function (object $object, array $arguments) use ($fun) {
try {
- return $fun->invokeArgs($ctx, $arguments);
+ return $fun->invokeArgs($object, $arguments);
} catch (\ReflectionException $e) {
throw new \BadMethodCallException($e->getMessage(), $e->getCode(), $e);
}
@@ -136,15 +131,11 @@ private function createExecutorFromMethod(\ReflectionMethod $fun): \Closure
* @psalm-return FunctionExecutor
*
* @param \ReflectionFunction $fun
- * @return \Closure
+ * @return \Closure(object, array): mixed
*/
private function createExecutorFromFunction(\ReflectionFunction $fun): \Closure
{
- return static function (?object $ctx, array $arguments) use ($fun) {
- if ($ctx === null) {
- return $fun->invoke(...$arguments);
- }
-
+ return static function (object $ctx, array $arguments) use ($fun) {
$closure = $fun->getClosure();
try {
diff --git a/src/Internal/Declaration/Dispatcher/DispatcherInterface.php b/src/Internal/Declaration/Dispatcher/DispatcherInterface.php
index 3a60fef5..695c7ec9 100644
--- a/src/Internal/Declaration/Dispatcher/DispatcherInterface.php
+++ b/src/Internal/Declaration/Dispatcher/DispatcherInterface.php
@@ -11,14 +11,12 @@
namespace Temporal\Internal\Declaration\Dispatcher;
+/**
+ * @internal
+ */
interface DispatcherInterface
{
- /**
- * @param object|null $ctx
- * @param array $arguments
- * @return mixed
- */
- public function dispatch(?object $ctx, array $arguments);
+ public function dispatch(object $ctx, array $arguments): mixed;
/**
* @return array<\ReflectionType>
diff --git a/src/Internal/Declaration/Instance.php b/src/Internal/Declaration/Instance.php
index 44394068..f34b1d7d 100644
--- a/src/Internal/Declaration/Instance.php
+++ b/src/Internal/Declaration/Instance.php
@@ -21,18 +21,15 @@
*/
abstract class Instance implements InstanceInterface
{
- protected object $context;
/**
* @var \Closure(ValuesInterface): mixed
*/
private \Closure $handler;
- /**
- * @param Prototype $prototype
- * @param object $context
- */
- public function __construct(Prototype $prototype, object $context)
- {
+ public function __construct(
+ Prototype $prototype,
+ protected readonly object $context,
+ ) {
$handler = $prototype->getHandler();
if ($handler === null) {
@@ -42,14 +39,10 @@ public function __construct(Prototype $prototype, object $context)
));
}
- $this->context = $context;
$this->handler = $this->createHandler($handler);
}
- /**
- * @return object|null
- */
- public function getContext(): ?object
+ public function getContext(): object
{
return $this->context;
}
diff --git a/src/Internal/Declaration/InstanceInterface.php b/src/Internal/Declaration/InstanceInterface.php
index b8f23c99..d58c714d 100644
--- a/src/Internal/Declaration/InstanceInterface.php
+++ b/src/Internal/Declaration/InstanceInterface.php
@@ -23,8 +23,5 @@ interface InstanceInterface
*/
public function getHandler(): callable;
- /**
- * @return object|null
- */
- public function getContext(): ?object;
+ public function getContext(): object;
}
diff --git a/src/Internal/Declaration/Instantiator/Instantiator.php b/src/Internal/Declaration/Instantiator/Instantiator.php
index 89b03aca..ebde7d2c 100644
--- a/src/Internal/Declaration/Instantiator/Instantiator.php
+++ b/src/Internal/Declaration/Instantiator/Instantiator.php
@@ -17,24 +17,11 @@ abstract class Instantiator implements InstantiatorInterface
{
/**
* @param PrototypeInterface $prototype
- * @return \ReflectionClass|null
- */
- protected function getClass(PrototypeInterface $prototype): ?\ReflectionClass
- {
- return $prototype->getClass();
- }
-
- /**
- * @param PrototypeInterface $prototype
- * @return object|null
+ * @return object
* @throws \ReflectionException
*/
- protected function getInstance(PrototypeInterface $prototype): ?object
+ protected function getInstance(PrototypeInterface $prototype): object
{
- if ($class = $this->getClass($prototype)) {
- return $class->newInstance();
- }
-
- return null;
+ return $prototype->getClass()->newInstance();
}
}
diff --git a/src/Internal/Declaration/Instantiator/WorkflowInstantiator.php b/src/Internal/Declaration/Instantiator/WorkflowInstantiator.php
index fcb56eb4..028ee115 100644
--- a/src/Internal/Declaration/Instantiator/WorkflowInstantiator.php
+++ b/src/Internal/Declaration/Instantiator/WorkflowInstantiator.php
@@ -12,6 +12,7 @@
namespace Temporal\Internal\Declaration\Instantiator;
use Temporal\Exception\InstantiationException;
+use Temporal\Interceptor\PipelineProvider;
use Temporal\Interceptor\WorkflowInboundCallsInterceptor;
use Temporal\Internal\Declaration\Prototype\PrototypeInterface;
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
@@ -23,7 +24,7 @@
final class WorkflowInstantiator extends Instantiator
{
public function __construct(
- private \Temporal\Interceptor\PipelineProvider $interceptorProvider,
+ private PipelineProvider $interceptorProvider,
) {
}
@@ -43,26 +44,16 @@ public function instantiate(PrototypeInterface $prototype): WorkflowInstance
/**
* @param PrototypeInterface $prototype
- * @return object|null
+ * @return object
* @throws \ReflectionException
*/
- protected function getInstance(PrototypeInterface $prototype): ?object
+ protected function getInstance(PrototypeInterface $prototype): object
{
- $handler = $prototype->getHandler();
+ $handler = $prototype->getHandler() ?? throw new InstantiationException(\sprintf(
+ 'Unable to instantiate workflow "%s" without handler method',
+ $prototype->getID(),
+ ));
- if ($handler === null) {
- throw new InstantiationException(\sprintf(
- 'Unable to instantiate workflow "%s" without handler method',
- $prototype->getID(),
- ));
- }
-
- $class = $handler->getDeclaringClass();
-
- if ($class !== null) {
- return $class->newInstanceWithoutConstructor();
- }
-
- return null;
+ return $handler->getDeclaringClass()->newInstanceWithoutConstructor();
}
}
diff --git a/src/Internal/Declaration/WorkflowInstance.php b/src/Internal/Declaration/WorkflowInstance.php
index bbe3ca9b..5cc99271 100644
--- a/src/Internal/Declaration/WorkflowInstance.php
+++ b/src/Internal/Declaration/WorkflowInstance.php
@@ -11,22 +11,22 @@
namespace Temporal\Internal\Declaration;
+use React\Promise\PromiseInterface;
use Temporal\DataConverter\ValuesInterface;
use Temporal\Interceptor\WorkflowInbound\QueryInput;
use Temporal\Interceptor\WorkflowInbound\UpdateInput;
use Temporal\Interceptor\WorkflowInboundCallsInterceptor;
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
use Temporal\Internal\Declaration\WorkflowInstance\SignalQueue;
-use Temporal\Internal\Declaration\WorkflowInstance\UpdateQueue;
use Temporal\Internal\Interceptor;
/**
* @psalm-import-type DispatchableHandler from InstanceInterface
* @psalm-type QueryHandler = \Closure(QueryInput): mixed
- * @psalm-type UpdateHandler = \Closure(UpdateInput): mixed
+ * @psalm-type UpdateHandler = \Closure(UpdateInput): PromiseInterface
* @psalm-type ValidateUpdateHandler = \Closure(UpdateInput): void
* @psalm-type QueryExecutor = \Closure(QueryInput, callable(ValuesInterface): mixed): mixed
- * @psalm-type UpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): mixed
+ * @psalm-type UpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): PromiseInterface
* @psalm-type ValidateUpdateExecutor = \Closure(UpdateInput, callable(ValuesInterface): mixed): mixed
* @psalm-type UpdateValidator = \Closure(UpdateInput, UpdateHandler): void
*/
@@ -65,7 +65,7 @@ final class WorkflowInstance extends Instance implements WorkflowInstanceInterfa
/**
* @param WorkflowPrototype $prototype
- * @param object $context
+ * @param object $context Workflow object
* @param Interceptor\Pipeline $pipeline
*/
public function __construct(
@@ -141,7 +141,7 @@ public function setUpdateValidator(\Closure $validator): self
*/
public function initConstructor(): void
{
- if (method_exists($this->context, '__construct')) {
+ if (\method_exists($this->context, '__construct')) {
$this->context->__construct();
}
}
@@ -156,8 +156,8 @@ public function getSignalQueue(): SignalQueue
/**
* @param non-empty-string $name
- * @return null|\Closure(ValuesInterface):mixed
*
+ * @return null|\Closure(QueryInput): mixed
* @psalm-return QueryHandler|null
*/
public function findQueryHandler(string $name): ?\Closure
@@ -166,8 +166,10 @@ public function findQueryHandler(string $name): ?\Closure
}
/**
- * @param string $name
- * @return \Closure
+ * @param non-empty-string $name
+ *
+ * @return null|\Closure(UpdateInput): PromiseInterface
+ * @psalm-return UpdateHandler|null
*/
public function findUpdateHandler(string $name): ?\Closure
{
@@ -176,6 +178,9 @@ public function findUpdateHandler(string $name): ?\Closure
/**
* @param non-empty-string $name
+ *
+ * @return null|\Closure(UpdateInput): void
+ * @psalm-return ValidateUpdateHandler|null
*/
public function findValidateUpdateHandler(string $name): ?\Closure
{
@@ -234,10 +239,6 @@ public function getUpdateHandlerNames(): array
return \array_keys($this->updateHandlers);
}
- /**
- * @param string $name
- * @return \Closure
- */
public function getSignalHandler(string $name): \Closure
{
return fn (ValuesInterface $values) => $this->signalQueue->push($name, $values);
diff --git a/src/Internal/Declaration/WorkflowInstance/SignalQueue.php b/src/Internal/Declaration/WorkflowInstance/SignalQueue.php
index afa98fbf..13e3b17d 100644
--- a/src/Internal/Declaration/WorkflowInstance/SignalQueue.php
+++ b/src/Internal/Declaration/WorkflowInstance/SignalQueue.php
@@ -36,7 +36,7 @@ final class SignalQueue
private $onSignal;
/**
- * @param string $signal
+ * @param non-empty-string $signal
* @param ValuesInterface $values
*/
public function push(string $signal, ValuesInterface $values): void
diff --git a/src/Internal/Declaration/WorkflowInstanceInterface.php b/src/Internal/Declaration/WorkflowInstanceInterface.php
index 1b07f3cf..1c9862f8 100644
--- a/src/Internal/Declaration/WorkflowInstanceInterface.php
+++ b/src/Internal/Declaration/WorkflowInstanceInterface.php
@@ -11,6 +11,11 @@
namespace Temporal\Internal\Declaration;
+use React\Promise\PromiseInterface;
+use Temporal\DataConverter\ValuesInterface;
+use Temporal\Interceptor\WorkflowInbound\QueryInput;
+use Temporal\Interceptor\WorkflowInbound\UpdateInput;
+
interface WorkflowInstanceInterface extends InstanceInterface
{
/**
@@ -19,8 +24,8 @@ interface WorkflowInstanceInterface extends InstanceInterface
public function initConstructor(): void;
/**
- * @param string $name
- * @return \Closure|null
+ * @param non-empty-string $name
+ * @return null|\Closure(QueryInput): mixed
*/
public function findQueryHandler(string $name): ?\Closure;
@@ -37,14 +42,14 @@ public function addQueryHandler(string $name, callable $handler): void;
public function addUpdateHandler(string $name, callable $handler): void;
/**
- * @param string $name
- * @return \Closure
+ * @param non-empty-string $name
+ * @return \Closure(ValuesInterface): void
*/
public function getSignalHandler(string $name): \Closure;
/**
* @param non-empty-string $name
- * @return \Closure
+ * @return null|\Closure(UpdateInput): PromiseInterface
*/
public function findUpdateHandler(string $name): ?\Closure;
diff --git a/src/Internal/Marshaller/Type/ArrayType.php b/src/Internal/Marshaller/Type/ArrayType.php
index 3edadda6..271331a4 100644
--- a/src/Internal/Marshaller/Type/ArrayType.php
+++ b/src/Internal/Marshaller/Type/ArrayType.php
@@ -69,11 +69,11 @@ public static function makeRule(\ReflectionProperty $property): ?MarshallingRule
}
/**
- * @param array $value
+ * @psalm-assert array $value
+ * @param mixed $value
* @param array $current
- * @return array|mixed
*/
- public function parse($value, $current)
+ public function parse($value, $current): array
{
if (!\is_array($value)) {
throw new \InvalidArgumentException(\sprintf(self::ERROR_INVALID_TYPE, \get_debug_type($value)));
diff --git a/src/Internal/Transport/Router/InvokeQuery.php b/src/Internal/Transport/Router/InvokeQuery.php
index 4036ddda..9ce45a1a 100644
--- a/src/Internal/Transport/Router/InvokeQuery.php
+++ b/src/Internal/Transport/Router/InvokeQuery.php
@@ -52,6 +52,7 @@ public function __construct(
*/
public function handle(ServerRequestInterface $request, array $headers, Deferred $resolver): void
{
+ /** @var non-empty-string $name */
$name = $request->getOptions()['name'];
$process = $this->findProcessOrFail($request->getID());
$context = $process->getContext();
@@ -79,19 +80,17 @@ static function () use ($name, $request, $resolver, $handler, $context): void {
/**
* @param WorkflowInstanceInterface $instance
- * @param string $name
- * @return \Closure
+ * @param non-empty-string $name
+ * @return \Closure(QueryInput): mixed
*/
private function findQueryHandlerOrFail(WorkflowInstanceInterface $instance, string $name): \Closure
{
- $handler = $instance->findQueryHandler($name);
-
- if ($handler === null) {
- $available = \implode(' ', $instance->getQueryHandlerNames());
-
- throw new \LogicException(\sprintf(self::ERROR_QUERY_NOT_FOUND, $name, $available));
- }
-
- return $handler;
+ return $instance->findQueryHandler($name) ?? throw new \LogicException(
+ \sprintf(
+ self::ERROR_QUERY_NOT_FOUND,
+ $name,
+ \implode(' ', $instance->getQueryHandlerNames())
+ ),
+ );
}
}
diff --git a/src/Internal/Transport/Router/InvokeUpdate.php b/src/Internal/Transport/Router/InvokeUpdate.php
index f73d8459..60858c76 100644
--- a/src/Internal/Transport/Router/InvokeUpdate.php
+++ b/src/Internal/Transport/Router/InvokeUpdate.php
@@ -36,6 +36,7 @@ public function handle(ServerRequestInterface $request, array $headers, Deferred
try {
$instance = $process->getWorkflowInstance();
+ /** @var non-empty-string $name */
$name = $request->getOptions()['name'];
$handler = $this->getUpdateHandler($instance, $name);
/** @psalm-suppress InaccessibleProperty */
@@ -86,12 +87,11 @@ public function handle(ServerRequestInterface $request, array $headers, Deferred
return;
}
- // There validation is passed
+ // Validation has passed
- /** @var PromiseInterface $promise */
$promise = $handler($input);
$promise->then(
- static function (mixed $value) use ($updateId, $context, $resolver): void {
+ static function (mixed $value) use ($updateId, $context): void {
$context->getClient()->send(new UpdateResponse(
command: UpdateResponse::COMMAND_COMPLETED,
values: EncodedValues::fromValues([$value]),
@@ -99,7 +99,7 @@ static function (mixed $value) use ($updateId, $context, $resolver): void {
updateId: $updateId,
));
},
- static function (\Throwable $err) use ($updateId, $context, $resolver): void {
+ static function (\Throwable $err) use ($updateId, $context): void {
$context->getClient()->send(new UpdateResponse(
command: UpdateResponse::COMMAND_COMPLETED,
values: null,
@@ -112,6 +112,7 @@ static function (\Throwable $err) use ($updateId, $context, $resolver): void {
/**
* @param non-empty-string $name
+ * @return \Closure(UpdateInput): PromiseInterface
*/
private function getUpdateHandler(WorkflowInstanceInterface $instance, string $name): \Closure
{
diff --git a/src/Internal/Workflow/Process/Process.php b/src/Internal/Workflow/Process/Process.php
index 588d9db2..37440778 100644
--- a/src/Internal/Workflow/Process/Process.php
+++ b/src/Internal/Workflow/Process/Process.php
@@ -139,7 +139,7 @@ function (SignalInput $input) use ($handler) {
)->onClose(
function (?\Throwable $error): void {
if ($error !== null) {
- // we want to fail process when signal scope fails
+ // Fail process when signal scope fails
$this->complete($error);
}
}
diff --git a/src/Internal/Workflow/Process/Scope.php b/src/Internal/Workflow/Process/Scope.php
index bd1671f8..a9671b5b 100644
--- a/src/Internal/Workflow/Process/Scope.php
+++ b/src/Internal/Workflow/Process/Scope.php
@@ -28,7 +28,6 @@
use Temporal\Worker\Transport\Command\RequestInterface;
use Temporal\Workflow;
use Temporal\Workflow\CancellationScopeInterface;
-use Temporal\Workflow\WorkflowContextInterface;
/**
* Unlike Java implementation, PHP has merged coroutine and cancellation scope into a single instance.
@@ -447,6 +446,7 @@ protected function makeCurrent(): void
protected function next(): void
{
$this->makeCurrent();
+ begin:
$this->context->resolveConditions();
if (!$this->coroutine->valid()) {
@@ -482,6 +482,7 @@ protected function next(): void
default:
$this->coroutine->send($current);
+ goto begin;
}
}
diff --git a/src/Internal/Workflow/ProcessCollection.php b/src/Internal/Workflow/ProcessCollection.php
index 4006a932..344ab782 100644
--- a/src/Internal/Workflow/ProcessCollection.php
+++ b/src/Internal/Workflow/ProcessCollection.php
@@ -12,7 +12,6 @@
namespace Temporal\Internal\Workflow;
use Temporal\Internal\Repository\ArrayRepository;
-use Temporal\Internal\Transport\ClientInterface;
use Temporal\Internal\Workflow\Process\Process;
/**
@@ -22,11 +21,6 @@ class ProcessCollection extends ArrayRepository
{
private const ERROR_PROCESS_NOT_FOUND = 'Process #%s not found.';
- public function __construct()
- {
- parent::__construct();
- }
-
/**
* @param string $runId
* @param non-empty-string|null $error Error message if the process was not found.
diff --git a/tests/Fixtures/src/Workflow/YieldScalarsWorkflow.php b/tests/Fixtures/src/Workflow/YieldScalarsWorkflow.php
new file mode 100644
index 00000000..a5eec719
--- /dev/null
+++ b/tests/Fixtures/src/Workflow/YieldScalarsWorkflow.php
@@ -0,0 +1,30 @@
+fail('LocalActivity not found in history');
}
+ public function testYieldNonPromises(): void
+ {
+ $workflow = $this->workflowClient->newWorkflowStub(YieldScalarsWorkflow::class);
+ $run = $this->workflowClient->start($workflow, ['hello', 'world', '!']);
+ $this->assertSame([ 'hello', 'world', '!'], $run->getResult('array'));
+ }
+
private function assertContainsEvent(WorkflowExecution $execution, int $event): void
{
$history = $this->workflowClient->getWorkflowHistory(
diff --git a/tests/Unit/Worker/AutowiringTestCase.php b/tests/Unit/Worker/AutowiringTestCase.php
index 3b20607e..83a25865 100644
--- a/tests/Unit/Worker/AutowiringTestCase.php
+++ b/tests/Unit/Worker/AutowiringTestCase.php
@@ -83,19 +83,4 @@ public function testInstanceCallMethodInvocation(\ReflectionFunctionAbstract $fn
$this->assertSame(0xDEAD_BEEF, $handler->dispatch($this, []));
}
-
- #[TestDox("Checks invocation without an object context or exception otherwise (if object context required)")]
- #[DataProvider('reflectionDataProvider')]
- public function testStaticCallMethodInvocation(\ReflectionFunctionAbstract $fn): void
- {
- $handler = new AutowiredPayloads($fn, new DataConverter(new JsonConverter()));
-
- // If the object context is required, then the method invocation without
- // "this" context should return an BadMethodCallException error.
- if ($handler->isObjectContextRequired()) {
- $this->expectException(\BadMethodCallException::class);
- }
-
- $this->assertSame(0xDEAD_BEEF, $handler->dispatch(null, []));
- }
}