When a class implements JsonSerializable (Countable and Friends), json_encode() encodes what jsonSerialize() returns, which becomes the API's contract:
<?php
final readonly class Product implements JsonSerializable {
public function __construct(public int $id, public string $sku, public string $title,
private int $cents, private int $stock) {}
public function jsonSerialize(): array { // the public contract of the API
return ['id' => $this->id, 'sku' => $this->sku, 'title' => $this->title,
'price' => sprintf('%d.%02d', intdiv($this->cents, 100), $this->cents % 100),
'in_stock' => $this->stock > 0];
}
}json_validate() (PHP 8.3) parses without building values. Here, a 5.3 MB document validated in 48 ms with no extra memory, while json_decode() took 87-179 ms and 105 MB. Use it when a yes or no is enough, as 4.18.9 does. If you need the data, decode once.