Eines schönen Arbeitstages schrieb ich Unit-Tests für Geschäftslogik für das Projekt, in dem ich arbeite. Meine Aufgabe war es, einige private Eigenschaften der Klasse mit bestimmten Werten zu initialisieren.
Normale Setter konnten nicht verwendet werden, da dort eine gewisse Logik formuliert wurde. Das Erben oder Sperren einer Klasse funktionierte ebenfalls nicht, da sie für endgültig erklärt wurde. Und selbst das Nachdenken passte nicht. Also suchte ich nach Lösungen für dieses Problem.
Ich habe einen interessanten Artikel gefunden , der beschreibt, wie die letzte Klasse mit der Bibliothek dg / bypass- final gesperrt werden kann. Diese Option hat mir gefallen und ich habe versucht, sie zu implementieren. Leider war ich nicht erfolgreich, da das Projekt die alte Version von PHPUnit verwendet.
Beim Nachdenken erinnerte ich mich an die Closure
Klasse und insbesondere an ihre statische bind()
-Methode, die anonyme Funktionen im Kontext des gewünschten Objekts einer Klasse implementieren kann. Weitere Informationen hierzu finden Sie in der offiziellen Dokumentation . Daher habe ich ein Merkmal erstellt, das ich in meinen Tests verwendet habe (vielleicht ist auch jemand nützlich).
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); } }
Dieses Merkmal verwendet ein Klassenobjekt, den Namen der Eigenschaft, für die Sie den Wert festlegen möchten, und tatsächlich den Wert selbst. Als nächstes wird eine einfache anonyme Funktion deklariert, die mit dem Zeiger $this
den resultierenden Wert einer Klasseneigenschaft zuweist. Als nächstes kommt die Closure
Klasse mit ihrer statischen bind()
-Methode. Die Methode akzeptiert ein Klassenobjekt, die oben beschriebene anonyme Funktion und den vollständigen Namen der Klasse. Daher ist eine anonyme Funktion in den Kontext des Objekts eingebettet, und die bind()
-Methode gibt uns ein Objekt der Closure
Klasse zurück, das wir als reguläre Funktion aufrufen können, da sie die magic __invoke()
-Methode definiert. Und voila!
Am Ende gelang es mir, mein Problem zu lösen, und dann erinnerte ich mich an das Singleton-Entwurfsmuster. Wird es auf die gleiche Weise möglich sein, eine anonyme Funktion zu implementieren, die neue Objekte der Klasse erstellt? Natürlich habe ich es überprüft!
Indem Sie ein kleines Stück Code schreiben
Sandbox mit einem 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));
Dies funktioniert nach dem gleichen Prinzip, aber anstatt einer Klasseneigenschaft einen Wert zuzuweisen, wird mit dem new
Operator ein neues Objekt erstellt. Die Funktion \spl_object_id()
gibt eine eindeutige Kennung für das Objekt zurück. Weitere Informationen zu dieser Funktion finden Sie in der Dokumentation . Mit spl_object_id()
und var_dump()
ich eindeutige Bezeichner von Objekten ab und sehe, dass sie unterschiedlich sind! Ich habe es immer noch geschafft, diese Theorie zu bestätigen und eine neue Instanz der Singleton-Klasse zu erstellen!
In diesem Artikel wollte ich meinen sehr merkwürdigen Fund mit der PHP-Community teilen.
Vielen Dank für Ihre Aufmerksamkeit!