MYMONDAY.ai
서비스스튜디오도구데이터·포맷73CSS 생성기19색상·이미지10인코딩·보안10웹·SEO11개발·데브옵스10파일·내보내기18계산기6가이드

JSON을 PHP 클래스로 변환

샘플 JSON을 타입이 지정된 생성자 프로퍼티, PHPDoc list 타입, 배열 매핑 메서드를 가진 PHP 클래스로 바꿔 줍니다.

  • 브라우저에서 처리
  • 가입 없음
  • 무료

도구 작업 공간

예제

옵션

JSON 입력

27줄435byte

결과

⌘/Ctrl + Enter로 복사
결과 · Payload.php
<?php

declare(strict_types=1);

final class Payload
{
    public function __construct(
        public int $id,
        public string $status,
        public PayloadCustomer $customer,
        /** @var list<PayloadItem> */
        public array $items,
        public float $total,
        public string $shippedAt,
        public mixed $tracking = null,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            id: $data['id'],
            status: $data['status'],
            customer: PayloadCustomer::fromArray($data['customer']),
            items: array_map(static fn ($v) => PayloadItem::fromArray($v), $data['items']),
            total: $data['total'],
            shippedAt: $data['shipped_at'],
            tracking: $data['tracking'] ?? null,
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'status' => $this->status,
            'customer' => $this->customer->toArray(),
            'items' => array_map(static fn ($v) => $v->toArray(), $this->items),
            'total' => $this->total,
            'shipped_at' => $this->shippedAt,
            'tracking' => $this->tracking,
        ];
    }
}

final class PayloadCustomer
{
    public function __construct(
        public int $id,
        public string $name,
        public string $email,
        public bool $vip,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            id: $data['id'],
            name: $data['name'],
            email: $data['email'],
            vip: $data['vip'],
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'vip' => $this->vip,
        ];
    }
}

final class PayloadItem
{
    public function __construct(
        public string $sku,
        public string $name,
        public int $qty,
        public float $price,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            sku: $data['sku'],
            name: $data['name'],
            qty: $data['qty'],
            price: $data['price'],
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'sku' => $this->sku,
            'name' => $this->name,
            'qty' => $this->qty,
            'price' => $this->price,
        ];
    }
}
타입3
크기2.7kB

JSON to PHP 변환기는 샘플 JSON을 읽어 declare(strict_types=1) 아래에 final class 선언을 만듭니다. 각 클래스는 생성자 프로퍼티 승격을 사용하므로 PHP 8.0 이상이 필요하고, 정적 메서드 fromArray(array $data)와 인스턴스 메서드 toArray()를 갖습니다.

두 메서드는 json_decode($json, true)와 json_encode($obj->toArray()) 위에 얹어 쓰도록 만들어졌습니다. 같은 배열 안의 객체들은 클래스 하나로 합쳐지고, 일부 원소에 없거나 일부에서 null인 키는 기본값 null인 널 허용 프로퍼티가 되며, 배열 프로퍼티에는 PHPDoc list<T> 타입이 붙습니다. 루트는 객체와 배열 모두 가능하고 "루트 타입 이름"(기본값 Payload)과 "네임스페이스" 옵션이 있습니다.

입력은 브라우저 탭 안에 머물고 업로드되지 않습니다. 한도는 20 MiB입니다. 타입은 샘플에서 나오므로 샘플 하나로는 키가 항상 있다는 것을 증명할 수 없고, 모든 샘플에서 null인 키는 타입 정보가 없어 mixed가 됩니다.

사용 방법

  1. JSON 붙여넣기

    샘플 데이터를 붙여넣거나 내장 샘플을 불러옵니다. 최상위 객체와 배열은 받지만 문자열, 숫자, 불리언 단일 값은 거부됩니다.

  2. 루트 이름과 네임스페이스 지정

    Payload가 마음에 들지 않으면 "루트 타입 이름"을 바꿉니다. PascalCase로 변환됩니다. "네임스페이스"에 App\Dto처럼 적으면 namespace 줄이 붙고, 비워 두면 붙지 않습니다.

  3. 결과 바로 확인

    변환 버튼은 없습니다. 입력하거나 옵션을 바꾸면 클래스가 바로 다시 만들어지며, 잘못된 JSON이나 빈 입력이면 대신 오류를 보여 줍니다.

  4. 클래스 확인

    루트 클래스가 먼저 나오고 중첩 객체 클래스가 뒤따릅니다. 배열 루트라면 출력이 // The root JSON value is list<PayloadItem> 같은 주석으로 시작합니다.

  5. 복사 또는 다운로드

    코드를 복사하거나 .php 파일로 저장하고, 오토로더가 요구하면 클래스별로 파일을 나눕니다.

작동 원리

JSON과 PHP 타입 대응

프로퍼티 타입은 그 위치에서 본 모든 값으로 정합니다. 합쳐진 모든 객체에 있고 null이 아닌 키는 필수 생성자 매개변수입니다. 일부 객체에 없거나 일부에서 null인 키는 = null 기본값이 있는 ?T가 되고, PHP 규칙대로 필수 매개변수가 항상 기본값 있는 매개변수보다 앞에 놓입니다.

스칼라는 string, int, float, bool이며 정수와 소수가 함께 있으면 float로 합쳐집니다. 10.0처럼 값은 정수인 소수 표기도 float로 보존되어 int가 아닌 float를 생성합니다. 모든 샘플에서 null인 키와 서로 다른 모양이 섞인 위치는 mixed입니다. 배열은 시그니처에서 array이고 매개변수 위의 PHPDoc @var list<T>가 원소 타입을 기록합니다.

JSON 값PHP 타입fromArray 표현식
stringstring$data['key']
정수int$data['key']
소수, 또는 정수와 소수가 섞인 값float$data['key']
true / falsebool$data['key']
타입과 null이 함께 있는 값, 일부 객체에 없는 키?T = null$data['key'] ?? null
모든 샘플에서 null, 또는 섞인 모양mixed$data['key'] ?? null (선택일 때)
객체생성된 클래스Class::fromArray($data['key'])
객체 배열array, @var list<Class>array_map(static fn ($v) => Class::fromArray($v), ...)
스칼라 배열array, @var list<T>$data['key']

이름 규칙과 예약어

클래스 이름은 키 경로의 PascalCase로 PayloadUser처럼 만듭니다. 배열 원소 클래스는 키의 단수형을 따라 items는 PayloadItem, categories는 PayloadCategory, data는 PayloadDataItem이 되고, 이름이 겹치면 2부터 숫자가 붙습니다. 프로퍼티 이름은 ASCII 영문자와 숫자로 만든 camelCase라서 user_id는 userId가 되고, 숫자로 시작하는 키에는 field 접두사가 붙어 2fa는 field2fa가 되며, 유일한 예약 이름 this는 thisValue가 됩니다.

원래 키는 fromArray와 toArray 안에 작은따옴표로 남으므로 JSON으로 되돌릴 때 실제 이름을 씁니다. 클래스 이름이 string, int, array, mixed, self 같은 PHP 타입 키워드와 (대소문자 구분 없이) 같으면 Data 접미사가 붙어, 루트 타입 이름을 string으로 하면 StringData가 됩니다. class라는 프로퍼티 이름은 PHP에서 허용되므로 $class로 그대로 둡니다.

'user_id' => $this->userId,
'2fa' => $this->field2fa,
'this' => $this->thisValue,

strict_types가 생성 코드에 미치는 영향

생성된 fromArray는 $data['key']를 캐스트 없이 생성자에 그대로 넘깁니다. declare(strict_types=1)에서는 int 프로퍼티에 문자열 "42"처럼 스칼라 타입이 다른 값이 오면 변환되지 않고 TypeError가 납니다. strict 모드에서도 허용되는 확장은 int에서 float로의 변환뿐입니다.

필수 키가 없으면 "Undefined array key" 경고가 나고, null이 널 비허용 매개변수에 들어갈 수 없으므로 이어서 TypeError가 납니다. DTO라면 대체로 원하는 동작이며, 샘플 객체 일부에서 빠진 키가 널 허용으로 생성되는 이유이기도 합니다.

실제 예시

중첩 객체와 리스트
입력
{"id":42,"user":{"name":"Ada","active":true},"roles":["admin","editor"]}
출력
<?php

declare(strict_types=1);

final class Payload
{
    public function __construct(
        public int $id,
        public PayloadUser $user,
        /** @var list<string> */
        public array $roles,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            id: $data['id'],
            user: PayloadUser::fromArray($data['user']),
            roles: $data['roles'],
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'user' => $this->user->toArray(),
            'roles' => $this->roles,
        ];
    }
}

final class PayloadUser
{
    public function __construct(
        public string $name,
        public bool $active,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            name: $data['name'],
            active: $data['active'],
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'name' => $this->name,
            'active' => $this->active,
        ];
    }
}

생성자 프로퍼티 승격을 쓴 final 클래스 두 개가 루트부터 나옵니다. 중첩 객체는 해당 클래스의 fromArray로 다시 만들고 문자열 리스트에는 list<string> docblock이 붙습니다.

예약어, 숫자로 시작하는 키, null, 합쳐진 항목 리스트
입력
{"user_id":7,"class":"admin","2fa":true,"price":19.5,"note":null,"items":[{"sku":"A1"},{"sku":"B2","qty":3}]}
출력
<?php

declare(strict_types=1);

final class Payload
{
    public function __construct(
        public int $userId,
        public string $class,
        public bool $field2fa,
        public float $price,
        /** @var list<PayloadItem> */
        public array $items,
        public mixed $note = null,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            userId: $data['user_id'],
            class: $data['class'],
            field2fa: $data['2fa'],
            price: $data['price'],
            note: $data['note'] ?? null,
            items: array_map(static fn ($v) => PayloadItem::fromArray($v), $data['items']),
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'user_id' => $this->userId,
            'class' => $this->class,
            '2fa' => $this->field2fa,
            'price' => $this->price,
            'note' => $this->note,
            'items' => array_map(static fn ($v) => $v->toArray(), $this->items),
        ];
    }
}

final class PayloadItem
{
    public function __construct(
        public string $sku,
        public ?int $qty = null,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            sku: $data['sku'],
            qty: $data['qty'] ?? null,
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'sku' => $this->sku,
            'qty' => $this->qty,
        ];
    }
}

2fa는 field2fa가 되고 null만 있는 키는 mixed입니다. 두 항목은 PayloadItem으로 합쳐지며 qty는 한쪽에만 있으므로 ?int = null입니다.

스칼라 배열
입력
{"tags":["a","b"],"scores":[1,2.5],"empty":[]}
출력
<?php

declare(strict_types=1);

final class Payload
{
    public function __construct(
        /** @var list<string> */
        public array $tags,
        /** @var list<float> */
        public array $scores,
        /** @var list<mixed> */
        public array $empty,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            tags: $data['tags'],
            scores: $data['scores'],
            empty: $data['empty'],
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'tags' => $this->tags,
            'scores' => $this->scores,
            'empty' => $this->empty,
        ];
    }
}

원소 타입은 PHPDoc에 적힙니다. tags는 list<string>, 1과 2.5는 list<float>로 합쳐지고, 빈 배열은 배울 원소가 없어 list<mixed>입니다.

네임스페이스를 지정한 최상위 배열
입력
[{"id":1,"name":"Ada"},{"id":2,"email":"grace@example.com"}]
출력
<?php

declare(strict_types=1);

namespace App\Dto;

// The root JSON value is list<PayloadItem>

final class PayloadItem
{
    public function __construct(
        public int $id,
        public ?string $name = null,
        public ?string $email = null,
    ) {}

    /** @param array<string, mixed> $data */
    public static function fromArray(array $data): self
    {
        return new self(
            id: $data['id'],
            name: $data['name'] ?? null,
            email: $data['email'] ?? null,
        );
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
        ];
    }
}

네임스페이스 옵션을 App\Dto로 하면 declare(strict_types=1) 아래에 namespace 줄이 붙습니다. 배열 루트는 주석이 되고 PayloadItem은 id가 필수, name과 email은 기본값 있는 널 허용입니다.

자주 하는 실수

  • 필수 키가 없으면 예외가 납니다

    $data['id']에는 ?? 기본값이 없어서 id가 없는 페이로드는 경고와 TypeError를 냅니다. 의도된 동작입니다. 실제 데이터에서 키가 빠질 수 있다면 그 키가 없는 레코드를 샘플에 넣으세요. 그러면 프로퍼티가 널 허용이 됩니다.

  • 샘플 하나로는 키가 항상 있다고 증명할 수 없습니다

    붙여넣은 모든 원소에 있는 키는 필수로 생성됩니다. 생성기는 붙여넣은 것만 보므로, API가 키를 생략하는 경우에는 선택 필드가 빠진 레코드를 포함해 여러 건을 넣으세요.

  • null만 있는 키는 mixed가 됩니다

    note가 모든 샘플에서 null이면 타입을 알 방법이 없어 기본값이 null인 mixed 프로퍼티가 됩니다. 값이 들어 있는 레코드를 붙여넣거나 실제 타입을 직접 적으세요.

  • PHPDoc list 타입은 실행 중에 검사되지 않습니다

    @var list<float>는 PHP가 아니라 PHPStan 같은 정적 분석 도구가 읽습니다. 1과 2.5가 float로 합쳐져 list<float>로 타입이 붙은 배열도 json_decode는 정수 1을 그대로 돌려줍니다.

  • 클래스가 한 파일에 모두 들어갑니다

    출력은 <?php 블록 하나입니다. PSR-4 오토로딩은 파일당 클래스 하나를 기대하므로 나눠야 합니다.

  • 2^53을 넘는 정수는 이미 반올림되어 있습니다

    타입 추론은 JSON 숫자를 JavaScript number 정밀도로 저장하므로 9007199254740993은 코드 생성 전에 자릿수를 잃을 수 있습니다. PHP int는 64비트지만, 이런 ID는 string을 쓰고 원본 데이터를 확인하세요.

자주 묻는 질문

어느 PHP 버전이 필요한가요?

생성자 프로퍼티 승격과 mixed 타입 때문에 PHP 8.0 이상이 필요합니다.

readonly 프로퍼티를 쓰나요?

아니요. 프로퍼티는 public이고 변경할 수 있습니다. PHP 8.1 이상이면 readonly를 직접 붙이세요. = null 기본값은 선택 키가 항상 보장될 때만 지우세요.

JSON 문자열을 클래스에 어떻게 넣나요?

json_decode($json, true)로 배열을 만들어 Payload::fromArray()에 넘기고, 되돌릴 때는 toArray() 뒤에 json_encode를 호출합니다.

네임스페이스는 어떻게 넣나요?

"네임스페이스" 옵션에 App\Dto처럼 적으면 declare(strict_types=1); 아래에 namespace App\Dto;가 들어갑니다.

선택 키는 어떻게 처리되나요?

배열의 일부 객체에서 빠진 키는 기본값 null인 널 허용 프로퍼티가 되어 필수 매개변수 뒤에 놓입니다. toArray()는 값이 없으면 null로 씁니다.

중첩 리스트는 어떻게 되나요?

단계마다 array_map으로 매핑되고, docblock에 list<list<클래스명>>처럼 중첩이 표시됩니다. 스칼라 리스트는 그냥 배열로 남습니다.

최상위가 JSON 배열이어도 되나요?

네. 원소 클래스가 생성되고, PHP에는 타입 별칭이 없어서 // The root JSON value is list<PayloadItem> 같은 주석이 루트를 대신합니다.

JSON이 업로드되나요?

아니요. 모든 처리는 브라우저 안에서 실행됩니다.

마지막 업데이트: