使用PHP Magic Methods __sleep和__wakeup

时间:2012-07-24 11:56:28

标签: php oop magic-methods

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,那么它会做同样的事情。这两种方法的正确用法是什么?

5 个答案:

答案 0 :(得分:38)

如上所述,当您__sleep()某个对象时会调用serialize(),而__wakeup()之后会调用unserialize()

序列化用于持久化对象:您将获得一个对象的表示形式,然后可以将其存储在$_SESSION,数据库,cookie或任何您想要的其他地方。

资源值

但是,serialize() 无法序列化(即转换为文本表示)值resource type。这就是unserialize()之后所有这些值都会丢失的原因。

对象图

或成员,成员的成员和无限的广告

另一个,也许更重要的一点是,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>
}

另一种可能性是,该对象是需要注册的某些(全局)数据结构的一部分。你当然可以手动完成这个:

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

但是,如果它是对象契约的一部分,它需要在该注册表中,那么将这个神奇的调用留给对象的用户并不是一个好主意。理想的解决方案是,如果对象关心它的责任,即在Thing中注册。这就是__wakeup()允许你透明地做的事情(即他不再需要担心那种神奇的依赖)给你的客户。

同样,如果合适,您可以使用__sleep()“取消注册”对象。 (对象在序列化时不会被破坏,但在您的上下文中可能有意义。)

闭包

最后但并非最不重要的是,闭包也支持序列化。这意味着您必须在__wakeup()中重新创建所有附加的闭包。

答案 1 :(得分:9)

它们非常类似于钩子功能,我们可以根据需要使用它们。我想出了这个简单的实时例子。现在尝试在两种情况下执行此代码:

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中转储的对象是相同的。

答案 2 :(得分:7)

在对象上调用serialize()和unserialize()时使用这些方法,以确保您有一个钩子来删除一些属性,如数据库连接,并在加载时将它们设置回来。在会话中存储对象时会发生这种情况。

答案 3 :(得分:4)

试试这个

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

答案 4 :(得分:0)

自PHP 7.4起,将提供新的__serialize()和__unserialize()方法,这些方法应会稍微改变__sleep和__wakeup魔术方法的用法。

  

PHP当前提供两种机制,用于自定义序列化   对象:__sleep()/ __ wakeup()魔术方法,以及   可序列化的接口。不幸的是,两种方法都有问题   将在下面讨论。该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()调用延迟到反序列化结束。