Skip to content

fix: merge method-level #[MethodRetry] into activity/workflow retry options - #783

Open
xepozz wants to merge 2 commits into
masterfrom
claude/temporal-php-bug-reproduce-963eac
Open

fix: merge method-level #[MethodRetry] into activity/workflow retry options#783
xepozz wants to merge 2 commits into
masterfrom
claude/temporal-php-bug-reproduce-963eac

Conversation

@xepozz

@xepozz xepozz commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

What was changed

Fixed mergeWith() on ActivityOptions, LocalActivityOptions, ChildWorkflowOptions and WorkflowOptions so a method-level #[MethodRetry] attribute is actually merged into the stub's retry options.

The guard was inverted ($this->diff->isPresent(...) is true only when retryOptions is still at its default null):

  • when RetryOptions were set — the merge was skipped and #[MethodRetry] was ignored;
  • when they were not set — null?->mergeWith() collapsed to null and the attribute was lost (a fatal Call to a member function mergeWith() on null on WorkflowOptions, which had no null-safe call).

Now the merge always runs when a MethodRetry is present, delegating field-level precedence to RetryOptions::mergeWith() (user-set fields win, defaults are filled from the attribute) and falling back to a fresh RetryOptions when none is set.

Why?

Restores the documented MethodRetry behaviour — "if RetryOptions are present on ActivityOptions/ChildWorkflowOptions the fields that are not default take precedence over parameters of this attribute" — and matches sdk-go/sdk-java/sdk-typescript. Fixes #777.

Checklist

  1. Closes [Bug] ActivityOptions does not correctly merge RetryOptions with method-level #[MethodRetry] #777

  2. How was this tested:

Added unit tests for mergeWith() covering all four option classes (tests/Unit/DTO/*OptionsTestCase.php): default RetryOptions filled from the attribute, RetryOptions created when unset, user-set fields kept while unset ones are filled, and mergeWith(null) is a no-op. Written first as failing tests, then fixed. Full Unit suite green (1163 tests).

  1. Any docs updates needed?

No.

…ions

The mergeWith() guard on ActivityOptions, LocalActivityOptions,
ChildWorkflowOptions and WorkflowOptions was inverted: it only ran the
merge when retryOptions was still at its default (null), where
null?->mergeWith() collapsed to null, and skipped the merge whenever the
user had set retry options. As a result #[MethodRetry] was ignored when
RetryOptions were present and lost (or fatal on WorkflowOptions) when
they were absent.

Delegate precedence to RetryOptions::mergeWith(), which already keeps
user-set fields and fills defaults from the attribute, and fall back to a
fresh RetryOptions when none is set.

Closes #777
@xepozz
xepozz requested review from a team, roxblnfk and wolfy-j as code owners July 28, 2026 07:05
The PossiblyNullReference on WorkflowOptions::mergeWith is no longer
raised now that the null-safe fallback (?? RetryOptions::new()) replaced
the unguarded $self->retryOptions->mergeWith() call, so the baseline
entry became an UnusedBaselineEntry error on the 8.3 static-analysis job.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] ActivityOptions does not correctly merge RetryOptions with method-level #[MethodRetry]

1 participant