true, 'phase' => self::PHASE_QUEUED, 'started_at' => time(), ] ); if ( $total !== null && $total >= 0 ) { $payload['total'] = $total; $payload['phase'] = $total > 0 ? self::PHASE_READING : self::PHASE_QUEUED; } $this->write( $payload ); } public function setTotal( int $total ): void { $progress = $this->current(); $progress['running'] = true; $progress['total'] = $total; $progress['processed'] = 0; $progress['phase'] = self::PHASE_READING; $this->write( $progress ); } public function tickReading( int $processed ): void { $progress = $this->current(); $progress['running'] = true; $progress['phase'] = self::PHASE_READING; $progress['processed'] = $processed; $this->write( $progress ); } public function indexing( string $sessionId, int $processed ): void { $progress = $this->current(); $progress['running'] = true; $progress['phase'] = self::PHASE_INDEXING; $progress['processed'] = $processed; $progress['session_id'] = $sessionId; $progress['indexing_since'] = time(); $progress['checked_at'] = null; $this->write( $progress ); } public function indexingChecked( int $processedBatches, ?int $expectedBatches ): void { $progress = $this->current(); $progress['checked_at'] = time(); $progress['processed_batches'] = $processedBatches; $progress['expected_batches'] = $expectedBatches; $this->write( $progress ); } public function finish( bool $success, string $message, int $processed ): void { $progress = $this->current(); $progress['running'] = false; $progress['phase'] = $success ? self::PHASE_DONE : self::PHASE_ERROR; $progress['processed'] = $processed; $progress['message'] = $message; $progress['finished_at'] = time(); $this->write( $progress ); } public function fail( string $message ): void { $progress = $this->current(); $progress['running'] = false; $progress['phase'] = self::PHASE_ERROR; $progress['message'] = $message; $progress['finished_at'] = time(); $this->write( $progress ); } public function isRunning(): bool { return (bool) ( $this->current()['running'] ?? false ); } public function current(): array { $raw = \get_option( self::OPTION, null ); if ( ! is_array( $raw ) ) { return self::defaults(); } $progress = array_merge( self::defaults(), $raw ); $updatedAt = (int) ( $progress['updated_at'] ?? $progress['started_at'] ?? 0 ); if ( $progress['running'] && $progress['phase'] !== self::PHASE_INDEXING && $updatedAt > 0 && time() - $updatedAt > SyncRunner::LOCK_TTL_SECONDS ) { $progress['running'] = false; $progress['phase'] = self::PHASE_ERROR; $progress['message'] = \__( 'Synchronization timed out. Please try again.', 'agent001-support-assistant-for-woocommerce' ); $progress['finished_at'] = time(); $this->write( $progress ); } return $progress; } private static function defaults(): array { return [ 'running' => false, 'phase' => self::PHASE_IDLE, 'processed' => 0, 'total' => 0, 'message' => '', 'started_at' => null, 'updated_at' => null, 'finished_at' => null, 'session_id' => null, 'indexing_since' => null, 'checked_at' => null, 'processed_batches' => 0, 'expected_batches' => null, ]; } private function write( array $progress ): void { $progress['updated_at'] = time(); if ( $progress['running'] && \get_transient( SyncRunner::LOCK_TRANSIENT ) ) { \set_transient( SyncRunner::LOCK_TRANSIENT, 1, SyncRunner::LOCK_TTL_SECONDS ); } \update_option( self::OPTION, $progress, false ); } }