web-dev-qa-db-fra.com

Utilisation de PHP Magic Methods __sleep and __wakeup

À quoi sert le __sleep et __wakeup des méthodes magiques en PHP? J'ai lu la documentation PHP mais ce n'est toujours pas clair:

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();

Cet exemple de code imprime:

Time to sleep.
Time to wakeup.

Si je devais renommer __sleep et __wakeup à foo et bar alors cela fait la même chose. Quelle est la bonne utilisation de ces deux méthodes?

40
Madan Sapkota

Comme déjà décrit, __sleep() est appelé lorsque vous serialize() un objet et __wakeup() après vous unserialize() it.

La sérialisation est utilisée pour conserver les objets: vous obtiendrez une représentation d'un objet sous forme de chaîne qui pourra ensuite être stockée dans $_SESSION, Une base de données, des cookies ou n'importe où ailleurs.

Valeurs des ressources

Cependant, serialize() ne peut pas sérialiser (c'est-à-dire se transformer en représentation textuelle) la valeur de type de ressource . C'est pourquoi toutes ces valeurs disparaîtront après unserialize() ing.

Graphique d'objet

ou membres, et les membres du membre et le ... à l'infini

Un autre point, peut-être plus important, est que serialize() traversera tout le graphe objet de $obj Si vous le sérialisez. C'est formidable lorsque vous en avez besoin, mais si vous n'avez besoin que de parties de l'objet et que certains objets liés sont "spécifiques à l'exécution" et partagés entre de nombreux objets mais également par d'autres objets, vous ne souhaiterez peut-être pas ce comportement.

PHP gère correctement les graphiques cycliques! Signification: si (un membre de) $ a est lié à $ b et que $ b est lié à $ a, il est géré correctement, quel que soit le niveau.

Exemple - objets spécifiques à une session (partagés)

Par exemple, un objet $database Est référencé par $obj->db, Mais aussi par d'autres objets. Vous voudrez que $obj->db Soit les mêmes objets - après unserialize() ing - que tous les autres objets de votre prochaine session, pas une instance isolée de l'objet de base de données.

Dans ce cas, vous auriez une méthode __sleep() comme celle-ci:

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

puis le restaurer comme ceci:

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

Une autre possibilité est que l'objet fait partie d'une infrastructure de données (globale) où il doit être enregistré. Vous pouvez bien sûr le faire manuellement:

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

Cependant, si cela fait partie du contrat d'objets qu'il doit être dans ce registre, ce n'est pas une bonne idée de laisser cet appel magique à l'utilisateur de votre objet. La solution idéale est, si l'objet se soucie de ses responsabilités, c'est-à-dire d'être enregistré dans Thing. C'est ce que __wakeup() vous permet de faire de manière transparente (c'est-à-dire qu'il n'a plus besoin de s'inquiéter de cette dépendance magique) pour votre client.

De même, vous pouvez utiliser __sleep() pour "désenregistrer" un objet si nécessaire. (Les objets ne sont pas détruits lorsqu'ils sont sérialisés, mais cela peut avoir un sens dans votre contexte.)

Fermetures

Enfin, les fermetures ne prennent pas en charge la sérialisation non plus. Cela signifie que vous devrez recréer toutes les fermetures attachées dans __wakeup().

46
phant0m

Ils sont à peu près comme des fonctions de crochet, que nous pouvons utiliser selon nos besoins. Je suis venu avec cet exemple simple en temps réel. Essayez maintenant d'exécuter ce code dans deux scénarios:

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));

Scénario 1:

Tout d'abord, en commentant les méthodes __sleep() et __wakeup(), vérifiez la sortie. Vous trouverez la ressource manquante lorsque vous la désérialiserez.

Scénario 2:

Maintenant, essayez de l'exécuter sans les commenter, vous comprendrez que l'objet sauvegardé en premier et en dernier var_dump Serait le même.

12
Mohammed Asad

Ces méthodes sont utilisées lors de l'appel de serialize () et unserialize () sur les objets pour vous assurer que vous disposez d'un hook pour supprimer certaines propriétés comme les connexions à la base de données et les redéfinir lors du chargement. Cela se produit entre autres lors du stockage d'objets dans des sessions.

7

essayez ceci

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

Depuis PHP 7.4 il y aura de nouvelles méthodes __serialize () et __unserialize () disponibles qui devraient légèrement changer l'utilisation des méthodes magiques __sleep et __wakeup.

PHP propose actuellement deux mécanismes pour la sérialisation personnalisée des objets: les méthodes magiques __sleep ()/__ wakeup (), ainsi que l'interface Serializable. Malheureusement, les deux approches ont des problèmes qui seront discutés ci-dessous. Ce RFC propose d'ajouter un nouveau mécanisme de sérialisation personnalisé qui évite ces problèmes.

Plus d'informations dans PHP RFC manual 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;

L'utilisation est très similaire à l'interface sérialisable. D'un point de vue pratique, la principale différence est qu'au lieu d'appeler serialize () dans Serializable :: serialize (), vous retournez directement les données qui doivent être sérialisées en tant que tableau.

L'exemple suivant illustre comment __serialize ()/__ unserialize () sont utilisés et comment ils composent sous héritage:

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"];
    }
}

Cela résout les problèmes avec Serializable en laissant la sérialisation et la désérialisation réelles à l'implémentation du sérialiseur. Cela signifie que nous n'avons plus à partager l'état de sérialisation, et ainsi éviter les problèmes liés à l'ordre de rétro-référence. Cela nous permet également de retarder les appels __unserialize () à la fin de la désérialisation.

1
DevWL