1.

PHPでオブジェクトを配列に変換する方法|キャスト・get_object_vars・json

編集

PHPでオブジェクトを配列に変換するには、単純な変換なら(array)キャストかget_object_vars()、ネストしたオブジェクトまで丸ごと配列化したいならjson_decode(json_encode($obj), true)を使います。本稿では3つの方法それぞれの挙動と落とし穴、用途別の使い分けを、最小コードとともに解説します。

 

この記事の要点
  • (array)キャストは最速だが、private/protectedプロパティのキー名に不可視のヌルバイト(\0)が付くという落とし穴がある。
  • get_object_vars()はアクセス可能なpublicプロパティだけをきれいに配列化する。呼び出す場所(クラス内外)で取得範囲が変わる。
  • json_decode(json_encode($obj), true)はネストしたオブジェクトまで再帰的に配列化できる唯一の手軽な方法。ただしpublicプロパティのみが対象で、型情報や非UTF-8データは失われる。
  • いずれも変換は浅いか深いかprivate/protectedを含むかで結果が変わる。用途に合わせて選ぶ。
  • 逆方向(配列→オブジェクト)は(object)キャストでstdClassになる。

 

主な変換方法の一覧

PHPには標準で複数の変換手段があります。まず全体像を把握しておきましょう。

  • 方法① (array)キャスト … 関数不要・最速。すべてのプロパティを拾うが、private/protectedのキー名が特殊になる。
  • 方法② get_object_vars() … アクセス可能なプロパティだけをクリーンに取得。
  • 方法③ json_decode(json_encode($obj), true) … ネスト構造ごと配列化したいときの定番。

それぞれ詳しく見ていきます。

 

方法① (array)キャストで変換する

もっとも手軽なのは型キャストです。オブジェクトの前に(array)を付けるだけで配列になります。

<?php
class User {
    public $name = 'Taro';
    public $age  = 30;
}

$user = new User();
$array = (array) $user;

print_r($array);
// Array ( [name] => Taro [age] => 30 )

publicプロパティだけのクラスなら、これで期待どおりの連想配列が得られます。

注意:private/protectedプロパティのキー名が特殊になる

(array)キャストはprivateやprotectedのプロパティも配列に含めますが、そのキー名に不可視の制御文字(ヌルバイト \0)が付与されるという独特の仕様があります。具体的には次のようになります。

  • privateプロパティ → キーが \0クラス名\0プロパティ名 になる
  • protectedプロパティ → キーが \0*\0プロパティ名 になる(*はアスタリスク1文字)
  • publicプロパティ → キーはプロパティ名そのまま

<?php
class Account {
    public    $id   = 1;
    protected $name = 'Taro';
    private   $pin  = 1234;
}

$array = (array) new Account();

var_dump(array_keys($array));
// array(3) {
//  [0]=> string(2) "id"
//  [1]=> string(7) " * name"      // 実際は \0*\0name
//  [2]=> string(13) " Account pin" // 実際は \0Account\0pin
// }

このヌルバイトは画面上では見えませんが文字列の一部としてキーに含まれているため、$array['name'] のように普通の名前ではアクセスできません。echoでは消えて見えるのにキー比較が一致しない、という分かりにくいバグの原因になります。private/protectedを持つクラスを安全に配列化したい場合は、後述の方法②・③を検討してください。

 

方法② get_object_vars()で変換する

get_object_vars()は、対象オブジェクトのアクセス可能なプロパティを連想配列で返す関数です。キー名はプロパティ名そのままで、ヌルバイトのような特殊文字は付きません。

<?php
class User {
    public    $name = 'Taro';
    public    $age  = 30;
    private   $pin  = 1234;
}

$user = new User();
$array = get_object_vars($user);

print_r($array);
// クラスの外から呼んだ場合:
// Array ( [name] => Taro [age] => 30 )

ポイントは「アクセス可能なプロパティ」という点です。呼び出す場所によって取得できる範囲が変わります。

  • クラスの外から呼ぶと、publicプロパティのみが返る(上記の例ではpinは含まれない)。
  • クラスの内側(メソッド内)から$thisを渡して呼ぶと、privateprotectedも含めて返る。

<?php
class User {
    public  $name = 'Taro';
    private $pin  = 1234;

    public function toArray(): array {
        return get_object_vars($this); // 内側なのでprivateも取れる
    }
}

print_r((new User())->toArray());
// Array ( [name] => Taro [pin] => 1234 )

ヌルバイト問題を避けつつ、可視性を意図的にコントロールしたいときに最適な方法です。なお、この関数は1階層分のプロパティだけを配列化します。プロパティの値がさらにオブジェクトの場合、その値はオブジェクトのまま残ります(再帰しません)。

 

方法③ json_decode(json_encode())で変換する

ネストした構造ごと配列化したい場合の定番が、JSONを経由する方法です。json_encode()でいったんJSON文字列にし、json_decode()の第2引数にtrueを渡して連想配列としてデコードします。

<?php
class Address {
    public $city = 'Tokyo';
}
class User {
    public $name    = 'Taro';
    public $address = null;
}

$user = new User();
$user->address = new Address(); // ネストしたオブジェクト

$array = json_decode(json_encode($user), true);

print_r($array);
// Array (
//    [name]   => Taro
//    [address] => Array ( [city] => Tokyo ) // ネストも配列化される
// )

このとおり、入れ子になったAddressオブジェクトまで再帰的に配列へ変換されます。これが他の2方法にはない最大の利点です。一方で、JSONを経由するがゆえの注意点があります。

  • publicプロパティしか変換されない … json_encode()はデフォルトでprivate/protectedを無視するため、それらは結果に含まれない(JsonSerializableを実装すれば制御可能)。
  • 型情報が失われる … すべてJSONで表現可能な型(文字列・数値・配列など)に変換される。リソース型などは扱えない。
  • 非UTF-8データで失敗する … json_encode()はUTF-8でない文字列があるとfalseを返し、変換が壊れることがある。
  • 2回の変換コストがかかる … 大量データでは(array)キャストより遅い。

 

3つの方法の比較

用途別に整理すると次のとおりです。private/protectedを含み、かつ最も安全に扱える観点では用途に応じた選択になりますが、一般的なDTOの変換では推奨行の方法が無難です。

方法 変換の深さ 対象の可視性 キー名 主な用途
(array) キャスト 浅い(1階層) public/protected/private すべて private/protectedに\0が付く publicのみのクラスを高速変換
get_object_vars() 浅い(1階層) 呼び出し位置で変化(外=public) プロパティ名そのまま(クリーン) 可視性を制御した安全な変換
json_decode(json_encode()) 深い(再帰) publicのみ プロパティ名そのまま ネスト構造ごと配列化

 

逆方向:配列をオブジェクトに変換する

反対に、配列をオブジェクトへ変換したい場合は(object)キャストが使えます。結果はstdClassのインスタンスになり、各キーがプロパティになります。

<?php
$array = ['name' => 'Taro', 'age' => 30];

$obj = (object) $array;

echo $obj->name; // Taro
var_dump($obj instanceof stdClass); // bool(true)

ネストした配列まで再帰的にオブジェクト化したい場合は、こちらもjson_decode(json_encode($array))(第2引数を省略、またはfalse)を使うと、入れ子の配列までstdClassに変換できます。

 

落とし穴まとめ

つまずきやすいポイント
  • (array)キャストのprivate/protectedキー問題 … キー名に不可視のヌルバイトが付くため、$array['プロパティ名']でアクセスできない。print_rでは見えないので原因に気づきにくい。private/protectedがあるなら方法②・③を使う。
  • get_object_varsの取得範囲 … クラスの外から呼ぶとpublicしか取れない。privateも欲しいならクラス内のメソッドから$thisを渡す。
  • 再帰の有無 … (array)キャストとget_object_varsは1階層だけ。ネストしたオブジェクトを配列にしたいならjson経由が必要。
  • stdClassへの逆変換 … 配列を(object)キャストすると独自クラスではなくstdClassになる。元のクラスには戻らない。
  • json経由の落とし穴 … publicのみ・型情報喪失・非UTF-8で失敗の3点に注意。

 

よくある質問(FAQ)

Q1.結局どの方法を使えばいいですか?

プロパティがpublicだけのシンプルなクラスなら(array)キャストが最速で十分です。private/protectedを含み、キー名をきれいに保ちたいならget_object_vars()。オブジェクトが入れ子になっていて全体を配列にしたいならjson_decode(json_encode($obj), true)を選びます。

Q2.(array)キャストで付くヌルバイトを取り除くには?

根本的には、private/protectedを正しく扱えるget_object_vars()(クラス内から呼ぶ)や、クラスにtoArray()メソッドを実装する方法に切り替えるのが安全です。すでにヌルバイト入りの配列を持っている場合は、キー文字列の\0区切りを分解して名前を取り出すこともできますが、最初から付かない方法を選ぶほうが確実です。

Q3.多次元(ネスト)のオブジェクトをまとめて配列化できますか?

はい。json_decode(json_encode($obj), true)なら、プロパティとして保持された別のオブジェクトや配列まで再帰的に配列へ変換されます。ただしpublicプロパティのみが対象で、型情報は失われる点に注意してください。private/protectedも含めて再帰変換したい場合は、各クラスに自前のtoArray()を実装するのが確実です。

編集
Post Share
子ページ

子ページはありません

同階層のページ

同階層のページはありません

最近更新/作成されたページ