Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127975 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id CA6FC1A00BC for ; Wed, 9 Jul 2025 16:13:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752077495; bh=Nnn+HdFQaFYQOoeINuCrPQ9zpCNn8Wp31IZu3AawYzE=; h=Date:From:To:In-Reply-To:References:Subject:From; b=mj6hdzaWDmFmjglcC5jRFzTkTmG4qLzWzF9RIlHtU3e5Pgvbt9iQpvlXap8PLQorR w106NifT04XR2cQiq6NZD4JM1mOP0OdNXSsjZYnPCvOKyWDAdWxKDCagPofBRCBj5u 0ZnS0n61K/fVlvKG6MhV7RA22UjVkugAdlAISvRErOPDkXP3XaeGS5wvTmMaAnu030 rb85pigQzcyn/HJ2mMbW0Bxa+bTRB1RFk58iyWXMvvxrM7YFCM4iFMzdGpX3797uYZ jZzqC2W+KYPy/EZbDE8ixh+hx3EyHPJfSjBppb/oaka97IFB+rh3lHPxIoeWirBENX T7UgkDDG26FmA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 1E4A31805A2 for ; Wed, 9 Jul 2025 16:11:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_05,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fhigh-a3-smtp.messagingengine.com (fhigh-a3-smtp.messagingengine.com [103.168.172.154]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 9 Jul 2025 16:11:23 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfhigh.phl.internal (Postfix) with ESMTP id 5451E140033A for ; Wed, 9 Jul 2025 12:13:13 -0400 (EDT) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-05.internal (MEProxy); Wed, 09 Jul 2025 12:13:13 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm3; t=1752077593; x=1752163993; bh=MYVFdrrR6P EVzwxrMuAtN9nQGxUa0/DPImTxY+6qJhI=; b=C1g56Ey2Q8ff/btA13g8NgbpMT 2LIOKKSglUlUte0OP3dlHNx1yBurIvT4TqyF6WyEPQ/A1gQTDNGeAhPI2pV2WPC7 IWOqQo9mZhRdH6/CPKJa7xA/Xdq1nj2x7pD+9BjX56IJelgIAtmKDstuxtbcWOW5 Nb+rbB9PrbsYpit4qjnaHikvYraLWpkKn/RYh+FDbJKLX/JE8ktcFyXdpR6s87yK sI+tJaKz7FlM/iU84bjsdCG77Ck1uPZTJpQxojjZn9fuXPLPkSdRSRtrASizUGuO 08gO8O94TmS2ByOqQ8rhQfzeSgOoGwp/F69jqMeDfAYZgglOGFfXInUBZ/jA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t= 1752077593; x=1752163993; bh=MYVFdrrR6PEVzwxrMuAtN9nQGxUa0/DPImT xY+6qJhI=; b=KMMJjqQ+sqUGs8LAss/wXvUPeJLiHRIKTsMSqoiVmNK2rlEgFGV H/c7wCyb0B7Hv55paN2V/VXGKSfJOVTAz0W4CrSJYjLviWMQDBiP48BJ9evoe0HP rKPcnEJyvzUDRZcGfYiopilKqlq2ijJmuyAtQ2dhhqs8Rfyu/PbEBO279HeKz8XO l0qouNmNQjMH+h48DlzKaW5Rd9ZAUvsIedsNM6tPuZt3Jny/uWyC8tq+HWrviAQI 5IF+cqa5sTSdq5QMJy70AnrlAoPJK/JWRt7gxqS5W7F15+iclAePyhw27tfJKMu5 t1JzaYLS/KsXKy0RNZjgyEx8Oxsf7hpRRYQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdefkedtudcutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecunecujfgurhepofggfffhvffkjghfufgtsegrtderreertd ejnecuhfhrohhmpedftfhosgcunfgrnhguvghrshdfuceorhhosgessghothhtlhgvugdr tghouggvsheqnecuggftrfgrthhtvghrnhepvdehudeiudejvdduudevlefghffggedtfe dtueeviefghfdtuddvvdelteefleejnecuffhomhgrihhnpehphhhprdhnvghtpdgvgiht vghrnhgrlhhsrdhiohdpphgvrghkugdrtghomhdpfehvgehlrdhorhhgnecuvehluhhsth gvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprhhosgessghothhtlhgv ugdrtghouggvshdpnhgspghrtghpthhtohepuddpmhhouggvpehsmhhtphhouhhtpdhrtg hpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id E72EF182007A; Wed, 9 Jul 2025 12:13:12 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: T9af0d159d321c39b Date: Wed, 09 Jul 2025 18:12:52 +0200 To: internals@lists.php.net Message-ID: <5a38b818-8366-4edc-93ab-a147e3a9aec3@app.fastmail.com> In-Reply-To: References: Subject: Re: [PHP-DEV] ::from() and tryFrom() on Basic Enums Content-Type: multipart/alternative; boundary=429a47eae8fb49848cf063022063a645 From: rob@bottled.codes ("Rob Landers") --429a47eae8fb49848cf063022063a645 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Wed, Jul 9, 2025, at 15:29, Deleu wrote: > Hi people! >=20 > Today I tried to do `ResourceType::from()` and was surprised that my I= DE immediately screamed at me. After further investigation, I realized t= hat Basic Enums (this is what the RFC called it [1]) does not have these= commodities backed into them. >=20 > I did some digging and I found 2 main discussions: Stringable by defau= lt [2][3] and backed-in commodities [4]. I think Benjamin, Derick and Ni= colas have argued why Stringable by default may not be desirable and I o= nly mention this for completeness of the conversation. I don't want to t= alk about that at all. Instead, I want to focus on the other side: const= ruct from string by default (from/tryFrom()). >=20 > I also read Larry's post that has been shared throughout these discuss= ions [5] and it seems fair and sound to use guardrails to discourage uni= ntended use. This gets into the territory of "let me do what I want, I k= now 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 a= nd steer away from debating, each camp has its merits. >=20 > Bilge said: >=20 >> My question, then, is why can't basic enumerations have these semanti= cs by default? Or, to state it more concretely, what would be the downsi= de to having all "basic" enumerations actually being "backed" enumeratio= ns whose values implicitly mirror their names for the purposes of conver= ting to/from strings? Would this not make basic enumeration more useful = without any particular downsides? >=20 > While I'm searching for similar clarification, I want to pose the ques= tion differently. I feel like the answer to his question is in Larry's a= rticle about discouraging fancy strings. My question boils down purely t= o: *can Basic Enums implement ::from() / tryFrom() methods?* > ** > Larry said: >=20 >> [...] In your case, you want to "upcast" a string to an enum. That me= ans 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 des= ign.* >=20 > 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 stri= ng and it cannot have duplicate values in a single enum. It means it's e= xtremely easy to define an Enum in PHP and at some point store it in a s= torage 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 se= rialization mechanism, which I'm guessing a backed-enum probably does, b= ut 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 d= atabase that is used by multiple teams and programming languages. >=20 > 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 hav= e ::from() in it?". There doesn't exist any other value (string or other= wise) that could be used in ::from() or ::tryFrom() in a Basic Enum, whi= ch could make it less contentious. Also, in the spirit of NOT making Enu= ms "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 discussio= n is because $enum->value is one and is stored. I also checked and: >=20 > enum Foo { > case 1; > case 2; > } >=20 > is a parse error. [6]. >=20 > 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: >=20 > ``` > enum Foo: string { > case Bar; > case Baz; > } > ``` > (which is a Fatal Error today) into this: >=20 > enum Foo: string { > case Bar =3D 'Bar'; > case Baz =3D 'Baz'; > } >=20 > I also like this proposal. Although semantically, I think it would be = better / have been better to have Basic Enum implementing the ::from() m= ethods, one could argue that adding it now could be a breaking change si= nce people could have Basic Enum already implementing their own custom := :from() method. >=20 > 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 wit= h 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 e= xecutions 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` chang= e to the Enum and opting-in into the from / tryFrom utilities. This shou= ld not affect Int-Backed Enums at all.=20 >=20 > Where casing is concerned (camelCase, PascalCase, snake-case, etc) [7]= , one can argue that if you want to have full control over casing, you s= hould 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 thi= nk about strings at all because my Enum is not a Fancy string. >=20 > I didn't intend to write such a long email, but I'm really keen on hea= ring arguments against everything I shared to see if there are any flaws= in my thought process. >=20 > [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 >=20 > -- > Marco Deleu Hey Marco, I think this relies on whether the =E2=80=9Cnatural value=E2=80=9D of a = unit enum is a string or not. It might be, or it might not be. I don=E2=80= =99t think you have a compelling argument on why it is a string and that= string is the name. Personally, I used int-backed enumerations far more= so I would argue that the natural value is an integer, not a string. I=E2= =80=99ve been thinking of a =E2=80=9Cquality-of-life RFC=E2=80=9D for ob= vious enums, for some time now. Basically, backed enums without a value = receive the =E2=80=9Cobvious=E2=80=9D value. So a string backed enum get= s the name, while an int gets the ordered number.=20 I think something like that makes more sense than trying to decide what = the =E2=80=9Cnatural value=E2=80=9D of a unit enum is.=20 =E2=80=94 Rob --429a47eae8fb49848cf063022063a645 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Wed, Jul 9, 2025, at 15:29, Deleu wrote:
Hi peopl= e!

Today I tried to do `ResourceType::from()` a= nd was surprised that my IDE immediately screamed at me. After further i= nvestigation, I realized that Basic Enums (this is what the RFC called i= t [1]) does not have these commodities backed into them.

<= /div>
I did some digging and I found 2 main discussions: Stringable = by default [2][3] and backed-in commodities [4]. I think Benjamin, Deric= k 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 w= ant to talk about that at all. Instead, I want to focus on the other sid= e: construct from string by default (from/tryFrom()).

I also read Larry's post that has been shared throughout these di= scussions [5] and it seems fair and sound to use guardrails to discourag= e 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 buil= t 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:

M= y question, then, is why can't basic enumerations have these semantics b= y default? Or, to state it more concretely, what would be the downside t= o having all "basic" enumerations actually being "backed" enumerations w= hose values implicitly mirror their names for the purposes of converting= to/from strings? Would this not make basic enumeration more useful with= out any particular downsides?

While I'm = searching for similar clarification, I want to pose the question differe= ntly. I feel like the answer to his question is in Larry's article about= discouraging fancy strings. My question boils down purely to: can Ba= sic Enums implement ::from() / tryFrom() methods?
<= br>
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 desi= gn.

Although this starts to thr= ead into very pedantic territory, I think, in fact, a unit enum&n= bsp;(assuming it means Basic enum) is in fact always serializable to a s= tring: `$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 alr= eady exists in the PHP engine behind `Enum::from()`. Maybe it's not seri= alizable in the sense that it doesn't implement any true serialization m= echanism, 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 recover= ing 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 th= is: just put `: string` on your Enum and duplicate the names with values= . Still, this doesn't address the "surprise effect" of "why this Enum do= esn'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 m= aking Enums "Fancy strings", I'm looking for ways to reconstruct my Enum= and all the behaviors available inside of it without even having to ass= ociate 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 check= ed and:

enum Foo {
   = ; case 1;
    case 2;
}

is a parse error. [6].

Larry has also su= ggested that instead of making Basic Enum implement `::from()` and `::tr= yFrom()` we could instead offer auto-populated String-Backed Enum values= . That means transforming this:

```
<= div>enum Foo: string {
    case Bar;
 = ;   case Baz;
}
```
(which is a= Fatal Error today) into this:

enum Foo: s= tring {
    case Bar =3D 'Bar';
  &nb= sp; case Baz =3D 'Baz';
}

I als= o 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 peo= ple could have Basic Enum already implementing their own custom ::from()= method.

In conclusion, the "complex" (as oppos= ed 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 u= sed in Web context where we may need to use asynchronous processes to ma= ke API calls fast and schedule executions at a different context which o= ften 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 fro= m / tryFrom utilities. This should not affect Int-Backed Enums at all.&n= bsp;

Where casing is concerned (camelCase, Pasc= alCase, snake-case, etc) [7], one can argue that if you want to have ful= l control over casing, you should definitely take control over the value= s of your Enum. The beauty (in my mind) about making it default to the e= num 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 n= ot a Fancy string.

I didn't intend to write suc= h a long email, but I'm really keen on hearing arguments against everyth= ing I shared to see if there are any flaws in my thought process.
<= div>
[6] <= a href=3D"https://3v4l.org/cDISV#v8.4.10">https://3v4l.org/cDISV#v8.4.10=

--
=
Marco Deleu
=

Hey Marco,

I think this relies on whether the =E2=80=9Cnatural value=E2=80= =9D of a unit enum is a string or not. It might be, or it might not be. = I don=E2=80=99t think you have a compelling argument on why it is a stri= ng and that string is the name. Personally, I used int-backed enumeratio= ns far more so I would argue that the natural value is an integer, not a= string. I=E2=80=99ve been thinking of a =E2=80=9Cquality-of-life RFC=E2= =80=9D for obvious enums, for some time now. Basically, backed enums wit= hout a value receive the =E2=80=9Cobvious=E2=80=9D value. So a string ba= cked enum gets the name, while an int gets the ordered number. 

I think something like that makes more sense than = trying to decide what the =E2=80=9Cnatural value=E2=80=9D of a unit enum= is. 

=E2=80=94 Rob --429a47eae8fb49848cf063022063a645--