static considered harmful - Kore Nordmann - PHP / Projects / Politics

Kore Nordmann - PHP / Projects / Politics

By Kore Nordmann, first published at Wed, 02 Mar 2011 10:42:00 +0100

static considered harmful

This is a provoking title, but, in my honest opinion, a very valid rule of thumb. During the last years of teaching other developers, developing PHP libraries and applications and doing quality assurance on other peoples software I came to this rather abrasive conclusion.

Introduction

Let's start at the very beginning - assuming we want to do object oriented software development. This is where we started to learn about objects and classes. The term here is object oriented programming and not class oriented programming, so let's remember what objects are about.

Objects encapsulate state and provide methods to transform this state. So far, so simple. Classes on the other hand are the blueprints for the objects, or better, define the type of an object. Without going into details about inheritance, subtype polymorphism and aggregation - which are IMHO the key points to start to understand object oriented design - this leaves us with a very basic conclusion: We should care about our objects - the classes are just their type definitions.

Introducing static

The keyword static now enables us to use methods and properties (state) per-class and not on a per-object basis. This basically leaves us with class oriented programming. This seems to make a lot of things simpler, at a first glance, but will almost certainly hurt you in the long term.

Why? Let's start with some examples. And yes, I have done all this myself during the past years and you will find examples of static misuse in almost every library out there.

Static dependencies

If you are using a class with static methods you always have to introduce static dependencies in your code. Let's check out an example:

<?php class FileTool { public static function recursiveDelete( $path ) { // … } } /* … */ class MyWhatEver { public function doSomething() { // … FileTool::recursiveDelete( '/' ); // … } }

For static method calls you have to use the class name of the implementing class in your method call FileTool::recursiveDelete(). The code in the class MyWhatEver now has a hard dependency on the FileTool class. There is now no way to change the used implementation without touching the code of MyWhatEver and every other class using the file tool.

There are usually two reactions regarding this:

  1. Why would I do that?

  2. If I want to do that, I'd just change the code.

The answers to those questions depend on the environment you are working in. The original developer of an application might not need to replace the method, if he / she does not do any unit testing. But if your application or library is used by others (for example another department in your company) they might want to replace the functionality, even in such a simple case, because the files are now managed on Cassandra, or similar. Or even if the code, which does the recursive deletion, contains a bug or misses a simple feature (link handling, permission handling on $platform). The only way to change the behaviour would be patching the foreign released code.

In such cases, changing the code often is no option. If you change foreign code without commit access to their repository you end up maintaining patches, which then need to be ported to every new version of the external library.

With the possibility of variable class names in static calls it is not even possible to automatically detect all calls to the static method using static source code analysis. (Rice's theorem)

Testing

If you start unit-testing your code you will quickly notice why static dependencies are so bad - because it becomes almost impossible to replace implementations during testing, so that you often end up testing your full framework stack or need to write dozens of helper classes just for the sake of testability. And you would not need that without static dependencies.

Let's consider the trivial example above again - when testing the doSomething() method in a unit test we would not want it to operate on the file system directly - because it is slow and most probably not relevant to the test case. For testing you would want to replace FileTool by a mock just returning canned data or an alternative implementation using something like vfsStream - a PHP stream wrapper which operates in memory.

If the object on which you call the doSomething() method now aggregates a FileTool object instead of the static call you could just pass another object implementing the same interface to the tested object and an alternative implementation would be used. In case of unit tests you could use PHPUnit's mocking features to automatically generate a mock object for you, which then is passed to the object. (There are also various other mock object libraries out there.)

In non-trivial applications this might result in complex object graphs, since the FileTool might not only be used by one type of objects, but in multiple places all over the application. This is especially true for Loggers or similar ubiquitous types. Passing (all) dependencies of a type to the object of the type is called dependency injection - and to build object graphs in complex applications, re-using the same objects for one type, can be done easily using dependency injection containers (DIC). But this is a topic for another blog post.

Examples

The example above is still quite artificial - so let's look at some of the more popular frameworks / component libraries out there and see why static is harmful in real code - already used in the wild.

I'll start with code I wrote and maintain and then cover three of the most popular libraries / frameworks: Apache Zeta Components, Symfony and Zend Framework. I do not intend to offend anybody (that's why I start with my own code and failures) - but I think it is really useful to outline the problems with static on examples of real code.

PHPillow

PHPillow is just a simple wrapper to access CouchDB, which synchronizes views and offers simple features for document validation. While you can just perform arbitrary requests against CouchDB using PHPillow it also allows you to define a document, including simple validation constraints for its properties, and store / retrieve them from the database.

I started developing PHPillow when Late Static Binding (LSB) had been introduced in PHPs trunk. Wanting to play with a new language feature and developing a library seldom ends up well, and thus PHPillow used static and LSB all over the place. By now we replaced some of those parts and you can use most of PHPillow also non-statically - bot not everything.

One thing, which is still left in the code, is the creation of response objects from CouchDBs HTTP responses. CouchDB itself returns JSON structures, which, together with HTTP status code of the response, tell you what happened and provide you the requested data. The data looks different in case of errors, view requests, document listings or when requesting a single document.

In PHPillow there is a phpillowResponseFactory with a static method parse(), which creates the proper response object from the JSON and HTTP response data. The parse() method is then called in the different connection handlers to create the response objects.

If CouchDB now implements a new feature, which emits an entirely different response type, you could request this using PHPillow, since you can basically perform any HTTP request, but the response factory would most probably need some adaption. But because of the static reference in the connection handlers, a user of the library could only do that by patching the response factories source code. If the connection handlers now would aggregate the response factory (and maybe use some default implementation) the user of the library could just inject his own response factory and be fine.

This is the classic example of a static dependency which makes it virtually impossible to adapt or enhance functionality in an existing library. PHPillow has a quite high test coverage, but the connection handler tests can obviously not be real unit tests, since they always also cover the response factory. There is also no sane way to replace it during the tests by a mock.

Zend Framework

There is a particularly bad example for static dependencies in the Zend Framework, which actually makes their OpenID component unusable for any project which is not based on the Zend Framework MVC stack. The idea of a loosely coupled, reusable component framework is destroyed by a single static method.

I was evaluating Zend_OpenId as an alternative for an OpenID implementation for Arbit. An OpenID consumer, which I was looking for, is required to perform HTTP redirects - this is not easy to integrate in a library since this most likely involves the whole MVC stack. You might just want to return the HTTP header and shut down or clean up your application framework or you might also echo some HTML / JavaScript also performing the redirect. An OpenID library thus requires some kind of callback for the application to hook into.

In Zend_OpenId the static method Zend_OpenId::redirect() is used to execute the HTTP redirect. This method depends on Zend_Controller_Response_Abstract, thus the Controller component from the Zend Framework - which you do not want to use if you are already using a different MVC implementation. Since the method is called statically in the consumer and the provider implementation there is no easy way to replace the implementation with something working for you.

Additionally, in the consumer, the call to the static method redirect() is executed in a method with > 80 lines of code - so replacing the implementation of the calling method isn't really an option, either. Replacing such a long method would require massive copy & paste - and you would, for example, never again benefit of bug fixes in that method.

This example shows how a single static method call can make a whole component unusable for many potential users by adding static dependencies. Those static dependencies are often not easy to refactor out of the code. In this case a redirector interface with a default implementation working on the Zend Framework MVC stack would probably work, though.

Symfony

Symfony 1 uses static quite extensively - looking at the symfony 2 source code it got better. But there are still some occurences, which, most probably, will hurt in the long run. On the other hand, as of today, symfony 2 is still under development and this might change until its final release.

Even this is not the only usage of static I want to point to a very simple example: A utility class. Utility classes, performing small re-occuring jobs, are a very common use case for static methods, since they provide very simple functions and it is so much easier to just call them statically and not require a object to be passed as a dependency to the calling object.

There are a lot common utility classes, like for file handling, escaping, or in this case, for checking if a number is in a given interval. The class Symfony\Component\Translation\\Interval basically exposes one important public static method test($number, $interval), which tests if a number is contained in a defined interval. As you can see in the source of that class, the method is not entirely trivial. The method is used statically in the MessageSelector - which means that there is no way to replace the call to the interval checking other the re-implementing the MessageSelector itself.

So, if the interval checking, or the used regular expression, now contains a bug there is no way for you to fix the bug, other then patching foreign code. This violates the Open-Closed-Principle, which basically boils down to:

You should be able to change behaviour without touching a components code. (See S.O.L.I.D.)

It seems wrong on the first hand sight to use dependency injection for small stupid things like usually implemented by utility classes, at first. Why should I inject objects for recursive file deletion, escaping or simply test if a numer is contained in a given interval? But once you hit a bug in that code, or need to change the behaiviour of the utility class you'll wish you had done this from the very beginning.

This gets worse if the utility class depends on static properties, a global state, for doing its job. An utility class for escaping could, for example, contain a static property specifying the encoding of the strings it is used with. Now two different code paths in your application, both using the escaping utility, can influence themselves by changing the (default) encoding used by the utility class.

Apache Zeta Components

Checking the Apache Zeta Components, there is not much usage of static. There is one class implementing a singleton, the WebDAV server and some classes providing registries, like in the database component.

We all, by now, know that Singletons are bad, since they are just sophisticated global variables. Luckily it is easy and backwards compatible to just make it possible to instantiate the WebDAV server multiple times by making the __construct() method public again.

But why are registries also a bad thing? They offer a dedicated extension point after all.

The database component, for example, allows you to manage multiple connections using the ezcDatabase::setInstance( 'name', $connection );. This makes it possible to create and use any number of database connections and there are no limits on what kind of connection you inject and will use in your application.

The problem lies with the registry itself. If your application now uses the ezcDatabase registry there is a hard dependency on that particular registry. If the registry now contains a bug you might not have commit access to the Apache Zeta Components source code to fix it right away. On the other hand it would probably be very tedious to adapt all calls to the registry (imagine people are writing modules following your coding standards and you do not have access to all those modules source code using the registry).

And besides potential bugs in the registry you also might want to extend the registry at some point. For example it could be sensible to return different different connection for read and write access to the database, or something alike. In any case the static dependency on the registry might limit your abilities to extend your application.

Additionally registries add dependencies to your class which cannot easily be detected by the reader of your source code. All object your object depends on which are explicitly passed to the constructor are quite obvious. Everything else, constructed in the class, or fetched from global singletons or registries can be really hard to detect. This again makes your code harder to maintain.

Luckily, in the database component, using the registry is optional and you can just use the plain connection handlers and inject those objects like you should do.

A valid use case

There are some cases where APIs force you to use static, for example if some extension points only allow you to specify a class name and not an object. If you are allowed to pass objects you can control the life cycle and add additional state information. If only a class name can be specified, like for the PHP stream handlers, the only way to maintain state (a virtual file system, f.e.) are static properties.

So called factory methods are another valid use case for static methods. There are different sensible ways to construct a complex number, for example from polar coordinates or from its real and imaginary part. One of those specifications might be used as constructor parameters and the other using a static alternative constructor like ComplexNumber::fromPolar() which then returns a ComplexNumber instance. Using PHP 5.3s LSB feature this can even work with subtypes of the complex number.

Since the factory methods, just like all your class constructors, are used in your DIC anyways, this won't create a static dependency of any kind.

Speed

Static methods are faster!

This is, interestingly, often the first point you here when arguing about static. Until now I didn't see any application where the difference is relevant in any way. For the case the microsecronds are really relevant to you you should probably think about using plain functions or switch to another language (maybe Assembler?).

For me maintainability and the absence of a global state is far more important because it makes your code easier to extend, easier to read and thus easier to maintain.

Disclaimer

It might not be easy to get rid of all static dependencies in your application / framework. But you might at least want to think twice about adding any any static method or property to your code in the future. Omitting static makes the life easier for all object oriented developers. We want to write object oriented code, after all, and not class oriented code - otherwise we can just as well go back to plain old functions.

If you need help refactoring your code or want to learn more about object oriented design, you can hire me.

If you liked this blog post, or learned something please consider using flattr to contribute back: .

Comments

Add new comment

Fields with bold names are mandatory.

eZ Components

eZ Components

Exploring PHP

Exploring PHP

Hire me

Amazon wishlist

Powered by