@@ -167,7 +167,7 @@ public function addStep(
167167 string $ id ,
168168 string |WorkflowAction $ action ,
169169 array $ config = [],
170- ? int $ timeout = null ,
170+ string | int | null $ timeout = null ,
171171 int $ retryAttempts = 0
172172 ): self {
173173 if (empty (trim ($ id ))) {
@@ -178,8 +178,19 @@ public function addStep(
178178 throw InvalidWorkflowDefinitionException::invalidRetryAttempts ($ retryAttempts );
179179 }
180180
181- if ($ timeout !== null && $ timeout <= 0 ) {
182- throw InvalidWorkflowDefinitionException::invalidTimeout ($ timeout );
181+ // Validate timeout format if provided
182+ if ($ timeout !== null ) {
183+ if (is_string ($ timeout )) {
184+ // Validate the string format and convert to ensure it's valid
185+ $ timeoutSeconds = $ this ->parseTimeoutString ($ timeout );
186+ if ($ timeoutSeconds <= 0 ) {
187+ throw InvalidWorkflowDefinitionException::invalidTimeout ($ timeout );
188+ }
189+ } else {
190+ if ($ timeout <= 0 ) {
191+ throw InvalidWorkflowDefinitionException::invalidTimeout ($ timeout );
192+ }
193+ }
183194 }
184195
185196 // Check for duplicate step IDs
@@ -193,7 +204,7 @@ public function addStep(
193204 'id ' => $ id ,
194205 'action ' => is_string ($ action ) ? $ action : $ action ::class,
195206 'config ' => $ config ,
196- 'timeout ' => $ timeout ,
207+ 'timeout ' => $ timeout , // Store original format, not converted
197208 'retry_attempts ' => $ retryAttempts ,
198209 ];
199210
@@ -217,7 +228,7 @@ public function addStep(
217228 public function startWith (
218229 string |WorkflowAction $ action ,
219230 array $ config = [],
220- ? int $ timeout = null ,
231+ string | int | null $ timeout = null ,
221232 int $ retryAttempts = 0
222233 ): self {
223234 $ stepId = 'step_ ' .(count ($ this ->steps ) + 1 );
@@ -242,7 +253,7 @@ public function startWith(
242253 public function then (
243254 string |WorkflowAction $ action ,
244255 array $ config = [],
245- ? int $ timeout = null ,
256+ string | int | null $ timeout = null ,
246257 int $ retryAttempts = 0
247258 ): self {
248259 $ stepId = 'step_ ' .(count ($ this ->steps ) + 1 );
@@ -459,11 +470,21 @@ public function build(): WorkflowDefinition
459470 // Convert builder format to Step objects
460471 $ steps = [];
461472 foreach ($ this ->steps as $ stepData ) {
473+ // Convert timeout to string format for Step object - keep original format if string, convert integer to string
474+ $ timeoutString = null ;
475+ if ($ stepData ['timeout ' ] !== null ) {
476+ if (is_string ($ stepData ['timeout ' ])) {
477+ $ timeoutString = $ stepData ['timeout ' ]; // Keep original string format
478+ } else {
479+ $ timeoutString = (string ) $ stepData ['timeout ' ]; // Convert integer to string
480+ }
481+ }
482+
462483 $ steps [] = new Step (
463484 id: $ stepData ['id ' ],
464485 actionClass: $ stepData ['action ' ],
465486 config: $ stepData ['config ' ],
466- timeout: $ stepData [ ' timeout ' ] ? ( string ) $ stepData [ ' timeout ' ] : null ,
487+ timeout: $ timeoutString ,
467488 retryAttempts: $ stepData ['retry_attempts ' ],
468489 conditions: isset ($ stepData ['condition ' ]) ? [$ stepData ['condition ' ]] : []
469490 );
@@ -498,6 +519,31 @@ public static function quick(): QuickWorkflowBuilder
498519 {
499520 return new QuickWorkflowBuilder ;
500521 }
522+
523+ /**
524+ * Convert timeout string to seconds.
525+ *
526+ * @param string $timeout Timeout string like '30s', '5m', '2h', '1d'
527+ * @return int Timeout in seconds
528+ *
529+ * @throws InvalidWorkflowDefinitionException If format is invalid
530+ */
531+ private function parseTimeoutString (string $ timeout ): int
532+ {
533+ if (! preg_match ('/^(\d+)([smhd])$/ ' , $ timeout , $ matches )) {
534+ throw InvalidWorkflowDefinitionException::invalidTimeout ($ timeout );
535+ }
536+
537+ $ value = (int ) $ matches [1 ];
538+ $ unit = $ matches [2 ];
539+
540+ return match ($ unit ) {
541+ 's ' => $ value ,
542+ 'm ' => $ value * 60 ,
543+ 'h ' => $ value * 3600 ,
544+ 'd ' => $ value * 86400 ,
545+ };
546+ }
501547}
502548
503549/**
0 commit comments