ぶち当たったのでメモ。
__destruct()
と __sleep()
以下のようなクラスがあるとする。
class Test {
private $data;
public function __sleep() {
echo "__sleep() called\n";
return array('data');
}
public function __destruct() {
echo "__destruct() called\n";
}
}
1. serialize()
を実行するとき
serialize(new Test);
インスタンスを作ってシリアライズを実行、そのままガベージコレクションを発動させるコード。このときは当然
__sleep() called
__destruct() called
となるが・・・
2. $_SESSION
が保存されるとき
session_start();
$_SESSION['test'] = new Test;
PHPはスクリプト終了時に session_encode()
を使ってセッションの特殊なシリアライズを行うが、このときは
__destruct() called
__sleep() called
デストラクタが先に呼ばれてしまう。自前のシリアライズとセッションへの格納を両方とも想定する場合、どちらが先にコールされても不具合が発生しないようにクラスを設計しておく必要がある。 is_resource()
なんかが役に立つんじゃないだろうか。
$fp = tmpfile();
echo "【Before fclose():】\n";
echo 'var_dump($fp): '; var_dump($fp);
echo '(bool)$fp: '; var_dump((bool)$fp);
echo 'is_resource($fp): '; var_dump(is_resource($fp));
fclose($fp);
echo "\n";
echo "【After fclose():】\n";
echo 'var_dump($fp): '; var_dump($fp);
echo '(bool)$fp: '; var_dump((bool)$fp);
echo 'is_resource($fp): '; var_dump(is_resource($fp));
この実行結果は下記のようになる。
【Before fclose():】
var_dump($fp): resource(1) of type (stream)
(bool)$fp: bool(true)
is_resource($fp): bool(true)
【After fclose():】
var_dump($fp): resource(1) of type (Unknown)
(bool)$fp: bool(true)
is_resource($fp): bool(false)
is_resource()
は解放したリソースに関しては FALSE
を返すが、ブール判定はそうではないので間違えないように。
__destruct()
と serialize()
こちらも同様の結果となった。
class Test implements Serializable {
private $data;
public function serialize() {
echo "serialize() called\n";
return serialize($this->data);
}
public function unserialize($data) {
$this->data = unserialize($data);
}
public function __destruct() {
echo "__destruct() called\n";
}
}
【追記】 解決法
PHP5.4以降専用
session_register_shutdown();
PHP5.3以前向け
register_shutdown_function('session_write_close');
でセッションが保存される時の挙動を serialize()
するときの挙動に近づけられるようだ。詳しくはコメント欄を参照。