
Request #48277 need a way to do type safe enumerations in php
Submitted: 2009-05-14 08:38 UTC Modified: 2015-04-11 06:38 UTC
Avg. Score:3.9 ± 1.2
Reproduced:9 of 9 (100.0%)
Same Version:6 (66.7%)
Same OS:5 (55.6%)
From: nairbv at yahoo dot com Assigned: levim (profile)
Status: Assigned Package: *General Issues
PHP Version: Any OS: Irrelevant
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2009-05-14 08:38 UTC] nairbv at yahoo dot com
**This is a feature request, not a bug

I can find no reasonable way of implementing any kind of useful typesafe enumeration pattern in php without an additional language feature.  There are a number of potential language features that could fix this.  Here are a couple of examples.

1) a "final" keyword for static class members, and permitting static initialisation at class load time.  Final is *not* the same thing as "const."  "final" + load-time initialisers would allow code like this:

final class color {
    private $name;
    public static final $RED = new boolean('Red');
    public static final $ORANGE = new boolean('Orange');
    function __construct($name){ $this->name=$name; }
    function __toString() { return $this->name; }

without static, you can't initialise them in the class.  Without final, any user of your class can color::RED = color::ORANGE (maybe accidentally in an if statement) and you're class is hosed.  With const, they can only be primitives, which can't have any functionality or attributes.  Finals would be initialised at run time (sometimes even in constructors etc, as long as they are only initialised once).

Don't confuse what I said about static initialisers with "late static binding," an already planned feature un-related to this request.

2) or, including an actual enum class would be nice.  see: for an example.

3) ideally we'd have both.  Having an enum class is obviously better than my first suggestion for the specific problem of implementing enums, but the features in my first suggestion also have other applications.

I'm seeing a lot of code that in various ways attempts to work around this lack.  None of them are elegant, in that they all involve repetitive, hard to read, or unsafe code.

one example I came across recently looked like this:
final class foo {
    static $foo;
    static $bar;

    function __construct()
        $foo = new someotherclass('attribute1','attribute2');
        $bar = new someotherclass('attribute3','attribute4');
new foo();

there was a comment before "new foo();" in this code (written by someone else) that said:

"need to instantiate class to fill static members. I don't like this setup, but haven't found a better way yet to have const members of class or interface be instantiated objects rather than constant expressions."

It happens frequently enough.  In this code that I found, an accidental assignment in an if statement breaks other calling code due to lack of final.  Lack of load-time static initialisation means that class members have to be instantiated by a call outside the class definition, and that every member (the real class has about 100 members) has to be defined twice.

I've also seen code like:

final class my_things {
    private $name;
    private static $all_things = array();
    function __construct($name,$otherstuff) {
        self::$all_things[$name] = $this;
    static function get_by_name($name) { 
        return self::$all_things[$name]; 

new my_things('thing1',...);
new my_things('thing2',...);

which, is also far less than ideal... and doesn't maintain any usable reference to the actual enumeration.  Each one must be looked up by it's reference string, often defined elsewhere as a series of consts.  It can also be added to since the constructor must be public.

The closest you can come to getting a real enum in php is to use public static final functions to get the enumerated values.  This requires a lot of plumbing code, i.e., a factory method that caches values so new ones are not constructed on every call (because we want to boolean::TRUE() === boolean::TRUE() not just ==, etc), and an entire function definition in which each enumerated value is instantiated/returned.

The late static binding feature being added in 5.3 might enable someone to write an enum superclass (function based) getting around *some* of this plumbing code, but using functions as enumerations would still be an ugly hack.

There are so many places where this is useful.  Say I want to add a list of log levels to a logging class.  I want each log level to be a numeric value that can be compared, and have a string representation that can be printed.  It can't be done in any safe, non-repetitive, object oriented, clean way.

Reproduce code:

Expected result:

Actual result:


Add a Patch

Pull Requests

Add a Pull Request


AllCommentsChangesGit/SVN commitsRelated reports
 [2013-06-23 08:33 UTC] knight at kopernet dot org
I hope one day we get rid of the final keyword altogether and instead of a fatal error it generates we just get a decent warning in the log file.
I'd be far more happy and feel safer as well if the PHP team considered using uninitialized variable a bigger issue.
The same goes for type hints which just make the code so much more rigid and make it so difficult writing unittest for example. Why must it throw a fatal instead of a warning?
 [2014-04-30 22:53 UTC]
-Package: Feature/Change Request +Package: *General Issues -Operating System: +Operating System: Irrelevant -PHP Version: 6CVS-2009-05-14 (CVS) +PHP Version: Any
 [2015-04-11 06:38 UTC]
-Assigned To: +Assigned To: levim
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Mon Sep 16 20:01:28 2019 UTC