Kore Nordmann - PHP / Projects / Politics ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :Author: Kore Nordmann :Date: Wed, 02 Mar 2011 10:54:37 +0100 :Revision: 7 :Copyright: CC by-sa ============================= ``static`` considered harmful ============================= :Description: 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 - let me tell you why… 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:: 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. __ http://arbitracker.org 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. __ https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Translation/Interval.php __ http://en.wikipedia.org/wiki/SOLID 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. __ http://gooh.posterous.com/singletons-in-php 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. __ http://php.net/manual/en/language.oop5.late-static-bindings.php 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. .. note:: If you need help refactoring your code or want to learn more about object oriented design, `you can hire me`__. __ http://qafoo.com/ .. Local Variables: mode: rst fill-column: 79 End: vim: et syn=rst tw=79 Trackbacks ========== Comments ======== - sokzzuka at Wed, 02 Mar 2011 11:52:48 +0100 Very valid points! Unfortunately, many people still think in therms of procedural programming, but use object oriented constructs. One of the first marks, that a programmer thinks procedural are static methods everywhere. Other are singletons or other global state constructs as well as classes, that consist 90% of getters and setters. A classic example of such design is the ZF controller layer. Anyway, good article ;) - Stuart Herbert at Wed, 02 Mar 2011 12:16:03 +0100 Hi, Interesting article, but it all hinges on an argument that isn't technically accurate. There is nothing stopping you doing $var::staticMethod() calls in PHP. You don't have to introduce static dependencies into your code. Best regards, Stu -- - Kore at Wed, 02 Mar 2011 12:23:29 +0100 @Stu: Yes, that's possible and can be considered something like dependency injection for static calls. But you would need to verify the interface of the passed class name yourself - since there is no way to enforce that by type hints. But yes, that is a way to work around that issue -- but no library discussed does that. - Daniel O'Connor at Wed, 02 Mar 2011 12:28:27 +0100 I've shied away from statics for factory methods: I find most people use them in a way which mixes business logic with object instantiation; and just makes life harder. class A { function bar() { $foo = Foo::factory();// Bad because I can never mock it out. return $foo->doSomething(); } function foo() { $bar = new Bar(); // Bad because I can never mock it out. return $bar->doSomething(); } } Worse; a lot of people fall into the trap of calling the method factory(); you end up with a whole bunch of arguments to configure a class in one particular way. I much prefer something more the the DI container pattern - FooFactory; which knows how to build aTestFoo(); aFoo(); aFooFromConfig(...); etc. The good thing about it is if I find myself injecting these DI containers / factories into business logic layers; I'm probably doing it wrong - and it's instantly obvious. - Stuart Herbert at Wed, 02 Mar 2011 12:32:09 +0100 @Kore I agree that the discussed libraries do things in a way no-one should encourage :) Regarding type-hinting ... seems to work fine when I tested locally. Seems possible to define static methods inside an interface definition, and to use standard class type-hinting when passing the object around (example code below). Best regards, Stu -- = 5.3):