On Wed, Jul 9, 2025 at 3:33 PM Deleu <deleu...@gmail.com> wrote: > Hi people! > > Today I tried to do `ResourceType::from()` and was surprised that my IDE > immediately screamed at me. After further investigation, I realized that > Basic Enums (this is what the RFC called it [1]) does not have these > commodities backed into them. > > I did some digging and I found 2 main discussions: Stringable by default > [2][3] and backed-in commodities [4]. I think Benjamin, Derick and Nicolas > have argued why Stringable by default may not be desirable and I only > mention this for completeness of the conversation. I don't want to talk > about that at all. Instead, I want to focus on the other side: construct > from string by default (from/tryFrom()). > > I also read Larry's post that has been shared throughout these discussions > [5] and it seems fair and sound to use guardrails to discourage unintended > use. This gets into the territory of "let me do what I want, I know what > I'm doing" vs "let me limit what you can do because I built it and I know > exactly what it was intended for", which I also want to try and steer away > from debating, each camp has its merits. > > Bilge said: > > My question, then, is why can't basic enumerations have these semantics by > default? Or, to state it more concretely, what would be the downside to > having all "basic" enumerations actually being "backed" enumerations whose > values implicitly mirror their names for the purposes of converting to/from > strings? Would this not make basic enumeration more useful without any > particular downsides? > > > While I'm searching for similar clarification, I want to pose the question > differently. I feel like the answer to his question is in Larry's article > about discouraging fancy strings. My question boils down purely to: *can > Basic Enums implement ::from() / tryFrom() methods?* > > Larry said: > > [...] In your case, you want to "upcast" a string to an enum. That means > you're doing some sort of deserialization, presumably. In that case, a > backed enum is what you want. *A unit enum isn't serializable, by design.* > > > Although this starts to thread into very pedantic territory, I think, in > fact, a *unit enum* (assuming it means Basic enum) is in fact always > serializable to a string: `$enum->value`. By design, the value is a string > and it cannot have duplicate values in a single enum. It means it's > extremely easy to define an Enum in PHP and at some point store it in a > storage engine / database in the form of `$enum->value`. Now if I want to > get back my Enum at a later stage I need to implement exactly the same code > that already exists in the PHP engine behind `Enum::from()`. Maybe it's not > serializable in the sense that it doesn't implement any true serialization > mechanism, which I'm guessing a backed-enum probably does, but I'm trying > to come from the very practical application of creating a Basic Enum at an > HTTP context (which is the bread and butter of PHP) and then recovering > said Enum in a background worker context without having to use PHP > `serialize()` function and store PHP-specific dialect in a database that is > used by multiple teams and programming languages. > > I also take the point that it is easy to argue against all this: just put > `: string` on your Enum and duplicate the names with values. Still, this > doesn't address the "surprise effect" of "why this Enum doesn't have > ::from() in it?". There doesn't exist any other value (string or otherwise) > that could be used in ::from() or ::tryFrom() in a Basic Enum, which could > make it less contentious. Also, in the spirit of NOT making Enums "Fancy > strings", I'm looking for ways to reconstruct my Enum and all the behaviors > available inside of it without even having to associate or think about a > string. The only reason a string comes into the discussion is because > $enum->value is one and is stored. I also checked and: > > enum Foo { > case 1; > case 2; > } > > is a parse error. [6]. > > Larry has also suggested that instead of making Basic Enum implement > `::from()` and `::tryFrom()` we could instead offer auto-populated > String-Backed Enum values. That means transforming this: > > ``` > enum Foo: string { > case Bar; > case Baz; > } > ``` > > (which is a Fatal Error today) into this: > > enum Foo: string { > case Bar = 'Bar'; > case Baz = 'Baz'; > } > > I also like this proposal. Although semantically, I think it would be > better / have been better to have Basic Enum implementing the ::from() > methods, one could argue that adding it now could be a breaking change > since people could have Basic Enum already implementing their own custom > ::from() method. > > In conclusion, the "complex" (as opposed to primitive) object Enum is not > a Fancy String and where I'm coming from I believe to be in sync with that > mindset. However, PHP is highly used in Web context where we may need to > use asynchronous processes to make API calls fast and schedule executions > at a different context which often involves serialization. As such, being > able to reconstruct a Basic Enum seems a rather fundamental need that we > can still make it viable by making a small `: string` change to the Enum > and opting-in into the from / tryFrom utilities. This should not affect > Int-Backed Enums at all. > > Where casing is concerned (camelCase, PascalCase, snake-case, etc) [7], > one can argue that if you want to have full control over casing, you should > definitely take control over the values of your Enum. The beauty (in my > mind) about making it default to the enum name is that it doesn't matter if > I follow PER-CS rules or not, the truth is I don't need to think about > strings at all because my Enum is not a Fancy string. > > I didn't intend to write such a long email, but I'm really keen on hearing > arguments against everything I shared to see if there are any flaws in my > thought process. > > [1] https://wiki.php.net/rfc/enumerations#basic_enumerations > [2] https://externals.io/message/118040 > [3] https://externals.io/message/124991 > [4] https://externals.io/message/123388 > [5] https://peakd.com/hive-168588/@crell/on-the-use-of-enums > [6] https://3v4l.org/cDISV#v8.4.10 > [7] https://externals.io/message/123388#123394 > > -- > Marco Deleu >
It's important to keep in mind that when non-backed enums have a value by default, it will be used as such. When inevitably the assumption is made that a value can be used because it exists, and then the enum name is changed, the "from" will break as well. If I rename a case and someone else is using this in a database and neither of us consider the name to be changed, we're now stuck with datacorruption and broken code. I prefer this to remain separate as it is.