JSON to PHP

Generate PHP 8 classes with typed, constructor-promoted properties and a fromArray() hydrator from any JSON object.

PHP 8.0+ — uses constructor property promotion. No dependencies.

Paste a JSON object and get back PHP 8 classes with typed, constructor-promoted properties and a fromArray() hydrator — nested objects become their own classes, arrays of objects hydrate with array_map(), and every property is typed instead of living in an untyped associative array.

  • PHP 8 constructor property promotion — no boilerplate property declarations
  • Nested objects become their own typed classes automatically
  • Arrays of objects hydrate with array_map(), fully typed
  • @var docblocks on array properties so PHPStan/Psalm understand element types
  • 100% private — the class is generated entirely in your browser

Why typed PHP classes instead of an array?

Decoding JSON in PHP with json_decode($json, true) gives you a plain associative array — fast, but with no autocomplete, no static analysis, and a typo in a key name only surfaces at runtime. Hydrating into a typed class instead means your IDE, PHPStan, and Psalm can all catch mistakes before the code ever runs, and the shape of your data is documented in one place instead of scattered across every call site that reads $data['user']['email'].

stdClass vs. Associative Array vs. Generated Class

json_decode($j, true)json_decode($j)Generated class
Access syntax$data['name']$data->name$data->name
Typed propertiesNoNoYes
IDE autocompleteNoPartial (dynamic)Full
Static analysis catches typosNoNoYes — PHPStan/Psalm
Nested data typingNone — nested arraysNone — nested stdClassNested classes, fully typed

Using the Generated Class

php
<?php

$json = file_get_contents('user.json');
$data = json_decode($json, true);

$user = User::fromArray($data);

echo $user->name;           // Ravi Kumar
echo $user->address->city;  // Surat

// Re-encode back to JSON
echo json_encode($user);

Constructor property promotion (PHP 8.0+) means the class body is just the typed parameter list — no separate property declarations, no boilerplate assignment statements. Nested objects become their own classes with their own fromArray(), and arrays of objects are hydrated with array_map() so a list of orders becomes a list of typed Order instances, not an array of raw associative arrays.

A Note on Types

Every field's type is inferred from the sample value you paste in — a field that is null in your sample becomes mixed(since a single example can't tell the difference between "always null" and "sometimes null, sometimes a string"), and array element types are inferred from the array's first non-null item. Review the generated types against your actual API contract before shipping — the generator gets you most of the way there, not all the way.

Making It Immutable: readonly Properties (PHP 8.1+)

A hydrated API response object generally shouldn't be mutated after construction — add PHP 8.1's readonly modifier to enforce that at the language level instead of just by convention:

php
final class User
{
    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly int $age,
    ) {}

    public static function fromArray(array $data): self
    {
        return new self(
            name: $data['name'],
            email: $data['email'],
            age: $data['age'],
        );
    }
}

$user = User::fromArray($data);
$user->name = "Changed"; // Error: Cannot modify readonly property User::$name

The generator emits plain (mutable) properties by default for the broadest PHP 8.0+ compatibility — add readonlyyourself once you know you're targeting 8.1 or later and want the extra safety.

Common Use Cases

  • Typed DTOs for an API responseHydrate a third-party or internal API's JSON directly into a typed object instead of passing an untyped array through your codebase
  • Laravel API ResourcesUse a generated class as the typed input to a JsonResource's toArray(), instead of reading raw array keys with no autocomplete
  • Symfony Serializer targetsPoint Symfony's Serializer denormalizer at the generated class to get typed objects straight out of deserialization
  • Webhook payload handlingHydrate an incoming webhook's JSON body into a typed class so your handler code gets IDE autocomplete and static analysis instead of guessing array keys

Frequently Asked Questions

PHP 8.0 or later, since it relies on constructor property promotion. If you're on PHP 7, you'll need to expand the promoted parameters into separate property declarations and an explicit constructor body.

No — it's a best-effort hydrator that assigns $data['key'] ?? null for each field. If a required field is missing from the array, PHP will throw a TypeError at the point of assignment (for non-nullable typed properties) rather than failing silently, which is usually what you want during development.

Yes — the generated classes are framework-agnostic plain PHP objects (POPOs), so they work as DTOs in any framework. In Laravel you might use them as API resource transformations; in Symfony, as the target of a custom Serializer normalizer.

That happens when the sample value for that field is null — there isn't enough information in one example to know whether it's meant to always be null, or is a nullable string/int/etc. Replace mixed with the correct nullable type (e.g. ?string) once you know the real shape.

No — generation runs entirely in your browser using JavaScript. Nothing is uploaded.

No, by default properties are mutable for the widest PHP 8.0+ compatibility. Add the readonly keyword yourself (PHP 8.1+) to each promoted property if you want the generated object to be immutable after construction — see the readonly section above for the exact syntax.

Related Tools