*/ private array $context; /** * @var array> */ private array $logs; /** * @param WorkflowJob $job Queue job. */ public function __construct( WorkflowJob $job ) { $this->job = $job; $this->context = $job->getContext(); $this->logs = $this->normalize_logs( $job->getExecutionResults() ); } /** * @return WorkflowJob */ public function job(): WorkflowJob { return $this->job; } /** * @return int */ public function id(): int { return $this->job->id; } /** * @return string */ public function status(): string { return $this->job->status; } /** * @param string $status Status. * @return void */ public function set_status( string $status ): void { $this->job->status = $status; } /** * @return array */ public function context(): array { $public = $this->context; unset( $public[ self::CONTEXT_KEY ] ); return $public; } /** * Replace public context while preserving runtime state. * * @param array $context Context. * @return void */ public function set_context( array $context ): void { $runtime = $this->runtime_state(); unset( $context[ self::CONTEXT_KEY ] ); $this->context = $context; $this->context[ self::CONTEXT_KEY ] = $runtime; } /** * @return int */ public function current_step_index(): int { return (int) ( $this->runtime_state()['current_step_index'] ?? 0 ); } /** * @param int $index Step index. * @return void */ public function set_current_step_index( int $index ): void { $state = $this->runtime_state(); $state['current_step_index'] = max( 0, $index ); $this->write_runtime_state( $state ); } /** * @return array> */ public function get_steps(): array { $state = $this->runtime_state(); if ( isset( $state['runtime_steps'] ) && is_array( $state['runtime_steps'] ) ) { return $state['runtime_steps']; } if ( isset( $state['initial_runtime_steps'] ) && is_array( $state['initial_runtime_steps'] ) ) { return $state['initial_runtime_steps']; } return array(); } /** * @param array>|null $steps Runtime steps, or null to clear. * @return void */ public function set_runtime_steps( ?array $steps ): void { $state = $this->runtime_state(); if ( null === $steps ) { unset( $state['runtime_steps'] ); } else { $state['runtime_steps'] = array_values( $steps ); } $this->write_runtime_state( $state ); } /** * Seed initial steps when creating a new execution. * * @param array> $steps Steps. * @return void */ public function set_initial_steps( array $steps ): void { $state = $this->runtime_state(); $state['initial_runtime_steps'] = array_values( $steps ); $state['current_step_index'] = $state['current_step_index'] ?? 0; $this->write_runtime_state( $state ); } /** * @return string|null */ public function next_run_at(): ?string { $run_at = trim( (string) $this->job->run_at ); return '' !== $run_at ? $run_at : null; } /** * @param string|null $run_at MySQL datetime or null. * @return void */ public function set_next_run_at( ?string $run_at ): void { $this->job->run_at = $run_at ?? current_time( 'mysql' ); } /** * @return string|null */ public function error_message(): ?string { $message = $this->runtime_state()['error_message'] ?? null; return is_string( $message ) && '' !== $message ? $message : null; } /** * @param string|null $message Error message. * @return void */ public function set_error_message( ?string $message ): void { $state = $this->runtime_state(); if ( null === $message || '' === $message ) { unset( $state['error_message'] ); } else { $state['error_message'] = $message; } $this->write_runtime_state( $state ); } /** * Mark a terminal status and clear runtime resume data. * * @param string $status Status. * @param string|null $message Optional error/stop message. * @return void */ public function set_terminal_state( string $status, ?string $message = null ): void { $this->set_status( $status ); $this->set_next_run_at( current_time( 'mysql' ) ); $this->set_runtime_steps( null ); $state = $this->runtime_state(); $state['completed_at'] = current_time( 'mysql' ); if ( null !== $message && '' !== $message ) { $state['error_message'] = $message; } $this->write_runtime_state( $state ); } /** * Append a step log entry. * * @param int $step_index Step index. * @param string $step_type Step type. * @param string $status Status. * @param string $message Message. * @param array $details Details. * @return void */ public function append_log( int $step_index, string $step_type, string $status, string $message, array $details = array() ): void { $this->logs[] = array( 'step_index' => $step_index, 'type' => $step_type, 'status' => $status, 'message' => $message, 'executed_at' => current_time( 'mysql' ), 'details' => $details, ); } /** * @return array> */ public function logs(): array { return $this->logs; } /** * Context JSON including runtime state (for persistence). * * @return array */ public function context_for_storage(): array { return $this->context; } /** * @return array */ private function runtime_state(): array { $state = $this->context[ self::CONTEXT_KEY ] ?? array(); return is_array( $state ) ? $state : array(); } /** * @param array $state Runtime state. * @return void */ private function write_runtime_state( array $state ): void { $this->context[ self::CONTEXT_KEY ] = $state; } /** * @param array $logs Raw logs. * @return array> */ private function normalize_logs( array $logs ): array { $out = array(); foreach ( $logs as $entry ) { if ( is_array( $entry ) ) { $out[] = $entry; } } return $out; } }