jsson_encodeとserializeでハマった話

# はじめに

サイト共通の重い計算処理を各ページでやるコストが高いので、処理結果をobj化してDBに保存して使いまわしたい
・・・と考えた。

# 最初にやってみた方法
## 保存用テーブル
```sql
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;
```

## 保存処理
```php
 $obj = xxxx //何某かの処理が終わった状態結果を持つインスタンス
 db::save(json_encode($obj));
```

## 読み込み処理
```php
  $data = db::SelectById($id);
  $obj = json_decode($data['data']);
```

# なぜか一部のデータが消える！
* $objが持つはずのプロパティが消える・・・

# 原因

json_encodeでオブジェクトを保存する場合、**対象はpublicプロパティのみ**。
インスタンスのprivate/protect変数も状態として保存したい場合には向かない。
あとは配列とオブジェクトが混ざってるような場合もserializeの方が良さそうか。
[【PHP】オブジェクトをjsonエンコード](https://qiita.com/ma_me/items/f48bcdb8c2e7eb816af4)

# 再現
```php
	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](https://www.php.net/manual/ja/function.serialize.php)

## Serializeを使ってみる
```php
	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　〜人類の存亡とか仁義とか全く関係ない戦い〜](https://norm-nois.com/blog/archives/1887)

保存サイズはjson_encodeの方が小さい。そりゃね。

# まとめ
* 配列で使うならさして違いは無い
* オブジェクトを利用する場合スコープの影響が発生してくる
* 純粋なデータの保存用途だけあればjson_encodeを使った方がコンパクト
* 中身まで同じオブジェクトを再現したいのであればserializeがよさげ