In a previous article, I describe an approach to a generic PHP Singleton pattern that can be summarized by the following diagram:
However, there is one drawback: the original type hinting information is lost. That would make things like dependency injection impossible. I intend to fix this issue in the following sections.
While writing the first version of the generic singleton, I realized that the wrapping anonymous class behaved as a Proxy to the instance of the class to “singletonize”:
a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic.
singletonize(), we have the following setting:
- RealSubject: Class A
- Proxy: Anonymous Class
Our anonymous class does the forwarding of method calls to our instance of class A through an implicit implementation of the public methods of Class A. However, the pattern is not complete as it requires an explicit sharing of the Subject interface between Class A and our anonymous class.
While doing a quick research on proxies, it occured to me that testing frameworks have been applying the concept of proxies for a while now. Indeed, the concept of test doubles, most specifically of spies, is related to proxies. If you were to look at code such as Phockito and its implementation of spies, you will realize that test doubles typically intercept calls to the original class in order to allow mocking and stubbing. The Phockito code is instructive, however, its use of HEREDOC syntax and the php preprocessor makes for code I found a bit procedural. I needed something that was more object oriented.
I then stumbled on an article that discusses modifying final classes and methods when trying to mock them. The approach was based on modifying an abstract syntax tree (AST) using PHP-Parser. I could now see how to create a generic proxy. Creating the generic proxy would bring us one step closer to having a truly generic PHP Singleton pattern.
Our approach will be to generate an abstract syntax tree (AST) for the class of which we want to build a proxy. Based on that AST, we will generate a new one that is a subclass of the subject and with methods that call the methods of the subject class. The work will be driven by node Visitors, while Command objects will help the pattern builder execute the required AST building actions. Please find the demo code here.
The Node Visitors
Our node visitors just emit commands to our pattern builders as PHP-Parser traverses the subject class abstract syntax tree (AST). Here is the code for the node visitor:
Through Command objects, the node visitors call back the pattern builder. Here is the Proxy pattern builder:
Whenever our node Visitor encounters a public method node of the subject class, it will create a corresponding public method node on the Proxy class with the same number of arguments as the original method. The Proxy method will just call the original method with the passed arguments. PHP-Parser provides helper methods to easily build the required nodes on the proxy class AST.
With this approach, let’s have a class such as the following one:
We apply our pattern as such:
We get a true proxy of the subject class, in that it extends the subject class and forwards requests to an instance of it. Here is the generated code for the subject class above:
Now The Generic Singleton
We now can build our generic Singleton as an subclass of the generic Proxy builder. What we need to keep in mind is that it is the process of building a Singleton that is a variation of the process of building a proxy. Hence, I subclass the Proxy class into a Singleton class:
note that the logic for building the constructor has changed. We now
build a new instance of the subject class only if one wasn’t
instantiated already. Moreover, method forwarding now forwards to the
single instance and not to
Using our newly minted generic Singleton like this:
We get the generated code for the Singleton:
Now, we have a truly generic way of building singletons in PHP. The AST building approach to implementing patterns can obviously be applied to other languages that have reflection capabilities such as C# and its roslyn compiler. Moreover, we also learned that singleton building inherits from proxy building.