[From the sandbox] Breaking the design pattern - Singleton in PHP

[From the sandbox] Breaking the design pattern - Singleton in PHP


One fine working day I wrote unit tests for business logic on a project in which I work. I was faced with the task of initializing some private properties of a class with certain values.


Normal setters could not be used, since there was some logic written there. It was also impossible to inherit or lock a class, because it was declared final. And even the reflection did not fit. Therefore, I began to look for solutions to this problem.


Found an interesting article , which describes how to use the dg/bypass-finals library to lock the final class. I liked this option and I tried to implement it. Unfortunately, I did not succeed, because the project uses the old version of PHPUnit.


After thinking, I remembered the Closure class, and specifically about its static bind () method, which can embed anonymous functions in the context of the desired object of a particular class. More information about this can be found in the official documentation . Therefore, I created a trait that I used in my tests (maybe someone will also be useful)


  trait PrivatePropertySetterTrait
 {
  protected function assignValue ($ object, string $ attribute, $ value)
  {
  $ setter = function ($ value) use ($ attribute) {
  $ this- & gt; $ attribute = $ value;
  };

  $ setterClosure = \ Closure :: bind ($ setter, $ object, \ get_class ($ object));
  $ setterClosure ($ value);
  }
 }  

This trait takes a class object, the name of the property where the value is to be set and, in fact, the value itself. Next, a simple anonymous function is declared, which, using the $ this pointer, assigns the resulting value to a class property. Next comes the Closure class with its static bind () method. The method accepts a class object, the anonymous function described above, and the full class name. Thus, the anonymous function is embedded in the context of the object and the bind () method returns us an object of the Closure class, which we can call as a normal function, because it defines the magic method > __ invoke () . And voila!


As a result, I managed to solve my problem, and then I remembered the Singleton design pattern. Will it be possible to implement an anonymous function in the same way, which will create new class objects? Of course, I went to check it!


By writing a small piece of code


Sandbox with code

  & lt;? 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));  

which works on the same principle, but instead of assigning a value to a class property, a new object is created using the new operator. The \ spl_object_id () function returns a unique identifier for the object. More information about this feature can be found in the documentation . With spl_object_id () and var_dump () I derive unique identifiers of objects and see what they are different! I still managed to confirm this theory and create a new instance of the Singleton class!


In this article, I wanted to share with the PHP community my very curious finding.


Thank you for your attention!

Source text: [From the sandbox] Breaking the design pattern - Singleton in PHP