Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are constants as evil as global variables and singletons?

I've heard many times on this forum that using global variable is a dead sin and implementing singleton is a crime.

It just came to my mind that old good constants bear all the features of these dishonored practices: they are globally accessed and no doubt they introduce globallest state ever.

So, the question is: shouldn't we declare a jihad to constants too, and use all the modern things like DI, IoC or other stylish words all the way instead?

like image 412
Your Common Sense Avatar asked Nov 05 '13 11:11

Your Common Sense


1 Answers

Generally speaking yes, avoid constants. They introduce coupling from the consumers to the global scope. That is, the consumers rely on something outside. This is unobvious, e.g.

class Foo
{
    public function doSomething()
    {
        if (ENV === ENV_DEV) {
            // do something this way
        } else {
            // do something that way
        }
    }
}

Without knowing the internals of doSomething, you will not know there is a dependency on the global scope having that constant. So in addition of making your code somewhat harder to understand, you are also limiting how it can be reused.

The above is also true for constants that have only one value, e.g.

public function log($message)
{
    fwrite(LOGFILE, $message);
}

Here the constant would point to a file resource defined somewhere outside as

define('LOGFILE', fopen('/path/to/logfile'));

And this is just as unobvious as using ENV. It's a dependency that requires something outside the class to exist. And I have to know that in order to work with that object. Since the class using this constant hides this detail, I might try to log something without making sure the constant exists and then I'd wonder why it doesn't work. It doesn't even have to be a resource, LOGFILE could simply contain the path as a string. Same result.

Relying on global constants in your consumers will also require you to setup global state in your unit-tests. This is something you generally want to avoid, even if the constants are fixed value, because the point of the unit-test is to test the unit in isolation and having to put the environment into a certain state hinders this.

Moreover, using global constants always poses the threat of constants of different libraries clashing. As a rule of thumb, don't put anything into the global scope. Use namespaces to cluster constants if you have to use them.

However, note that namespaced constants still have the same issues regarding coupling, so do class constants. As long as this coupling is within the same namespace it's less critical, but once you start to couple to constants from various namespaces you are again hampering reuse. For that matter, consider any constants public API.

An alternative to using constants would be to use immutable Value Objects, for instance:

class Environment
{
    private $value;

    public function __construct($value)
    {
        $this->assertValueIsAllowedValue($value);
        $this->value = $value;
    }

    public function getValue() {
// …

This way, you can pass around these values to the objects that need them, in addition to making sure the values are valid. Like always, YMMV. This is just an option. A single constant will not make your code unusable, but relying largely on constants will have a detrimental effect, so as a rule of thumb, try to keep them down to a minimum.

On a related side note, you might also be interested in:

  • Pros and Cons of Interface constants and
  • PHP global in functions
like image 159
Gordon Avatar answered Oct 12 '22 21:10

Gordon