I'm trying to include a constant in a Enum value but I get the fatal error Enum case value must be compile-time evaluatable
(here's the code) :
const PARENT = 'parent';
enum MyEnum:string
{
case firstChild = PARENT . '_child1';
case secondChild = PARENT . '_child2';
}
I don't understand why I get this error, because the value of a constant can't change. I have already Google the error but I have found nothing. Any idea?
The distinction between "compile-time" and "run-time" in PHP is not always obvious, because we don't explicitly "build" a PHP project like we would with a language like C or Java. The rules of what happens when are also subtle, and governed partly by historical features of the language.
In this case, it looks like the constant PARENT
would be available when the definition of MyEnum
was being processed; but that's not quite how it works.
If we do the same thing with a normal class, we can try to see this in action:
class Foo {
const EXAMPLE = OTHER_CONST;
}
var_dump(class_exists('Foo'));
echo Foo::EXAMPLE;
If you run that code on its own, you will see that the class is compiled without the constant it references actually being available, and the class_exists
call succeeds. Only when you try to actually access Foo::EXAMPLE
does PHP try to evaluate the global constant it refers to, and gives an error (or, in older versions, a Notice or Warning).
That allows things to load "in the wrong order" - either of these will succeed, even though one defines the constant "before" the class, and one "after" it:
define('OTHER_CONST', 'some value');
class Foo {
const EXAMPLE = OTHER_CONST;
}
var_dump(class_exists('Foo'));
echo Foo::EXAMPLE;
or:
class Foo {
const EXAMPLE = OTHER_CONST;
}
var_dump(class_exists('Foo'));
define('OTHER_CONST', 'some value');
echo Foo::EXAMPLE;
I've used define
here to make it more obvious that this is run-time code; it could equally be a const
line in a different PHP file being included, for instance.
The key here is that even if the constant definition appears before the class definition, it's not actually evaluated when the class definition is being compiled. It is "evaluated at run-time" when the class constant is first referenced.
Coming back to your enum example, the error makes a bit more sense: although the constant definition happens to appear first in the file, uses of it are not "evaluated at compile time". Unlike class constants, the values of backed enum cases are always calculated while the enum is being compiled, so can't contain references to things that won't exist until run-time, which unfortunately includes global constants.
However, a small modification does work:
class Something
{
const PARENT = 'parent';
}
enum MyEnum:string
{
case firstChild = Something::PARENT . '_child1';
case secondChild = Something::PARENT . '_child2';
}
This works because in this case the class constant itself can be evaluated at compile-time, and will then be available when the enum definition is compiled.
To bring things full-circle, this will not work:
const GRAND_PARENT = 'parent';
class Something
{
const PARENT = GRAND_PARENT;
}
enum MyEnum:string
{
case firstChild = Something::PARENT . '_child1';
case secondChild = Something::PARENT . '_child2';
}
var_dump(class_exists('MyEnum'));
echo MyEnum::firstChild->value;
Here, the global constant is considered a run-time value, so the class constant is deferred until run-time; when the enum is compiled, it tries to fetch the class constant's value but can't, so gives an error.