php 62 lines · 9 steps

Tracking task progress in a JSON file

A small PHP class persists long-running task state to a per-task JSON file with atomic writes.

Explained by highlit
1<?php
2 
3final class TaskProgressReporter
4{
5 private string $statusFile;
6 
7 public function __construct(private string $taskId, string $storageDir = '/tmp/tasks')
8 {
9 if (!is_dir($storageDir)) {
10 mkdir($storageDir, 0755, true);
11 }
12 $this->statusFile = rtrim($storageDir, '/') . "/{$taskId}.json";
13 }
14 
15 public function start(int $total): void
16 {
17 $this->write(['state' => 'running', 'done' => 0, 'total' => $total, 'started_at' => time()]);
18 }
19 
20 public function advance(int $done, ?string $message = null): void
21 {
22 $current = $this->read();
23 $current['done'] = $done;
24 $current['percent'] = $current['total'] > 0 ? (int) round($done / $current['total'] * 100) : 0;
25 $current['updated_at'] = time();
26 if ($message !== null) {
27 $current['message'] = $message;
28 }
29 $this->write($current);
30 }
31 
32 public function finish(string $message = 'Completed'): void
33 {
34 $current = $this->read();
35 $current['state'] = 'done';
36 $current['percent'] = 100;
37 $current['done'] = $current['total'];
38 $current['message'] = $message;
39 $current['finished_at'] = time();
40 $this->write($current);
41 }
42 
43 public function fail(string $reason): void
44 {
45 $this->write(array_merge($this->read(), ['state' => 'failed', 'error' => $reason, 'finished_at' => time()]));
46 }
47 
48 public function read(): array
49 {
50 if (!is_file($this->statusFile)) {
51 return ['state' => 'unknown', 'done' => 0, 'total' => 0, 'percent' => 0];
52 }
53 return json_decode((string) file_get_contents($this->statusFile), true) ?? [];
54 }
55 
56 private function write(array $payload): void
57 {
58 $tmp = $this->statusFile . '.tmp';
59 file_put_contents($tmp, json_encode($payload, JSON_PRETTY_PRINT), LOCK_EX);
60 rename($tmp, $this->statusFile);
61 }
62}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Persisting state to a file lets separate processes poll progress without shared memory or a database.
  2. 2Writing to a temp file then renaming makes updates atomic so readers never see a half-written file.
  3. 3Deriving fields like percent from stored values keeps the persisted state consistent and self-describing.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Tracking task progress in a JSON file — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code