特定のプロパティを除いてserializeする
via. php.internals: Re: need inverted __sleep?
前のエントリに関連しますが。
__sleepでも同様のことができますね。
__sleepと__wakeupでもいい気がするんですけどどうなんでしょう。
http://php.mirror.camelnetwork.com/manual/ja/language.oop.magic-functions.php
説明の最後にある"変数の名前"ですが、1つ前のエントリのようなNULL文字を含む名前になります。
serialize() は、クラスに特殊な名前 __sleep の関数があるかどうかを調べます。 もしあれば、シリアル化の前にその関数を実行します。 この関数で、オブジェクトをクリアすることができます。 またこの関数は、シリアル化するオブジェクトについて、 すべての変数の名前を配列で返すことが前提となっています。
ということで、php.internals: Re: need inverted __sleep?にあるサンプルを、public/protected/privateで大丈夫なように
<?php function getVisibilityAsArray($class_name, array $var_names) { $result = array(); $class = new ReflectionClass($class_name); foreach ($class->getProperties() as $property) { if (!in_array($property->getName(), $var_names)) { continue; } $prefix = ''; if ($property->isProtected()) { $prefix = chr(0) . '*' . chr(0); } else if ($property->isPrivate()) { $prefix = chr(0) . $class_name . chr(0); } $result[] = $prefix . $property->getName(); } return $result; }
といった関数を用意して書き換えてみました。
<?php class ClassA { private $a; protected $b; public $c; private $prefixs = array(); public function setExclude($prefix) { if (!is_array($prefix)) { $prefix = (array)$prefix; } $this->prefixs = $prefix; $this->prefixs[] = 'prefixs'; } public function __sleep() { $excludes = getVisibilityAsArray(__CLASS__, $this->prefixs); return array_diff(array_keys((array)$this), $excludes); } } $obj = new ClassA(); var_dump(serialize($obj)); $obj = new ClassA(); $obj->setExclude(array('a')); var_dump(serialize($obj)); $obj = new ClassA(); $obj->setExclude(array('b')); var_dump(serialize($obj)); $obj = new ClassA(); $obj->setExclude(array('c')); var_dump(serialize($obj)); $obj = new ClassA(); $obj->setExclude(array('a', 'b', 'not_exist')); var_dump(serialize($obj));
実行結果は次の通り。
$ php __sleep.php string(85) "O:6:"ClassA":4:{s:9:"ClassAa";N;s:4:"*b";N;s:1:"c";N;s:13:"ClassAnames";a:0:{}}" string(40) "O:6:"ClassA":2:{s:4:"*b";N;s:1:"c";N;}" string(45) "O:6:"ClassA":2:{s:9:"ClassAa";N;s:1:"c";N;}" string(48) "O:6:"ClassA":2:{s:9:"ClassAa";N;s:4:"*b";N;}" string(27) "O:6:"ClassA":1:{s:1:"c";N;}" $
サブクラスでもうまく動きます。
<?php class ClassB extends ClassA { private $x; protected $y; public $z; } $obj = new ClassB(); $obj->setExclude(array('a')); var_dump(serialize($obj)); // 元々、aはシリアライズされない $obj = new ClassB(); $obj->setExclude(array('b')); var_dump(serialize($obj)); $obj = new ClassB(); $obj->setExclude(array('c')); var_dump(serialize($obj)); $obj = new ClassB(); $obj->setExclude(array('a', 'b', 'y', 'z', 'not_exist')); var_dump(serialize($obj));
以下、実行結果。
$ php __sleep02.php string(81) "O:6:"ClassB":5:{s:9:"ClassBx";N;s:4:"*y";N;s:1:"z";N;s:4:"*b";N;s:1:"c";N;}" string(86) "O:6:"ClassB":5:{s:9:"ClassBx";N;s:4:"*y";N;s:1:"z";N;s:9:"ClassAa";N;s:1:"c";N;}" string(89) "O:6:"ClassB":5:{s:9:"ClassBx";N;s:4:"*y";N;s:1:"z";N;s:9:"ClassAa";N;s:4:"*b";N;}" string(68) "O:6:"ClassB":4:{s:9:"ClassBx";N;s:4:"*y";N;s:1:"z";N;s:1:"c";N;}" $
ちなみに、Serializableインターフェース - Do You PHP はてなで触れたPHP Serialization, Stack Traces, and Exceptions | Articles - Fabien Potencierでも「なぜ__sleep使わないでSerializableインターフェースを使ったの?」というコメントがあがっていますが、
ということだそうです。
#4 Fabien ― February 12, 2009 07:41
@Eric: Because the Serializable interface is much more flexible. In this specific case, we don't use anything fancy, but for consistency, I always use the interface.