Briser le modèle de conception - Singleton en PHP

Un beau jour ouvrable, j'ai écrit des tests unitaires de logique métier sur le projet dans lequel je travaille. Ma tâche consistait à initialiser certaines propriétés privées de la classe avec des valeurs spécifiques.


Les setters normaux ne pouvaient pas être utilisés, car une certaine logique y était énoncée. L'héritage ou le verrouillage d'une classe n'a pas fonctionné non plus, car il a été déclaré final. Et même la réflexion ne convenait pas. J'ai donc commencé à chercher des solutions à ce problème.


J'ai trouvé un article intéressant qui décrit comment utiliser la bibliothèque dg / bypass-finals pour verrouiller la classe finale. J'ai aimé cette option et j'ai essayé de la mettre en œuvre. Malheureusement, je n’ai pas réussi, car le projet utilise l’ancienne version de PHPUnit.


Après réflexion, je me suis souvenu de la classe Closure , et plus particulièrement de sa méthode statique bind() , qui peut implémenter des fonctions anonymes dans le contexte de l'objet souhaité d'une classe. Vous trouverez plus d'informations à ce sujet dans la documentation officielle . Par conséquent, j'ai créé un trait que j'ai utilisé dans mes tests (peut-être que quelqu'un sera également utile)


 trait PrivatePropertySetterTrait { protected function assignValue($object, string $attribute, $value) { $setter = function ($value) use ($attribute) { $this->$attribute = $value; }; $setterClosure = \Closure::bind($setter, $object, \get_class($object)); $setterClosure($value); } } 

Ce trait prend un objet de classe, le nom de la propriété où vous souhaitez définir la valeur et, en fait, la valeur elle-même. Ensuite, une simple fonction anonyme est déclarée, qui, à l'aide du pointeur $this , attribue la valeur résultante à une propriété de classe. Ensuite, la classe Closure est livrée avec sa méthode bind() statique. La méthode accepte un objet classe, la fonction anonyme décrite ci-dessus et le nom complet de la classe. Ainsi, une fonction anonyme est incorporée dans le contexte de l'objet et la méthode bind() nous renvoie un objet de la classe Closure , que nous pouvons appeler comme une fonction régulière, car elle définit la __invoke() magique __invoke() . Et le tour est joué!


En fin de compte, j'ai réussi à résoudre mon problème, puis je me suis souvenu du modèle de conception Singleton. Sera-t-il possible de la même manière d'implémenter une fonction anonyme qui créera de nouveaux objets de la classe? Bien sûr, je suis allé le vérifier!


En écrivant un petit morceau de code


Sandbox avec un code

 <?php final class Singleton { private static $instance; public static function getInstance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } private function __construct() { } private function __clone() { } private function __wakeup() { } } $s1 = Singleton::getInstance(); \var_dump(\spl_object_id($s1)); $createNewInstance = function () { return new self(); }; $newInstanceClosure = Closure::bind($createNewInstance, $s1, Singleton::class); $s2 = $newInstanceClosure(); \var_dump(\spl_object_id($s2)); 

qui fonctionne sur le même principe, mais au lieu d'attribuer une valeur à une propriété de classe, un nouvel objet est créé à l'aide du new opérateur. La fonction \spl_object_id() renvoie un identifiant unique pour l'objet. Vous trouverez plus d'informations sur cette fonctionnalité dans la documentation . Avec l'aide de spl_object_id() et var_dump() je var_dump() des identifiants uniques d'objets et je vois ce qu'ils sont différents! J'ai quand même réussi à confirmer cette théorie et à créer une nouvelle instance de la classe Singleton!


Dans cet article, je voulais partager ma découverte très curieuse avec la communauté PHP.


Merci de votre attention!

Source: https://habr.com/ru/post/fr450554/


All Articles