45

__sleepPHPでの__wakeupandmagicメソッドの使用法は何ですか?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つの方法の適切な使用法は何ですか?

4

5 に答える 5

51

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

シリアライゼーションは、オブジェクトを永続化するために使用されます。オブジェクトの表現を文字列として取得し$_SESSION、データベース、Cookie、またはその他の必要な場所に格納できます。

リソース値

ただし、リソース タイプの値をシリアル化 (つまり、テキスト表現に変換) するserialize() ことはできません。これが、これらの値がすべてing後に失われる理由です。unserialize()

オブジェクトグラフ

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

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

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

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

たとえば、$databaseオブジェクトは によって参照されますが$obj->db、他のオブジェクトによっても参照されます。データベース オブジェクトの分離されたインスタンスではなく、次のセッションの他のすべての$obj->dbオブジェクトと同じオブジェクトになりたいと思うでしょう。unserialize()

この場合、次__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>
}

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

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

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

同様に、必要に__sleep()応じてオブジェクトを「登録解除」するために使用できます。(オブジェクトはシリアライズされても破棄されませんが、状況によっては意味があるかもしれません。)

閉鎖

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

于 2012-07-24T12:28:17.570 に答える
15

これらはフック関数によく似ており、必要に応じて使用できます。この単純なリアルタイムの例を思いつきました。次の 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が同じであることがわかります。

于 2014-12-05T04:54:15.820 に答える
8

These methods are used when calling serialize() and unserialize() on the objects to make sure you have a hook to remove some properties like database connections and set them back when loading. This happens when storing objects in sessions among other things.

于 2012-07-24T11:59:21.697 に答える
5

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() 呼び出しを非シリアル化の最後まで遅らせることもできます。

于 2019-11-10T13:28:39.960 に答える
4

これを試してください

<?php
  $ob = new sleepWakeup();
  $safe_me = serialize($ob);
  $ob = unserialize($safe_me);
?>
于 2012-07-24T12:00:01.043 に答える