8
8

More than 5 years have passed since last update.

__destruct() と __sleep() の順序

Last updated at Posted at 2013-12-12

ぶち当たったのでメモ。

__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() するときの挙動に近づけられるようだ。詳しくはコメント欄を参照。

8
8
14

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8