[PHP-DEV] Enums should implement Stringable interface by default

Hi,
I’m writing this email with a reference to https://wiki.php.net/rfc/howto.
My suggestion is that all enums (both basic/pure and backed) should implement Stringable interface by default, and before you say that it’s totally wrong or impossible let me explain why.
Let’s say I have a backed enum:

enum Suit: string {
  case Hearts = 'H';
  case Diamonds = 'D';
  case Clubs = 'C';
  case Spades = 'S';
}

Now if I’m trying something like this "echo Suit::Hearts;"I will get an error “Fatal error: Uncaught Error: Object of class Suit could not be converted to string”
BUT if I’m trying to use this enum as a part DTO (data transfer object) and then serialize it or do something as simple as “echo json_encode(Suit::Hearts);” then suddenly PHP knows how to convert enum to string. This feels so unfair to me that I finally decided to write this email.

The same applies to basic enums which have only case names, but they are still stringable as $enum->name.

Please give me the initial feedback to understand if it’s worth proceeding with a RFC or not.

Best regards,
Janis Kajaks

On 28/02/2025 08:33, Janis Kajaks wrote:

My suggestion is that all enums (both basic/pure and backed) should implement Stringable interface by default,

This has come up at least twice before:

* [RFC] [Under Discussion] Auto-implement Stringable for string backed enums - Externals
* String enums & __toString() - Externals

These threads also touch on some of the same issues:

* Request for RFC Karma - Enum value semantics proposal - Externals
* [Discussion] - Externals

Fundamentally, there are different schools of thought on what an "enum" should be, which lead to different sets of features.

I think some of those differences are fundamental enough that you could only have both if the language had multiple enum-like things which the user could choose between.

--
Rowan Tommins
[IMSoP]

So to summarize the proposed behavior (as I understand it):

* If an enum is backed, it auto-casts to ->value
* If an enum is not backed, it auto-casts to ->name

I believe this has the following problems (at least):

* Unbacked enums are not supposed to be representable as scalar values - if I, as a developer, intended an enum to be representable as a scalar, it would be backed;
* Different behavior depending on whether the enum is backed or not is confusing;
* If an unbacked enum is created, then later a developer wants to change it to backed, it can break code / change output in unexpected, and in ways that are very difficult to search for. You need to, at least, not just search for usages of the enum, but usages of the properties/variables which can be that enum (and repeat recursively for further assignments), then work out which of those might involve auto-casting. (This sort of change probably isn't very common, but that doesn't mean it should be made a nightmare when someone does want to do it)

I don't think enums should ever auto-cast to scalar values (backed or not)

Venturing further down this road starts getting into what I suspect is dangerous territory, with obvious examples being:
* enums as array keys (which I would like to be able to do, but not via this sort of auto-casting)
* comparing different enums with the same backed values (or even different unbacked enums with the same ->name)

And that's without looking at potential conflicts this behavior would bring with planned features such as PHP: rfc:adts

My personal opinion on the current json_encode() behavior with backed enums here (and anywhere else where's there's similar auto-casting behavior) is that it's undesirable and it should be an error. I think it's completely ambiguous as to what the author intended (or if they even intended to at all) when representing a backed enum in JSON.

If you do want this behavior in your application in cases such as json_encode(), it's pretty easy to write a wrapper or helper that casts values as desired. I've done this with database level code (eg. casting bool to 1/0 for MySQL, as a simple example).

On Fri, Feb 28, 2025, at 10:11 AM, Rowan Tommins [IMSoP] wrote:

On 28/02/2025 08:33, Janis Kajaks wrote:

My suggestion is that all enums (both basic/pure and backed) should
implement Stringable interface by default,

This has come up at least twice before:

* [RFC] [Under Discussion] Auto-implement Stringable for string backed enums - Externals
* String enums & __toString() - Externals

These threads also touch on some of the same issues:

* Request for RFC Karma - Enum value semantics proposal - Externals
* [Discussion] - Externals

Fundamentally, there are different schools of thought on what an "enum"
should be, which lead to different sets of features.

I think some of those differences are fundamental enough that you could
only have both if the language had multiple enum-like things which the
user could choose between.

Also see: On the use of enums | PeakD

Not auto-implementing Stringable was a deliberate design decision, for reasons that have not changed.

--Larry Garfield