あるページで、2つの変数 $customer_id、$messageを使っています。別のページでその変数を使いたいのですが、同じセッションではないので、セッション変数は使えません。外部ストレージを使えるとして、複数の変数をまとめて保存するにはどのような方法があるでしょうか?
キーなし配列(添字配列)
お手軽な方法です。他の言語のタプル的な使い方です。
保存する側をパック側と呼ぶことにします。
$arr = [
$customer_id,
$message,
];
$data = serialize($arr);
// $dataを外部ストレージに保存します
Code language: plaintext (plaintext)
読み出す側をアンパック側と呼ぶことにします。
// 外部ストレージから$dataを読み込んできます
$arr = unserialize($data);
list(
$customer_id,
$message
) = $arr;
Code language: plaintext (plaintext)
あるとき、$customer_name も保存することになりました。
パック側です。配列の最後に追加します。
$arr = [
$customer_id,
$message,
$customer_name,
];
Code language: plaintext (plaintext)
アンパック側です。
list(
$customer_id,
$message,
$customer_name
) = $arr;
Code language: plaintext (plaintext)
ユーザーがパック処理にアクセスしてから、新しいバージョンをデプロイしたら...
古いバージョンのデータを新しいバージョンでアンパックすることになるわね
パック側とアンパック側の処理日時には時間差があります。
(1)パック側が、$customer_id、$message をパックした。
(2)$cutomer_id、$message、$customer_nameバージョンをデプロイした。
(3)アンパックします。$customer_nameがないので、PHP Notice: undefined offset が発生し、$customer_nameはNULLとなってしまいます。
警告とNULLを嫌って、次のように書きました。
list(
$customer_id,
$message
) = $arr;
$customer_name = $arr[2] ?: '';
Code language: plaintext (plaintext)
警告がないのはいいよね
さて、数カ月たちました
それから、それから...
さて、数カ月後がたって、$address を追加することになりました。
ところが、警告とNULLを嫌って、わざわざ書いた、
$customer_name = $arr[2] ?: '';
の意味を忘れてしまいました。
「$customer_nameは配列の最後に追加しなければいけない」とカン違いしてしまいました。
そして、
$arr = [
$customer_id,
$message,
$address, // ココに挿入
$customer_name,
];
Code language: plaintext (plaintext)
list(
$customer_id,
$message,
$address // ココに挿入
) = $arr;
$customer_name = $arr[3] ?: '';;
Code language: plaintext (plaintext)
と書いてしまいました。
何が悪いの?パック、アンパックはペアになっているから、問題ない気がするんだけど
パック、アンパックの処理日時には時間差があるのよ
(1)旧バージョンでパックして、外部ストレージに保存し、
$arr = [
$customer_id, // 123
$message, // "Foo"
$customer_name, // "Taro"
];
Code language: plaintext (plaintext)
(2)新バージョンをデプロイ
(3)新バージョンでアンパックすると、
list(
$customer_id, // 123と復元
$message, // "Foo"と復元
$address // "Taro"と復元
) = $arr;
$customer_name = $arr[3] ?: ''; // '' と復元
Code language: plaintext (plaintext)
$customer_name は空文字列、$addressが "Taro"となってしまいました。
このキーなし配列バージョンの問題は、警告やNULLを気にした誰かが
list(
$customer_id,
$message
) = $arr;
$customer_name = $arr[2] ?: '';
Code language: plaintext (plaintext)
と書いてしまうかもしれないこと、
この意味を忘れて、新しい変数を追加するとき、間違った箇所に挿入してしまうかもしれないことです。
list(
$customer_id,
$message,
$address // ココに挿入
) = $arr;
$customer_name = $arr[3] ?: '';
Code language: plaintext (plaintext)
キーバリュー配列
キーなし配列バージョンは、変数の順序が重要で、そのことを知っていなければいけません。キーなし配列に格納したのがよくありませんでした。
キーバリュー配列に格納していれば、順序を気にする必要がありません。
// パック側
$arr = [
'cutomer_id' => $customer_id,
'message' => $message,
'cutomer_name' => $customer_name,
];
$data = serialize($arr);
// アンパック側
$arr = unserialize($data);
$customer_id = $arr['cutomer_id'];
$message = $arr['message'];
$cutomer_name = $arr['cutomer_name'];
Code language: plaintext (plaintext)
順序を維持するよりは、キー名のほうが間違えなさそうですが、キー名の入力間違いの可能性は残っています。
また、パック、アンパックの詳細を晒しているのを改善したいところです。
キーバリュー配列バージョンをクラスにする
クラスにすることを前提に、クラスを利用する側から書いていきます。
パックしたい変数をオブジェクトのプロパティに設定します。もしプロパティ名を間違っていれば、IDEが教えてくれます。
アンパックするときは、オブジェクトのプロパティから取得します。
// パック側
$foo = new FooPackUnpack();
$foo->customer_id = $customer_id;
$foo->message = $message;
$foo-customer_name = $customer_name;
$data = $foo->pack();
// アンパック側
$foo = FooPackUnpack::unpack($data);
$customer_id = $foo->customer_id;
$message = $foo->message;
$customer_name = $foo->customer_name;
Code language: plaintext (plaintext)
クラス側です。
パックしたい変数ごとに、プロパティを用意します。setter/getterを用意してもいいと思います。
パック、アンパックの詳細をメソッドにします。パック処理は、キーバリュー配列をつくってserializeします。
class FooPackUnpack
{
public $customer_id = 0;
public $message = '';
public $customer_name = '';
public function pack(): string
{
$arr = [
'cutomer_id' => $this->customer_id,
'message' => $this->message,
'cutomer_name' => $this->customer_name,
];
return serialize($arr);
}
public static function unpack(string $data): FooPackUnpack
{
$arr = unserialize($data);
$foo = new FooPackUnpack();
$foo->customer_id = $arr['customer_id'] ?: 0;
$foo->message = $arr['message'] ?: '';
$foo->customer_name = $arr['customer_name'] ?: '';
return $foo;
}
}
Code language: plaintext (plaintext)
キーなし配列バージョンをクラスにする
実は、キーなし配列バージョンはすでに運用中です。そこで、キーなし配列バージョンをリファクタします。
クラスを利用する側です。
キーバリュー配列バージョンのクラスを利用する側と全く同じです。
// パック側
$foo = new FooPackUnpack();
$foo->customer_id = $customer_id;
$foo->message = $message;
$foo-customer_name = $customer_name;
$data = $foo->pack();
// アンパック側
$foo = FooPackUnpack::unpack($data);
$customer_id = $foo->customer_id;
$message = $foo->message;
$customer_name = $foo->customer_name;
Code language: plaintext (plaintext)
クラス側です。
パック処理は、キーなし配列を作って、serializeします。
class FooPackUnpack
{
public $customer_id = 0;
public $message = '';
public $customer_name = '';
public function pack(): string
{
$arr = [
$this->customer_id,
$this->message,
$this->customer_name,
];
return serialize($arr);
}
public static function unpack(string $data): FooPackUnpack
{
$arr = unserialize($data);
$foo = new FooPackUnpack();
$foo->customer_id = $arr[0] ?: 0;
$foo->message = $arr[1] ?: '';
$foo->customer_name = $arr[2] ?: '';
return $foo;
}
}
Code language: plaintext (plaintext)
この書き方なら、新しい変数を追加するときは、わざわざ配列の途中に挿入しないですよね。
pack/unpackでなくて、serialize/unserializeはどう?
stringify/parseもあるわね
format/parse
encode/decodeもあるわよ