PHP8 の Serialize memo
PHP8 から Serializable Interface + magic method になる
タイトル通り。忘れてたよ。
で、Serializable Interface の方は serialized(): ?string
だけじゃなく __serialize(): array
も実装することになる。 serialized の方はシリアライズ化した文字列を返すが __serialize は配列化して、PHP側が serialize する感じっぽい。
これの話: https://php.watch/versions/8.1/serializable-deprecated
Test
PHP8.3 で実行。
serialize.php はこんな感じ。
<?php
class OldType implements \Serializable {
public function serialize(): ?string { return serialize('I am old man' ); }
public function unserialize(string $data): void { print_r($data); }
}
$ss = serialize(new OldType());
print "=========================\n";
print "Serialize String: {$ss}\n\n";
print "=========================\n";
unserialize($ss);
実行結果。
❯ php serialize.php
PHP Deprecated: OldType implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in /Users/yasui/tmp/serialize.php on line 3
Deprecated: OldType implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in /Users/yasui/tmp/serialize.php on line 3
=========================
Serialize String: C:7:"OldType":20:{s:12:"I am old man";}
=========================
s:12:"I am old man";%
magic method の方
serialize_magic.php
<?php
class NewType {
public function __serialize(): array { return ["hello" => 'world' ]; }
public function __unserialize(array $data): void { print_r($data); }
}
$ss = serialize(new NewType());
print "=========================\n";
print "Serialize String: {$ss}\n\n";
print "=========================\n";
unserialize($ss);
結果
❯ php serialize_magic.php
=========================
Serialize String: O:7:"NewType":1:{s:5:"hello";s:5:"world";}
=========================
Array
(
[hello] => world
)
混ぜ返してみる
C:7:"OldType":20:{s:12:"I am old man";}
を serialize_magic に通して見てみる。
serialize_mix.php
<?php
class NewType {
public function __serialize(): array { return ["hello" => 'world' ]; }
public function __unserialize(array $data): void { print_r($data); }
}
$ss = serialize(new NewType());
print "=========================\n";
print "Serialize String: {$ss}\n\n";
print "=========================\n";
unserialize($ss);
実行
❯ php serialize_mix.php
=========================
PHP Warning: Class OldType has no unserializer in /Users/yasui/tmp/serialize_mix.php on line 10
Warning: Class OldType has no unserializer in /Users/yasui/tmp/serialize_mix.php on line 10
メソッド呼び出しの優先順位
- see: https://www.php.net/manual/ja/language.oop5.magic.php#object.serialize
- see: https://www.php.net/manual/ja/class.serializable.php
シリアル化するときのメソッド呼び出しの順序
__serialize() と __sleep() が両方同じオブジェクトに定義されていた場合、 __serialize() だけが呼び出されます。 __sleep() は無視されます。 オブジェクトが Serializable インターフェイスを実装していた場合、 インターフェイスの serialize() メソッドは無視され、 __serialize() が代わりに使われます。
- 【__serializeがない時】_sleep()
- 【Serializableインターフェース実装時】__serialize()
3. 【Serializable未実装】serialize()
__serialize() の目的は、 任意のオブジェクトの表現をシリアライズしやすいように定義することです。 配列の要素はオブジェクトのプロパティに対応していても構いませんが、必須ではありません。
逆に、unserialize() は 特殊な名前 __unserialize() を持つかを調べます。 もしあれば、__serialize() が返した配列を復元し、この関数に渡します。 この関数では、その配列から必要に応じてオブジェクトのプロパティを復元して構いません。
上記の仕様だと __unserialize
と unserialize
を実装した時は、__unserialize
があれば unserialize($object->__unserialize($original_data))
のような挙動になるはず。
まず二つの実装
serialize_all.php
<?php
class OldType implements \Serializable {
public function __serialize(): array { return ["hello" => 'world' ]; }
public function __unserialize(array $data): void { print "[Call]" . __METHOD__."\n"; print_r($data); }
public function serialize(): ?string { return serialize('I am old man' ); }
public function unserialize(string $data): void { print "[Call]" . __METHOD__."\n"; print_r($data); }
}
$ss = 'C:7:"OldType":20:{s:12:"I am old man";}';
print "=========================\n";
unserialize($ss);
実行結果
❯ php serialize_all.php
=========================
[Call]OldType::unserialize
s:12:"I am old man";%
__unserialize のみ実装
serialize_one.php
<?php
class OldType {
public function __serialize(): array { return ["hello" => 'world' ]; }
public function __unserialize(array $data): void { print "[Call]" . __METHOD__."\n"; print_r($data); }
}
$ss = 'C:7:"OldType":20:{s:12:"I am old man";}';
print "=========================\n";
unserialize($ss);
実行結果
❯ php serialize_one.php
=========================
PHP Warning: Class OldType has no unserializer in /Users/yasui/tmp/serialize_one.php on line 10
Warning: Class OldType has no unserializer in /Users/yasui/tmp/serialize_one.php on line 10
よくわかってない混乱ポイント
-
public __unserialize(array $data): void
だからunserialize
を実装してないときは、内部的に__unserialize
を呼び出して、unserialize
でオブジェクトを復元する感じ…? - PHP8.1 で使う時で、アップグレードしてきた物は
Serializable
を実装しているときは、public __serialize(): array
とpublic __unserialize(array $data): void
を実装しとけりゃとりあえず良い?
ここらへんの phpt ファイル
Serializable/__serialize の時は __serialize に行くけど、unserialize の時は、 O:~ は __unserialize, C:~ は Serializable::unserialize に行く感じっぽい。