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:
Why would I do that?
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
Fields with bold names are mandatory.
sokzzuka at Wed, 02 Mar 2011 11:52:48 +0100
Very valid points!
Link to commentUnfortunately, 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,
Link to commentInteresting 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.
Link to commentDaniel 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.
Link to commentclass 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 :)
Link to commentRegarding 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
--
<?php
interface TestClass
{
static public function testMe();
}
class TestClass1 implements TestClass
{
static public function testMe()
{
echo __METHOD__ . "\n";
}
}
class TestClass2 implements TestClass
{
static public function testMe()
{
echo __METHOD__ . "\n";
}
}
class TestClass3
{
static public function testMe()
{
echo __METHOD__ . "\n";
}
}
$test1 = new TestClass1;
$test2 = new TestClass2;
$test3 = new TestClass3;
function testMe(TestClass $testClass)
{
$testClass::testMe();
}
testMe($test1);
testMe($test2);
// next one should cause a fault
testMe($test3);
Kore at Wed, 02 Mar 2011 12:42:40 +0100
@Stuart Herbert:
Link to commentI agree that this is a valid way to do that. Did not now that you can call static methods on object instances this way. But a static API still encourages static coupling of code. Where is the point to use that kind of API instead of just issuing dynamic calls on the passed objects?
I understand that "static" can be considered as a hint that a method has no side affects on the object - but I consider the possible impact on developers misusing the API and introducing static coupling in their code bad enough to omit this additional information.
An additional remark: In PHP you can also always call static methods dynamically without any notice. So can always also declare methods static and still use them dynamically. But users of your API will still be misled.
Stuart Herbert at Wed, 02 Mar 2011 13:04:01 +0100
@Kore:
Link to commentI agree that the risk is real. On the flip side, static means that there's no $this inside the method to be abused by the class's author (or, more likely, subsequent maintainer), something that is often overlooked at initial design time.
Best regards,
Stu
DampeS8N at Wed, 02 Mar 2011 13:21:12 +0100
Singleton isn't an abuse of static methods.
Link to commentI've only ever seen the point of static -anything- in the case of singleton and the factories you already mentioned. While singleton is an antipattern, you're talking about a single static method on the very same object that will make use of that method. Which is identical to your case for factories.
So Singleton won't create static dependencies either. And while singleton is almost always used incorrectly, it is unfair to call it out as an abuse of static all on its own.
Sebs at Wed, 02 Mar 2011 13:26:42 +0100
Teaching by lengthy examples is what a consultant would do, but not what a coder will read until the end.
Link to commentEspecially when the article is flooded with NO, DONT, DO NOT and NOT RIGHT. Those NEGATIVE MESSAGES will not motivate anyone to change behaviour. Instead people will start insisting that they are RIGHT.
Turn that negative stance into a positive one and split the article in 4 parts, the feedback will be better, the message will be received.
Ade at Wed, 02 Mar 2011 14:21:29 +0100
I really enjoyed this article. Demonstrating specific examples from widely used frameworks was neat.
Link to commentAs you state, problems with statics become very apparent when trying to unit test. This is most infuriating when the method in question may call a series of static methods in turn calling another series of static methods and so on.
kwasseem at Wed, 02 Mar 2011 14:55:22 +0100
Hi Kore,
Link to commentyou brought up some interesting points to think upon. I agree with the inter-dependence of the Zend Stack, it is not so much loosely-couple as it claims to be. (had the frustration recently)
Else, I would not say 'static methods' are evil.; i think this is a bit too harsh. Static methods do have their uses. Instead I will say use 'static' methods wisely and sparingly. Static factory or helper methods are there for a reason, for direct access of generic method - you don't want to instantiate a whole object just to use it only for a per functionality basis - so such a class-oriented way.
I like your way of defining the object oriented programming and class oriented programming.
Good to come across your blog,
Cheers!
//K. Wasseem
Karsten at Wed, 02 Mar 2011 15:28:46 +0100
@Sebs: Yes, that is one of the pressing problems of today. Everyone is just too damn lazy to read something. Poor society...
Link to commentJared Williams at Wed, 02 Mar 2011 15:59:49 +0100
Totally agree, public static functions are evil. And there is no real reason to use them.
Link to commentThe PHP streams should have never required a class name, but an instance.
I'd also go a step further and also say extends is evil in component/framework libraries. For an example, http://marc.info/?l=php-internals&m=126844505831948&w=2 , someone discovering that Zend Form's components are as flexible or adaptable as they should be.
It will be interesting when the current 5.3.99 goes live, with trait support, as then it I believe the better frameworks would provide a collection of traits which then the developer can mix and match to build the desired behaviour.
-J
Weltraumschaf at Fri, 04 Mar 2011 13:24:51 +0100
I miss one important thing (known by evey functional programmer):
Link to commentYou should only use a static method/function when you assure that on the same input values the same output value will be computed and returned (idempotent).
One big fail and antipattern is s.th. like: AClass::isLoggedIn()
That also counts for factory methods: A factory method must allways return the same output on the same input values. Elsewise you have introducec a global state in your function.
Giorgio Sironi at Sat, 05 Mar 2011 13:42:08 +0100
All valid points, really. Thanks for analyzing the frameworks approach, I was only familiar with Zend Framework 1.
Link to commentFactory Methods for Value Objects is pretty much the only use I pursue for static methods; in that case, I'll never change the implementation.
About utility classes, I found that they are often the symptom of a missing object which contain the data they operate on; however this is not an object which should be injected in the client code of these utility classes. It's more likely to correspond to a primitive value passed around.
Consider for example transforming underscore_strings to camelCaseOnes for identifying controllers: a CamelCaseUtil class could be replaced by an object ControllerName which wraps the string itself.
Professor Nunzabar at Thu, 21 Apr 2011 13:20:47 +0200
I really enjoyed the article. It would be nice to see a "corrected" version of the MyWhatEver class where I assume you would inject a FileTool object.
Link to commentIouri at Tue, 17 May 2011 09:30:31 +0200
I use static methods as a way to namespace methods.
Link to commentBut I don't use static state, it is evil. Use static as proper function i.e they should not modify anything.
Gucci pas cher femme at Tue, 29 Nov 2011 07:20:57 +0100
jour surpasser son idole et de gagner son Championnat WWE quatrième femme en moins de trois years.Yet, le recul, qui est vraiment surpris
Link to commentNicolò Martini at Wed, 07 Dec 2011 10:43:03 +0100
The problem with a static method is not in the static method in itself, but in its use.
Link to commentThe main point is that if we call a static method of a class in the form
ClassName::method_name()
we hard code the name of that class in that method.
But that is not too much different from instantiating an object directly in the method, with a line like
$object = new ClassName();
In that case we have hard-coded the ClassName in the method code too (in this case we also have hard-coded the arguments passed to the constructor).
To avoid to introduce static dependencies without the needing of instantiating an object as Stuart suggested, we can inject the class name in the constructor, in a setter or in the method arguments (PHP >= 5.3):
<?php
class foo{
public static function bar(){ echo 'hello';}
}
class bar{
/** @param string $className; */
public function f($className){
$className::bar();
}
}
It remains the problem of type hinting: the argument $className is a string, and we have to manually check if the corresponding class implements some interface or extends some base class. And that is boring and not very clean. For example we can do that in this way:
<?php
class bar{
/** @param string $className; */
public function f($className){
if($className != 'foo' && !is_subclass_of($className, 'foo')
throw InvalidArgumentException("$className must be of type foo");
$className::bar();
}
}
Regards,
Nicolò
Student at Wed, 25 Apr 2012 22:41:19 +0200
@Sebs: Yes, that is one of the pressing problems of today. Everyone is just too damn lazy to read something. Poor society...
Link to commentYour so wrong and actually quite offensive in your poor observation, the problem is the contradiction, as a student what chance do I have when the poeple I look upto can't even make their minds up as to what is acceptable??? This very article contradicts many others, then in the comments contradicts itself!!! Another problem is when experienced programmers talk about things like code seperation and dependencies, in this example as has already been pointed out
new someClass();
SomeClass::someMethod();
both are hard coded and create dependencies, this very article implies that ther should be no hard coded dependencies? How is that possible? When experienced programmers talk about code seperation in web development particularly they imply there should be no code mix whatsoever, again how is that possible?
There are many more issues, for instance the overuse of industry jargon as an alternative to plain english in discussions like this. Experienced programmers saying X is evil/bad/poor practice/insert another negative comment then offering no explanation or justification as to why that is the case, or masking a weak argument with meaningless jargon.
Bob Foster at Thu, 03 May 2012 04:28:13 +0200
You went wrong with "assuming we want to do object oriented software development". Object oriented programming (OOP) isn't a religion, it's an approach that suits some problem domains, not so much for others.
Link to commentIn sympathy with the sobbing Student, I'll go on to make a brief but I hope cogent argument why OOP isn't the omega of programming.
Even in modeling, the world isn't comprised of objects with a fixed repertoire of skills. Just as in English, there are verbs. Verbs sometimes transform objects - change their nature. Every dependency graph need not have the ability to transform itself into some linear sequence that is one of the possible partial orders of the graph, far less the many possible partial orders, e.g., the parallel decomposition of the graph. This is the job of verbs, and of objects that expose enough of themselves to be transformable.
The object as a repository of state it alone can modify in response to the slings and arrows of outrageous method calls - the essence of information hiding - is an unsuitable tool for concurrent programming, in which state changes must be carefully controlled. This is the proper domain of functional programming, in which state never changes.
Zach at Thu, 24 May 2012 17:22:29 +0200
THANK YOU!
Link to commentMisuse of static methods by PHP programmers is a pet peeve of mine. There's a place for static methods, but you know what? 99% of the time when I see them used in PHP they're used incorrectly. I see crap like classes that keep state in themselves through static methods and modifying internal static state information ALL THE TIME!
Also I'd add there's more to the world than just objects. Sometimes a static method is the right thing to use... It's just that I see it misused more than I see it used correctly. The two big examples that come to mind are:
1. The factory pattern and its kissing cousin, the singleton pattern (A lot of people confuse just using static methods and effecting class-level state with the singleton pattern, but this is wrong).
2. Methods that are truly stateless and do not impact the internal state of the class. I'm of the opinion that anything with state should be an instance. You see this a lot less in PHP because PHP has a concept of functions outside of classes that don't necessitate util classes, but classes give you limited scoping of static variables that should otherwise not pollute your global namespace.
Tom B at Mon, 13 Aug 2012 14:21:17 +0200
I was right with you until you mentioned factories as a valid exception. They really aren't. They have the exact same issues as any static method (or indeed, the new keyword).
Link to commentIf you're doing TDD then you should notice this immediately. Calling Factory::create() or similar is no different than any other static method and has all the same pitfalls.
jachu at Wed, 07 Nov 2012 10:37:53 +0100
Thanks, your entry straightened my knowledge. What you suggest instead of static methods? How should I do something like FileTool::recursiveDelete('/') ?
Link to commentEvery framework which I used have a lot of static methods including yii, zend, kohana etc
Mjason3x at Fri, 08 Feb 2013 19:14:20 +0100
Yep. i also would like to know from you the best way to implement the FileTool::recursiveDelete() you mentioned about. You only gave examples of how those methods in those frameworks may affect on the long run but never gave an example of how they can be better implemented. I would have appreciated this article more if it was so.
Link to comment