temporal-php / support
Helpers that simplify working with the Temporal PHP SDK
Installs: 2 275
Dependents: 0
Suggesters: 0
Security: 0
Stars: 9
Watchers: 1
Forks: 1
Open Issues: 5
Requires
- php: >=8.1
- temporal/sdk: ^2.8
Requires (Dev)
- buggregator/trap: ^1.4
- dereuromark/composer-prefer-lowest: ^0.1.10
- pestphp/pest: ^2.34
- pestphp/pest-plugin-arch: ^2.7
- phpunit/phpunit: ^10.5
- vimeo/psalm: ^5.23
Suggests
- buggregator/trap: For better debugging and protobuf messages dumping
README
Temporal PHP Support
Enhance your development experience with TemporalThe package includes attributes, helpers, factories, interfaces, interceptors, etc. to enhance the developer experience when using the Temporal PHP SDK.
Installation
To install the package in your PHP application, add it as a dev dependency to your project using Composer:
composer require temporal-php/support
Usage
Factories
The package provides factories to create Activity and Worker stubs in a more convenient way. With these factories, there is less code because all nested options are moved to the parameters of one method.
Use the \Temporal\Support\Factory\ActivityStub
factory to create an Activity stub:
use \Temporal\Support\Factory\ActivityStub; #[\Temporal\Workflow\WorkflowInterface] class HelloWorkflow { #[\Temporal\Workflow\WorkflowMethod] public function run(string $user) { yield ActivityStub::activity( class: UserService::class, startToCloseTimeout: 60, retryAttempts: 5, )->getContactEmail($user)->then( fn (string $email) => ActivityStub::activity( class: HelloService::class, startToCloseTimeout: '10 minutes', retryAttempts: 5, )->sendHelloEmail($user, $email), ); } }
Use the \Temporal\Support\Factory\WorkflowStub
factory to create a Workflow stub in a client scope:
use \Temporal\Support\Factory\WorkflowStub; /** * @var \Temporal\Client\WorkflowClient $client */ $stub = WorkflowStub::workflow($client, HelloWorkflow::class, executionTimeout: '1 day'); $run = $client->start($stub, 'User'); // ...
Or create a Child Workflow stub in a workflow scope:
use \Temporal\Support\Factory\WorkflowStub; #[\Temporal\Workflow\WorkflowInterface] class RegisterWorkflow { #[\Temporal\Workflow\WorkflowMethod] public function run(string $user) { yield \Temporal\Promise::all([ WorkflowStub::childWorkflow(GreetingWorkflow::class, executionTimeout: '1 hour')->greet($user), WorkflowStub::childWorkflow(SubscribeNewsWorkflow::class, executionTimeout: '10 minutes')->subscribe($user), WorkflowStub::childWorkflow(PrepareUserSpaceWorkflow::class, executionTimeout: '1 hour')->handle($user), ])->then( // Suppress failures onRejected: static fn () => null, ); // ... } }
Attributes
Attributes can be used on Workflow or Activity definitions to set default stub options.
#[\Temporal\Support\Attribute\TaskQueue('my-task-queue')] #[\Temporal\Support\Attribute\RetryPolicy(attempts: 5)] #[WorkflowInterface] interface HelloWorkflow { #[WorkflowMethod] public function greet(string $name); } $stub = \Temporal\Support\Factory\WorkflowStub::workflow($client, HelloWorkflow::class); // TaskQueue is now set to 'my-task-queue' and RetryPolicy to 5 attempts $stub->greet('User'); // You can override the default options $stub = \Temporal\Support\Factory\WorkflowStub::workflow( $client, HelloWorkflow::class, taskQueue: 'another-task-queue', retryAttempts: 1, )->greet('User');
Note
Attributes will work only if you use the Activity and Worker factories from this package.
Warning
Use attributes on the definitions that you use in factories. So, if you separate interfaces and implementation, apply attributes to the interfaces.
VirtualPromise interface
Every time we use yield
in a Workflow to wait for an action to complete, a Promise is actually yielded.
At this point, the IDE and static analyzer usually get lost in type definitions,
and we experience difficulties and inconveniences because of this.
However, if the Promise interface had the @yield
annotation,
we could explain to the IDE what type of value we expect to be sent back into the generator from the coroutine.
Since ReactPHP isn't yet planning
to add the @yield
annotation to their promises
(Temporal PHP uses ReactPHP promises),
we suggest using our solution for typing - VirtualPromise
.
use Temporal\Support\VirtualPromise; #[\Temporal\Activity\ActivityInterface] class HelloService { /** * @param non-empty-string $name * * @return VirtualPromise<non-empty-string> */ public function greet(string $name) { // ... } } #[\Temporal\Workflow\WorkflowInterface] class WorkflowClass { #[\Temporal\Workflow\WorkflowMethod] public function run(string $name) { $activity = \Temporal\Support\Factory\ActivityStub::activity(HelloService::class); // IDE will know that $name is a non-empty-string $name = yield $activity->greet($name); // ... } }
Warning
don't implement the VirtualPromise
interface yourself, use it only as a type hint.
Note
PHPStorm and Psalm can handle the @yield
annotation, but PHPStan can't yet (issue).
Contributing
We believe in the power of community-driven development. Here's how you can contribute:
- Report Bugs: Encounter a glitch? Let us know on our issue tracker.
- Feature Suggestions: Have ideas to improve the package? Create a feature request!
- Code Contributions: Submit a pull request to help us improve the codebase. You can find a list of issues labeled "help wanted" here.
- Spread the Word: Share your experience with the package on social media and encourage others to contribute.
- Donate: Support our work by becoming a patron or making a one-time donation