メニューを閉じる

テクノデジタルグループ

メニューを開く

2016.03.26

プログラミング

PHPでJavaBeansなオブジェクトをAjaxで返す

こんにちは、shuです。

今回はPHPのお話です。
タイトルに突っ込みどころはありますが、何が言いたいかというと、

PHPでカプセル化されたクラスのオブジェクトをJSONシリアライズし、プロパティを取り出したい。

ということです。
それがどのような場面かと考えたときに、JavaBeansみたいなカプセル化されたクラスをAjaxで返すときかな~と思い、このタイトルに至りました。

普通に json_encode() すればいいんじゃないの?と考えられるかもしれませんが、とりあえず見ていきましょう。

<?php

class Clock
{
	private $displayType;

	public function setDisplayType($displayType)
	{
		$this->displayType = $displayType;
	}

	public function getDisplayType()
	{
		return $this->displayType;
	}
}

$clock = new Clock();
$clock->setDisplayType('analog');

$json = json_encode($clock);
echo $json;

この例は、時計というモノを抽象化したモデルを生成しJSONシリアライズして結果を出力するという簡単なコードです。
時計モデルには表示タイプが属性として備わっています。
ここではアナログ時計を作りたいので、表示タイプには ‘analog’ をセットしています。
では早速サーバに配置して結果を見てみましょう。

{}

結果は空のJSONオブジェクトです。これはなぜでしょう?
PHPではjson_encode()した際にpublicプロパティしかJSONシリアライズされません。
publicなアクセサメソッドが用意されていてもそれらは無視されるんですね。
protectedでも同様です。

ではプロパティをpublicに変更するしかないのでしょうか?
次のサンプルコードを見ていきましょう。

<?php

class Clock implements \JsonSerializable
{
	private $displayType;

	public function setDisplayType($displayType)
	{
		$this->displayType = $displayType;
	}

	public function getDisplayType()
	{
		return $this->displayType;
	}

	public function jsonSerialize()
	{
		return (object) get_object_vars($this);
	}
}

$clock = new Clock();
$clock->setDisplayType('analog');

$json = json_encode($clock);
echo $json;

なにやら JsonSerializable というインターフェイスが実装されていますね。
早速結果を見ていきましょう。

{"displayType":"analog"}

最初のサンプルとは違い displayType が取得できました。
何が違うのかというと言わずもがなJsonSerializableですね。
この JsonSerializable というインターフェイスはPHP5.4で追加されたものです。

JsonSerializableを実装したオブジェクトに json_encode() をかけると、jsonSerialize()が呼び出され、このメソッドの戻り値がJSONシリアライズの対象になります。

極端な話、以下のように無理やり別の値に書き換えることができるので、実装には注意が必要です。

<?php

class Clock implements \JsonSerializable
{
	private $displayType;

	public function setDisplayType($displayType)
	{
		$this->displayType = $displayType;
	}

	public function getDisplayType()
	{
		return $this->displayType;
	}

	public function jsonSerialize()
	{
		return 'hoge';
	}
}

$clock = new Clock();
$clock->setDisplayType('analog');

$json = json_encode($clock);
echo $json;
"hoge"

サンプルコードclock2.phpの場合はget_object_vars()を使用し、クラス内のプロパティを返しているので、displayTypeがJSONシリアライズの結果として出力されました。

これで完璧!と思いきや、以下のサンプルを見てみるとなにやら動きが怪しいです。

<?php

class Clock implements \JsonSerializable
{
	private $displayType;

	public function setDisplayType($displayType)
	{
		$this->displayType = $displayType;
	}

	public function getDisplayType()
	{
		return $this->displayType;
	}

	public function jsonSerialize()
	{
		return (object) get_object_vars($this);
	}
}

class Wristwatch extends Clock implements \JsonSerializable
{
	private $bandMaterial;

	public function setBandMaterial($bandMaterial)
	{
		$this->bandMaterial = $bandMaterial;
	}

	public function getBandMaterial()
	{
		return $this->bandMaterial;
	}

	public function jsonSerialize()
	{
		return (object) get_object_vars($this);
	}
}

$wristwatch = new Wristwatch();
$wristwatch->setDisplayType('analog');
$wristwatch->setBandMaterial('leather');

$json = json_encode($wristwatch);
echo $json;

今度は時計モデルを継承し腕時計モデルを作っています。
腕時計モデルには、時計モデルには無いバンド素材を属性として持っています。
上記のサンプルコードではアナログ表示でレザーバンド仕様の腕時計を作っています。
では結果を見てみましょう。

{"bandMaterial":"leather"}

んん?結果に bandMaterial しか出力されません。親クラスの displayType はどこへ行ったのでしょうか?

この現象の原因はget_object_vars()の挙動にあります。
よくマニュアルを読んでみると以下のような記述があります。

指定した object について、 そのスコープ内でアクセス可能な非 static プロパティを取得します。

つまり get_object_vars() に Wristwatch 自身である $this を渡していますが、displayType は親クラスのprivateプロパティなので Wristwatch からはアクセスできずにこのような結果となったわけです。

ではどうするか?次のサンプルコードを見てみましょう。

<?php

class Clock implements \JsonSerializable
{
	private $displayType;

	public function setDisplayType($displayType)
	{
		$this->displayType = $displayType;
	}

	public function getDisplayType()
	{
		return $this->displayType;
	}

	public function jsonSerialize()
	{
		return (object) get_object_vars($this);
	}
}

class Wristwatch extends Clock implements \JsonSerializable
{
	private $bandMaterial;

	public function setBandMaterial($bandMaterial)
	{
		$this->bandMaterial = $bandMaterial;
	}

	public function getBandMaterial()
	{
		return $this->bandMaterial;
	}

	public function jsonSerialize()
	{
		$properties = parent::jsonSerialize();
		return (object) array_merge(json_decode(json_encode($properties), true), get_object_vars($this));
	}
}

$wristwatch = new Wristwatch();
$wristwatch->setDisplayType('analog');
$wristwatch->setBandMaterial('leather');

$json = json_encode($wristwatch);
echo $json;

jsonSerialize() の実装が少し変わりました。
一度親クラスの jsonSerialize() をコールして親のプロパティと自分自身のプロパティをマージしたものを返しています。

では結果はどのようになるかというと、

{"displayType":"analog","bandMaterial":"leather"}

無事、displayType と bandMaterial が出力されました。

このようにして、カプセル化したオブジェクトをAjaxでJSONとして返したいときは JsonSerializable をうまく使ってみてください。

ただし、JsonSerializable はPHP5.4以降なので、5.3以前では使用できませんのでご注意を。
#その場合はどうするかというと、json_encode()する前に、事前に対象オブジェクトのプロパティの一覧を取ってあげて、それらをjson_encode()に渡してあげてとかとか・・・


【記事への感想募集中!】

記事への感想・ご意見がありましたら、ぜひフォームからご投稿ください!
  • こんな記事が読んでみたい、こんなことが知りたい、調べてほしい!という意見も募集中!
  • いただいた感想は今後の記事に活かしたいと思います!

感想フォームはこちら


【テクノデジタルではエンジニア/デザイナーを積極採用中です!】

下記項目に1つでも当てはまる方は是非、詳細ページへ!
  • 自分でアプリを作ってみたい
  • ITで世の中にワクワクを生み出したい
  • 使いやすさ、デザインにこだわったWebサイトを開発したい

採用情報の詳細はこちら


Qangaroo(カンガルー)

  • 徹底した見やすさと優れた操作性で、テストの「見える化」を実現。
  • テストの進捗が見える。開発がスマートに進む。
  • クラウド型テスト管理ツール『Qangaroo(カンガルー)』

【テクノデジタルのインフラサービス】

当社では、多数のサービスの開発実績を活かし、
アプリケーションのパフォーマンスを最大限に引き出すインフラ設計・構築を行います。
AWSなどへのクラウド移行、既存インフラの監視・運用保守も承りますので、ぜひご相談ください。
詳細は下記ページをご覧ください。

https://www.tcdigital.jp/infrastructure/

最近の記事