web-dev-qa-db-ja.com

PHPマジックメソッド__sleepおよび__wakeupの使用

PHPでの__sleepおよび__wakeupマジックメソッドの使用方法を教えてください。 PHPのドキュメントを読みましたが、まだはっきりしていません:

class sleepWakeup {

    public function __construct() {
        // constructor //
    }

    public function __sleep() {
        echo 'Time to sleep.';
    }

    public function __wakeup() {
        echo 'Time to wakeup.';
    }

}

$ob = new sleepWakeup();

// call __sleep method
echo $ob->__sleep();

echo "\n";

// call __wakeup method
echo $ob->__wakeup();

このサンプルコードは次の内容を出力します。

Time to sleep.
Time to wakeup.

__sleep__wakeupの名前をfoobarに変更すると、同じことが行われます。これら2つの方法の適切な使用法は何ですか?

40
Madan Sapkota

すでに説明したように、 __sleep() は、オブジェクト serialize() オブジェクトおよび __wakeup()のときに呼び出されます あなたの後に unserialize() それ。

オブジェクトを永続化するためにシリアル化が使用されます。オブジェクトの表現を文字列として取得し、_$_SESSION_、データベース、Cookie、またはその他の任意の場所に保存できます。

リソース値

ただし、serialize()cannotresource type の値をシリアル化(つまり、テキスト表現に変換)します。これが、unserialize() ingした後にこれらの値がすべて失われる理由です。

オブジェクトグラフ

またはメンバー、およびメンバーのメンバーと...広告無限

別の、おそらくより重要な点は、serialize()がシリアライズすると、_$obj_のオブジェクトグラフ全体をトラバースすることです。これは必要な場合に最適ですが、オブジェクトの一部のみが必要で、特定のリンクされたオブジェクトが「ランタイム固有」であり、多くのオブジェクト間で共有されているだけでなく、他のオブジェクトによっても共有されている場合、その動作は望ましくない場合があります。

PHPは循環グラフを正しく処理します!意味:$ a(のメンバー)が$ bにリンクしていて、$ bが$ aにリンクしている場合、正しく処理されますが、深いレベルがあります。

例-セッション固有の(共有)オブジェクト

たとえば、_$database_オブジェクトは_$obj->db_によって参照されますが、他のオブジェクトによっても参照されます。 _$obj->db_をunserialize() ingの後で、データベースオブジェクトの分離されたインスタンスではなく、次のセッションの他のすべてのオブジェクトと同じオブジェクトにする必要があります。

この場合、次のような__sleep()メソッドがあります。

_/**
/* DB instance will be replaced with the one from the current session once unserialized()
 */
public function __sleep() {
    unset($this->db);
}
_

次のように復元します。

_public function __wakeup() {
    $this->db = <acquire this session's db object>
}
_

別の可能性は、オブジェクトが登録される必要があるいくつかの(グローバル)データ構造の一部であるということです。もちろん、これを手動で行うこともできます。

_$obj = unserialize($serialized_obj);
Thing::register($obj);
_

ただし、それがそのレジストリにある必要があるオブジェクトコントラクトの一部である場合、この魔法の呼び出しをオブジェクトのユーザーに任せることはお勧めできません。理想的な解決策は、オブジェクトがその責任に関心がある場合、つまりThingに登録されている場合です。 __wakeup()を使用すると、クライアントに対して透過的に(つまり、魔法のような依存関係について心配する必要がなくなります)ことができます。

同様に、必要に応じて、__sleep()を使用してオブジェクトを「登録解除」できます。 (オブジェクトはシリアル化されても破棄されませんが、コンテキストで意味をなす場合があります。)

閉鎖

最後に重要なことですが、クロージャはnotでもシリアル化をサポートします。つまり、__wakeup()で、アタッチされたすべてのクロージャを再作成する必要があります。

46
phant0m

これらはフック関数によく似ており、必要に応じて使用できます。この簡単なリアルタイムの例を思いつきました。次に、このコードを2つのシナリオで実行してみます。

_class demoSleepWakeup {
    public $resourceM;
    public $arrayM;

    public function __construct() {
        $this->resourceM = fopen("demo.txt", "w");
        $this->arrayM = array(1, 2, 3, 4); // Enter code here
    }

    public function __sleep() {
        return array('arrayM');
    }

    public function __wakeup() {
        $this->resourceM = fopen("demo.txt", "w");
    }
}

$obj = new demoSleepWakeup();
$serializedStr = serialize($obj);
var_dump($obj);
var_dump($serializedStr);
var_dump(unserialize($serializedStr));
_

シナリオ1:

最初に__sleep()および__wakeup()メソッドをコメント化して、出力を確認します。シリアル化を解除すると、リソースが不足していることがわかります。

シナリオ2:

ここで、コメントを外して実行してみてください。最初と最後の_var_dump_にダンプされたオブジェクトが同じであることがわかります。

12
Mohammed Asad

これらのメソッドは、オブジェクトでserialize()およびunserialize()を呼び出すときに使用され、データベース接続などの一部のプロパティを削除して、ロード時にそれらを元に戻すフックがあることを確認します。これは、特にセッションにオブジェクトを保存するときに発生します。

これを試してください

<?php
  $ob = new sleepWakeup();
  $safe_me = serialize($ob);
  $ob = unserialize($safe_me);
?>
4
donald123

PHP 7.4以降、新しいメソッド__serialize()および__unserialize()が使用可能になり、__ sleepおよび__wakeupマジックメソッドの使用法が若干変更されます。

PHPは現在、オブジェクトのカスタムシリアル化のための2つのメカニズムを提供しています:__sleep()/ __ wakeup()マジックメソッドとSerializableインターフェースです。残念ながら、どちらのアプローチにも以下で説明する問題があります。このRFCでは、これらの問題を回避する新しいカスタムシリアル化メカニズムを追加することを提案しています。

PHP RFCマニュアル https://wiki.php.net/rfc/custom_object_serialization の詳細。

// Returns array containing all the necessary state of the object.
public function __serialize(): array;

// Restores the object state from the given data array.
public function __unserialize(array $data): void;

使用方法は、Serializableインターフェースに非常に似ています。実用的な観点からの主な違いは、Serializable :: serialize()内でserialize()を呼び出す代わりに、シリアル化する必要のあるデータを配列として直接返すことです。

次の例は、__ serialize()/ __ unserialize()がどのように使用され、継承のもとでどのように構成されるかを示しています。

class A {
    private $prop_a;
    public function __serialize(): array {
        return ["prop_a" => $this->prop_a];
    }
    public function __unserialize(array $data) {
        $this->prop_a = $data["prop_a"];
    }
}
class B extends A {
    private $prop_b;
    public function __serialize(): array {
        return [
            "prop_b" => $this->prop_b,
            "parent_data" => parent::__serialize(),
        ];
    }
    public function __unserialize(array $data) {
        parent::__unserialize($data["parent_data"]);
        $this->prop_b = $data["prop_b"];
    }
}

これにより、シリアライザの実装に実際のシリアル化と非シリアル化を任せることで、Serializableの問題が解決されます。つまり、シリアル化の状態を共有する必要がなくなり、逆参照の順序付けに関する問題を回避できます。また、__ unserialize()呼び出しをシリアル化解除の終わりまで遅らせることができます。

1
DevWL