Logging global PHP objects and saving memory using a lazy loading proxy

Quite often when you are working with legacy code you will come across a mess of globals. Every single method will make use of the same global instance of the database class for example. So where do you begin to work with this massive impediment?

Logging is a great way to see what methods and classes are being used by you application and where. To achieve this you would normally need to add a logging call to each and every method in the code base. Clearly this would be incredibly tedious and time consuming.

This is where a proxy object can be implemented to save time and centralise the logging functions. The basic idea of a proxy object is that it will be instantiated in place of the actual class and the proxy will delegate any calls through to the original class. For the purposes of this example the original class will be called Database and the proxy object will be called LazyLoadingProxy.

Lazy Loading Proxy diagram

Based upon the diagram above we can work through a simple example to demonstrate this powerful technique. Initially the class lazy loading proxy is instantiated and assigned to the global variable $Database.

https://gist.github.com/724314#file_gistfile2.php

When the proxy is setup it is told the class name and then the exact path where it can find the class. The proxy will now lie in wait for a request to a method or class property of the proxied class (Database).

https://gist.github.com/724314#file_gistfile3.php

If the underlying class is accessed the proxy will require_once() Database.class.php and create a new instance of it on the fly. This is the lazy loading aspect of the process. In this way resources are not consumed until the Database class is really needed. Once instantiated the object is “cached” so that any future requests will reuse the same instance of the class.

So our proxy lazy load class is very simple like the following:

https://gist.github.com/724314#file_lazy_loading_proxy.php

To log the calls you can simply echo out the request in each magic method or you could make it more sophisticated with the help of FirePHP and FireBug. I use the latter and it is really handy to see where the legacy code is calling classes and methods that it should not be!

The only hiccup I have found with this system is that all tests using method_exists() need to be changed to be is_callable() . This is because the former appears to use PHPs introspection methods where as the latter attempts to call the method from what I can guess.

This design pattern also has another application in systems that make extensive use of global objects which are instantiated at bootstrap. It will save memory as only the classes that are actually referenced will be instantiated just in time.

I recently used this to make a large legacy application more efficient. In the bootstrap file it simply looped through all the files in a directory called classes and looked for files beginning with a certain prefix (for example SYSBlog.php or SYSSessions.php). When it found a file it would load it via require_once() and then using eval() it would instantiate the class. This would subsequently be used globally as you can see below.

https://gist.github.com/717262#file_legacy_includes.php

A few optimisations that could be applied to the aforementioned code would be to:

  • make use of the glob() function in PHP, which allows you loop through files that match a pattern. In this case something like glob('SYS*.php') would have done the trick.
  • remove the use of eval() which could just as easily be implemented using variable variables. So in the example this should be implemented as $$className = $className() and if you need to access a property $$className->getMethod().
  • make use of the SPL autoload functions that PHP provides to call the classes when they are needed.

But lets just be honest with ourselves; the code is crap and needs to be rewritten, but what if we do not have the time? Then we can use the following (only slightly improved I know):

https://gist.github.com/717262#file_slightly_improved_includes.php

Now the class will only be loaded into memory when it is actually being used. This makes sense when you think that a response to a poll would never need to use methods in the blog classes for example. I found this technique was saving 30-70% of memory depending on the request. This could be most easily seen when making ajax requests as they often only made use of one class to perform their actions.

In the actual project the code is slightly different in that it does use an autoloader called Überloader written by Jamie Matthews (a colleague at Mosaic), which is available over on his github account. With the autoloader I was able to dispense with all the require_once()s and the need to pass the class path into the lazy loading proxy class.

blog comments powered by Disqus
  1. wnemay reblogged this from holywell
  2. holywell posted this