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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Persisting state to a file lets separate processes poll progress without shared memory or a database.
- 2Writing to a temp file then renaming makes updates atomic so readers never see a half-written file.
- 3Deriving fields like percent from stored values keeps the persisted state consistent and self-describing.
Related explainers
php
<?php namespace App\Http\Requests\DataObjects;
Typed request DTOs in Laravel
data-transfer-object
validation
immutability
Intermediate
6 steps
php
<?php namespace App\Jobs;
Rendering invoice PDFs in a Laravel queue job
queued jobs
pdf generation
file storage
Intermediate
9 steps
php
<?php namespace App\Services;
Building a valid iCalendar feed in PHP in Laravel
ical
serialization
formatting
Intermediate
8 steps
php
<?php namespace App\View\Components;
Building a breadcrumb component in Laravel
blade components
url parsing
string manipulation
Intermediate
8 steps
php
<?php namespace App\Experiments;
Weighted random selection in PHP
weighted-random
cumulative-sum
sampling
Intermediate
8 steps
php
final class FieldEncryptor { private string $key;
Authenticated field encryption with libsodium
encryption
libsodium
authenticated-encryption
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/tracking-task-progress-in-a-json-file-explained-php-768a/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.