Arrays as Constant Values

Some things never change. Most programming languages use constants to represent such things. A constant is defined once and cannot change its value during program execution. While there are many valid use cases for constants, they are by default accessible in every scope, and thus even though they are immutable, are global state. The more global constants program code relies on, the harder it becomes to reuse.

Class constants, introduced in PHP 5, are less problematic than global constants, but since before PHP 7.1 they all were publicly visible, it is also very easy to just use them all around the place, and create couplings that may not be desired.

Since PHP 5.3, there have been a few changes with regards to constants, and many of them went unnoticed. We will, therefore, have a look at what has changed since PHP 5.3, to better understand the change that came with PHP 7.

To define a global constant in PHP, we use the built-in define() function:

define('CONSTANT', 'the-value');

This defines the constant at runtime. One might think that it would be better to already define the constant at compile-time (and in fact, from a performance standpoint, it probably would), but since the constant name can be an expression, this is not possible:

define(get_constant_name(), 'the-value');

function get_constant_name()
{
    return 'CONSTANT';
}

PHP cannot evaluate this expression at compile-time because it would need to call a function, which obviously cannot be done before runtime.

PHP 5.3 also introduced compile-time constants. They are defined using const:

const CONSTANT = 'the-value';

Compile-time constants must be defined in the top-level scope. Thus, it is not possible to conditionally define them:

if (!defined('CONSTANT')) {
    const CONSTANT = 'the-value';
}

This is actually pretty obvious: the if statement can only be evaluated at runtime, but the constant needs to be defined already at compile-time? This cannot work, and trying out the above code leads to a syntax error:

PHP Parse error: syntax error,
unexpected 'const' (T_CONST) in ...

The only way to conditionally define a constant is by defining it at runtime:

if (!defined('CONSTANT')) {
    define('CONSTANT', 'the-value');
}

PHP 5.6 already added two nice features with regard to constants. You can now use so-called constant expressions when defining a compile-time constant:

const FOO = 'the' . '-value';

You can only use simple expressions, however. It will not work, for example, to call a function:

const CONSTANT = get_value();

function get_value()
{
    return 'the-value';
}

If you try, PHP will complain:

PHP Fatal error:
Constant expression contains invalid operations in ...

PHP 5.6 also allows you to define an array as the value of a constant, something that was not possible in older versions of PHP. This works for global constants:

const CONSTANT = ['the', 'array'];

This also works for class constants:

class Mapper
{
    const MAP = [
        'a' => 'x',
        'b' => 'y'
    ];

    public function map(string $letter): string
    {
        return self::MAP[$letter];
    }
}

$mapper = new Mapper;
var_dump($mapper->map('a'));

Note that class constants can only be defined at compile-time, not at runtime. After all, it is const, and not define().

There is one missing piece, which was introduced in PHP 7: the runtime definition of global constants, allowing array values.

define ('CONSTANT', ['the', 'array']);

This enables us to use the result of an arbitrary function or method to define a constant. This might come in handy, for instance, when working with configuration values, which by definition should be read-only:

define ('CONFIGURATION', parse_ini_file('config.ini'));

While, in this use case, we would prefer to create a configuration object, being able to define a constant with an array value at runtime is certainly a nice addition to PHP.