1, 'scan_id' => $scan['id'], 'job_id' => (int) $job['id'], 'state' => $state, 'cursor' => $cursor ); $save( $scan ); self::resume( $store, $scan ); } public static function resume( Store $store, array &$scan ): void { if ( ! array_key_exists( 'work_checkpoint', $scan ) ) { return; } $marker = $scan['work_checkpoint']; if ( ! is_array( $marker ) || 1 !== ( $marker['version'] ?? null ) || ( $marker['scan_id'] ?? null ) !== ( $scan['id'] ?? null ) || ! is_int( $marker['job_id'] ?? null ) || $marker['job_id'] < 1 || ! is_int( $marker['cursor'] ?? null ) || $marker['cursor'] < 0 || ! in_array( $marker['state'] ?? null, array( 'pending', 'done', 'failed', 'skipped' ), true ) ) { throw new \RuntimeException( 'Saved scan work checkpoint is invalid; no queue progress was applied.' ); } $store->finishWorkForScan( $scan['id'], $marker['job_id'], $marker['state'], $marker['cursor'] ); // The next scan save clears this marker. A crash before that save replays only // this already-accounted queue transition, never the completed file analysis. unset( $scan['work_checkpoint'] ); } }