JSON to PHP: typed classes for decoded JSON
json_decode gives you stdClass objects or arrays: no types, no autocompletion, and no guarantee that a field exists. This JSON to PHP generator writes a class per object shape with typed properties, so the rest of your code can depend on a known structure. The classes use PHP 7.4+ typed properties and are self-validating: every value is checked when you build an object from decoded JSON.
Example
{
"id": 42,
"title": "Write docs",
"done": false,
"priority": 2.5,
"tags": ["docs"],
"owner": { "name": "Ada", "email": null },
"due": "2026-10-01T09:00:00Z",
"subtasks": [
{ "title": "Outline", "estimate": 2 },
{ "title": "Draft" }
]
}
An excerpt of the generated Subtask class:
class Subtask {
private string $title; // json:title Required
private ?int $estimate; // json:estimate Optional
public function __construct(string $title, ?int $estimate) {
$this->title = $title;
$this->estimate = $estimate;
}
public static function from(stdClass $obj): Subtask { /* ... */ }
public function to(): stdClass { /* ... */ }
public function validate(): bool { /* ... */ }
public function getTitle(): string { /* ... */ }
public function getEstimate(): ?int { /* ... */ }
}
Each property records its JSON key and whether it is required in a comment. Optional values use nullable types such as ?int. Nested objects are typed with their generated class names, and lists are plain array properties whose elements are checked one by one during conversion.
What the generated methods do
from(stdClass $obj)builds an instance fromjson_decode($json)output, recursively creating nested objects and lists.to()converts the object back to astdClassready forjson_encode.validate()and per-property validators throw anExceptionwith a message likeAttribute Error:Task::titlewhen a value has the wrong type.- Getters for every property, and
sample()methods that create example instances for tests.
$task = Task::from(json_decode($json)); // stdClass, not associative array
echo $task->getTitle();
echo json_encode($task->to());
Note that from() expects objects, so call json_decode without the true flag.
Generated code size
Because every property has its own from, to, validate, getter and sample method, the output is long: the small example above produces several hundred lines. That is the price of explicit runtime validation. If you only want a lightweight DTO, keep the property declarations and constructor and delete the rest, or turn them into a modern PHP 8 class with constructor promotion:
final class Subtask {
public function __construct(
public readonly string $title,
public readonly ?int $estimate = null,
) {}
}
Handling validation errors
try {
$task = Task::from(json_decode($body));
} catch (Exception $e) {
error_log('Invalid task payload: ' . $e->getMessage());
http_response_code(422);
}
Catching the exception at the point where external data enters your application keeps bad payloads from spreading. For stricter decoding of the raw string, pass JSON_THROW_ON_ERROR to json_decode so malformed JSON throws a JsonException instead of returning null. In frameworks such as Laravel or Symfony, run this conversion inside a request DTO or form-request class so controllers only ever receive valid, typed objects.
Type mapping
| JSON | PHP |
|---|---|
| integer / decimal | int / float |
| boolean / string | bool / string |
| ISO date-time | DateTime |
| array | array (with element checks) |
| object | a class |
| optional | nullable type ?T |
Property names are converted to camelCase (user_name becomes $userName); the conversion methods keep using the original keys. Format the result with the PHP Formatter. If you just need a PHP array literal of the data, use JSON to PHP Array. Generation runs in your browser.