はじめに
サイト共通の重い計算処理を各ページでやるコストが高いので、処理結果をobj化してDBに保存して使いまわしたい
・・・と考えた。
最初にやってみた方法
保存用テーブル
CREATE TABLE IF NOT EXISTS `xxxx_save_t` (
`xxxx_save_id` int(11) NOT NULL AUTO_INCREMENT,
`data` longblob NOT NULL,
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
`delete_time` datetime DEFAULT NULL,
`delete_flg` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`xxxx_save_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
保存処理
$obj = xxxx //何某かの処理が終わった状態結果を持つインスタンス
db::save(json_encode($obj));
読み込み処理
$data = db::SelectById($id);
$obj = json_decode($data['data']);
なぜか一部のデータが消える!
- $objが持つはずのプロパティが消える・・・
原因
json_encodeでオブジェクトを保存する場合、対象はpublicプロパティのみ。
インスタンスのprivate/protect変数も状態として保存したい場合には向かない。
あとは配列とオブジェクトが混ざってるような場合もserializeの方が良さそうか。
【PHP】オブジェクトをjsonエンコード
再現
class CustomDateTime {
public $_dateTime = null;
protected $_dateStr = "";
protected $_isSet = false;
public function __construct($date) {
$this->_dateTime = new DateTime($date);
$this->_dateStr = $date;
$this->_isSet = true;
}
}
public function test() {
$tmpData = [
'array' => [1,2,3],
'customDateTime' => new CustomDateTime('2020-1-1'),
];
var_dump($tmpData);
var_dump("<br/><br/>");
$tmpData2 = json_decode(json_encode($tmpData));
var_dump($tmpData2);exit;
}
結果
array(2) { ["array"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } ["customDateTime"]=> object(CustomDateTime)#43 (3) { ["_dateTime"]=> object(DateTime)#44 (3) { ["date"]=> string(19) "2020-01-01 00:00:00" ["timezone_type"]=> int(3) ["timezone"]=> string(10) "Asia/Tokyo" } ["_dateStr":protected]=> string(8) "2020-1-1" ["_isSet":protected]=> bool(true) } } string(10) "
object(stdClass)#45 (2) { ["array"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } ["customDateTime"]=> object(stdClass)#46 (1) { ["_dateTime"]=> object(stdClass)#47 (3) { ["date"]=> string(19) "2020-01-01 00:00:00" ["timezone_type"]=> int(3) ["timezone"]=> string(10) "Asia/Tokyo" } } }
jsonを介した時点で非publicなdateStr/isSetが抹消された
対策
配列やオブジェクトのpublic変数だけでよければjson_encodeでよいがprotectの状態なども持たせたい場合はserializeを使う
Serialize PHP:Manual
Serializeを使ってみる
public function test() {
$tmpData = [
'array' => [1,2,3],
'customDateTime' => new CustomDateTime('2020-1-1'),
];
var_dump($tmpData);
var_dump("<br/><br/>");
$tmpData2 = unserialize(serialize($tmpData));
var_dump($tmpData2);exit;
}
結果
array(2) { ["array"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } ["customDateTime"]=> object(CustomDateTime)#43 (3) { ["_dateTime"]=> object(DateTime)#44 (3) { ["date"]=> string(19) "2020-01-01 00:00:00" ["timezone_type"]=> int(3) ["timezone"]=> string(10) "Asia/Tokyo" } ["_dateStr":protected]=> string(8) "2020-1-1" ["_isSet":protected]=> bool(true) } }
array(2) { ["array"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } ["customDateTime"]=> object(CustomDateTime)#45 (3) { ["_dateTime"]=> object(DateTime)#46 (3) { ["date"]=> string(19) "2020-01-01 00:00:00" ["timezone_type"]=> int(3) ["timezone"]=> string(10) "Asia/Tokyo" } ["_dateStr":protected]=> string(8) "2020-1-1" ["_isSet":protected]=> bool(true) } }
非publicな状態も含めて変換された。
その他メモ
serializeは環境によっては文字関連でエラーになったりするそう
serialize VS json_encode 〜人類の存亡とか仁義とか全く関係ない戦い〜
保存サイズはjson_encodeの方が小さい。そりゃね。
まとめ
- 配列で使うならさして違いは無い
- オブジェクトを利用する場合スコープの影響が発生してくる
- 純粋なデータの保存用途だけあればjson_encodeを使った方がコンパクト
- 中身まで同じオブジェクトを再現したいのであればserializeがよさげ