Static properties
The implications: Static properties got a bad reputations because, as many other things in software development, people were using them without fully understanding their potential implications. Let’s see some of these implications in PHP, (trying to widen our view in other languages when required) so that we understand what are we talking about.
(a) Static properties can make you loose control in a multi-threading environment.
As you may know, static classes are not objects, so they are not always stored in memory the same way objects do. For example, in C# (and other languages) when a class is loaded, its metadata may be loaded in memory and cached. This is not the case for static properties that are “an important constituent part of the MethodTable data structure” and they are stored in the heap, which means they are accessible by all threads. As a consequence, one thread may change the value of static property that is also used by another thread.
If the static property is read-only then you are quite safe. If not and you insist on using a static property in such an environment, you should take into account some troublesome situations that you may come across. Usually, languages provide you the tools to do so (e.g in C# you can use the volatile keyword or Interlocked) but I won’t go into more details here.
In PHP, this may not be an issue. For example, in pthreads, static variables have a class entry scope. When a new thread is started, statics are copied (removing complex variables, like objects and resources). You can find more here.
Please, keep at the back of your head, that this may not be, and probably isn’t, the case for other languages. For example, JVM shares static variables between threads. C# does the same. So, if you want to consider yourselves as “software developers” and not “PHP developers”, try to widen your viewpoint.
(b) Testability:
Static properties represent global state and this results into coupling between the unit your are testing and the class where the static property belongs. Coupling is the enemy of modularity and testing becomes more difficult because the state of a static property is very difficult to reason within tests. As you may know, tests should be written with as less knowledge of the unit’s implementation as possible. And that’s because what we test is the expected functionality of the unit. Refactoring a unit is an internal process and should not break tests.
(c) Lifetime:
Static properties have a lifetime that matches the entire run-time of the program. This means, even once you’re done using your class, the memory from all those static variables cannot be garbage collected.
(d) Hidden dependencies
If a class A uses static properties of class B, a hidden dependency exists for class A. You cannot tell that class A depends on class B just by looking at the class A interface (the signatures of the public methods).
So, are static properties bad ? No, they are not bad but they can be tricky and create side-effects if not used carefully. And since complexity should never be added to your software without a good reason, I would say: use them frugally. But static properties exist for a reason. And this is sharing information among all class instances. And there are indeed some rare cases where they are really useful. For example, when using feature toggles, when a class caches the results of a process for performance reasons, in profilers, etc.
The viewpoint: The main driving reason for this post was a statement that I read about static properties that was saying: “If a variable can be altered by any instance of a class then the fundamental principle behind encapsulation/information hiding is lost entirely: An object is no longer in complete control of its state.” I don’t agree with this statement and the reason is my viewpoint on static properties. A viewpoint that I would like to share and I think that it may keep you away from troubles.
Personally, I feel that there is not any encapsulation breaking here as far as the static property is used only by instances of the same class (the static property is not public neither public static methods are defined to manage it. Thoughts on the latter case can be found in the next sections). The encapsulation breaking argument is based on the assumption that the static property is part of the object status. But I think this viewpoint is, if not wrong, at least, error-prone. The static property should not be considered, or treated, like being part of the object status. After all, it is not really part of the object’s blueprint. The “get_object_vars” function of PHP will not return any static properties. What is more, you can access it even without an object of that class. Because of this, the object should work fine no matter what is the value of the static property. This is a design principle that stems from my viewpoint.
What is more, I like to consider static properties as part of class’es ecosystem. So, encapsulation is still being retained in a class level. Encapsulation in object-level is quite irrelevant here, because it is like the object is communicating with something external to itself. Having said that, I think it is better to use the static properties directly through object’s methods and not through static methods (the latter should always be stateless). The reasoning can be found a bit later in this article.
Let’s have a look at this simple example:
<?php class Person { private static $database; public function setDatabase($database) { $this->database = $database; } }
The main argument against static properties here is that whenever setDatabase is called on an instance it can potentially break any other existing instance. For me, the problem lies in how the static $database is being used. If the object’s methods assume that a specific database connection has been set up and can be found in the $database variable, then their implementation is faulty (temporal coupling). As I was saying, the static properties should not be considered as part of the object’s status and so the object (and its methods) should not make any assumptions about their values.
So, if an object’s method wants to use the a static property, then one of two things should happen. Either (a) the method should first check if the value of the static property is the expected one, or (b) the method should be able (by design) to work fine no matter what is the value of the static property. If neither (a) nor (b) happens, then the developer is misusing the static property. And this can happen is many things, not only to static properties. If none of these two is possible then defining such a static variable was probably wrong.
I am not sure how can I express this better. Static properties are (and should) sharing information about the class ecosystem and are not part of a common state between all objects. They work one a layer higher than the objects. They are there before any object is created and after all objects have been destroyed. It is like the class it self, beside being a blueprint for objects, it is a “container” for the objects that has its own ecosystem. This ecosystem is created by defining static properties and static methods and, of course, the ecosystem is accessible by all the inhabitants of the container. The inhabitants can influence the ecosystem (change the value of a static property) but cannot have exclusive control of it (they cannot be sure of the value carried by a static property at any moment).
(stateless) Static Methods
The fact that we are using OOP to build our application does not mean that functions are forbidden. Stateless function are, in fact, always there (e.g as helper functions). Static functions allows us to group them based on their functionality. Static methods are nothing more than namespaced functions. So, it is good to keep them in classes that are not meant for instantiation. Classes that contain only stateless static methods. This keeps your code organization simpler and is more compatible with the Single Responsibility Principle. The main implication here is:
Testability
Once upon a time, static methods could not be mocked. But things have changed (see: https://blog.gougousis.net/mocking-static-methods-with-mockery// ). Nonetheless, it’s harder and increases your application’s complexity.
(non stateless) Static Methods
This is a strange situation. It is not functional. since it has state. But it is also not OOP since we don’t have objects. Their existence is strange even with the viewpoint that I described in the previous paragraphs because we allow a class B to modify the ecosystem where instances of class A live. It’s getting a mess, it makes you doubt about the cohesion of these classes and, generally, I cannot see a good reason for that.