Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129124 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 1EFD31A00BC for ; Fri, 7 Nov 2025 04:53:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1762491225; bh=Ob8h4NkZhtvdpnyZUmfQgDFcFCybNglKoi7Fsia0OzE=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=EtkP3GtLYiqtVVQSot78X2zyzpR8njkyWk+GpCwKePf/ygxgpFczM4fYPCFbTkmZJ I5Nm24y5jiIl1WiyGvKplC1XQQlW8qPCFChNnDt3Cf63BHtsLmeeH+afwEb+akX98x OEW5nQCKG4sbFCji+YTq69acwkPgX3uyTk75EaN0zgfTz9UWJwS73KYg3PZTcyQhtK Mr0gONkHfalrXo2jpM3k1+7eyVH8raG652X/P8QiRwDkO6g6hqdoJrIAFktPUHFKHv zs8PdGdZW6CK4vLdremoU7VzXkhrOz1rFNLq5eyN6P4ZCTPENdQws3EjAtZCs71I7H cme3zdAuvVAjg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 81C8918006E for ; Fri, 7 Nov 2025 04:53:44 +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=0.3 required=5.0 tests=BAYES_05,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, FREEMAIL_REPLY,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-ed1-f45.google.com (mail-ed1-f45.google.com [209.85.208.45]) (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 ; Fri, 7 Nov 2025 04:53:44 +0000 (UTC) Received: by mail-ed1-f45.google.com with SMTP id 4fb4d7f45d1cf-63bdfd73e6eso555715a12.0 for ; Thu, 06 Nov 2025 20:53:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762491218; x=1763096018; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=SSEvvvFvFNcC/D+3g7Ih7AOFjZtvF6yUDWajm2ypgrQ=; b=l5ly86tlCQ0dmxw8nt6zjis8lnhA5u78wHYUvvqNUt+m+zM94p4VaQ2KS6GmouZi2a 25oJTheUQEGf8lu8nYBY2B/iMSnbbHeoQSLofTFs5p4lxD8KZAWmcYVE0tYcbLakQC4a uQFWWh3kErBg/Y36yTwZKVWXhtlVai9qPZgwchZSoHTrS8xl6L1oxlm3Qceg9LJnD9Fu SzZycRPUU7LaO/79ems0JfQG9hFN9gz91dB6tF30wYUTps/XBSEAmLFZ3YaqBaqLOLRa YX68m/mMP3TzBoQ5Qi+XMs1UwrTpyUnXWjJMRS4tNOJfxjQJEyznypSwq5TNfzvssao8 cHxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762491218; x=1763096018; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=SSEvvvFvFNcC/D+3g7Ih7AOFjZtvF6yUDWajm2ypgrQ=; b=OWpCQieEOUe3n2bOAAWK+Evqxl1DnQgdF71skKOSN47V7GC25LI6jtbcG+eb0UqnHy VGKRrknt8mWjs9lqScbPdQuUDNAWOJVubvwksmzTBC+OXj4Sl4S9F+e3IMfPi94j+jA2 E4Cnk8nhHWb41kSpLed1hXhK/gJMyoyBE179BlehceNw+hhNGn7Vk16COpTZLCqciDzO c4sMjm4AJRwI3raveLjoUf51iNyqaDEFkyeLt6uhpQZNq1q0fQh4ahU4j9uQEG2cWikK iI8RTbEXpNYpEDLRdYURIC5ySVQ1oRM0dsgisNNGvzxCL874VZ1edumTXcCD1nUPtM5S whmQ== X-Forwarded-Encrypted: i=1; AJvYcCXvBO5SXzpaSs2GVecx8QbwSMgNaZmB1RjVe/6zxGlvPvmFocGAnU7QdczD1Ts3aGbFMzOzdu97/Og=@lists.php.net X-Gm-Message-State: AOJu0YzT0HnlEmdHVW9cwJT0xPty6eeDAWjcb22gW1uZbPhkKdr/oTW7 EMZ9rpw/sCWSxFmd4ILRGbZBxHVHFbzWA8dWU+FEd0x5og6XI2Nfqg5xYuzyWYrzV/SMLS7A8OD /rAUoFmXQQJwguvSv69WMTh3PVe7vmmOO/jGWcZpOHA== X-Gm-Gg: ASbGncuX6ZVlYC8kfD2TXkIejuvzBdxQzhtHIS1dUSXVn8Nh54lq8gQGINKHW2Niuko qUs8M2tjcTINW6C8Us/BsDQwsFWsHD/+O/rQdyKOPVp42ANXK/s2o7DDT7fijlC1UNwqGvDdd/W pHiLfkuHaGuE9J/Vk8mt+L2TfSzyeQC1SgCTxGzgwIu1xNzRlbG72w4Q4AeA+fm97B+OHyUfECq twdlUoXnSZmLqh3EAeuLtra4Y4Ey5mGrqtAzN4AXlb9SlCgOPcY3oU/QIW/xQ== X-Google-Smtp-Source: AGHT+IH6KTADoOuCuaQGG0FjdSWRC6LXp/4gvEb5mviegtMr23YmK2qaimmHzQDHx+CWu7VkTrgr+8Ejqk9L5bd2AZg= X-Received: by 2002:a17:907:3d8c:b0:b70:aebe:2ef7 with SMTP id a640c23a62f3a-b72d09c3960mr41006866b.14.1762491217830; Thu, 06 Nov 2025 20:53:37 -0800 (PST) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Fri, 7 Nov 2025 07:53:26 +0300 X-Gm-Features: AWmQ_bnCdHQAm0wVf4ktzPAJnM9IUzGHPn_tTaKWYLw93-w9abdU0C7wRnhx57g Message-ID: Subject: Re: [PHP-DEV] [RFC] [Discussion] BackedEnum::values() - Seeking feedback before formal RFC To: Valentin Udaltsov Cc: Larry Garfield , php internals Content-Type: multipart/alternative; boundary="000000000000f565890642f9f6a1" From: mikhail.d.savin@gmail.com (Mikhail Savin) --000000000000f565890642f9f6a1 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable =D0=BF=D1=82, 7 =D0=BD=D0=BE=D1=8F=D0=B1. 2025=E2=80=AF=D0=B3. =D0=B2 07:08= , Valentin Udaltsov : > =D1=87=D1=82, 6 =D0=BD=D0=BE=D1=8F=D0=B1. 2025=E2=80=AF=D0=B3., 19:30 Lar= ry Garfield : > >> On Wed, Nov 5, 2025, at 10:09 PM, Mikhail Savin wrote: >> > Hi internals, >> > >> > I would like to propose adding a native values() method to the >> BackedEnum >> > interface that returns an array of all backing values. Before creating= a >> > formal RFC, I'm seeking feedback on the concept and approach. >> > >> > =3D=3D Summary =3D=3D >> > >> > The proposal adds: >> > >> > interface BackedEnum { >> > public static function values(): array; >> > } >> > >> > This would allow: >> > >> > enum Status: string { >> > case Active =3D 'active'; >> > case Inactive =3D 'inactive'; >> > } >> > >> > Status::values(); // ['active', 'inactive'] >> > >> > =3D=3D Motivation =3D=3D >> > >> > This pattern is extremely common in the wild. Based on GitHub code >> search: >> > >> > * ~3,860+ direct implementations of this exact pattern >> > * ~20,000-40,000 estimated real usage when accounting for shared >> traits >> > * Used in major frameworks: Symfony core (TypeIdentifier.php), >> > Laravel ecosystem >> > * Documented by PHP.net: The manual itself shows EnumValuesTrait as >> > an example >> >> Correction: The manual does not show an EnumValuesTrait that I can find. >> There is a *comment* in the manual that includes this method, but that's >> not part of the manual proper, and frankly 90% of comments in the manual >> should be removed. (cf: >> https://www.php.net/manual/en/language.enumerations.traits.php#129250) >> >> > Common use cases: >> > * Database migrations: $table->enum('status', Status::values()) >> > * Form validation: $validator->rule('status', 'in', Status::values()= ) >> > * API responses: ['allowed_statuses' =3D> Status::values()] >> > >> > =3D=3D Implementation =3D=3D >> > >> > I have a working implementation with tests: >> > https://github.com/php/php-src/pull/20398 >> > >> > The implementation: >> > * Mirrors the existing cases() method structure >> > * Extracts the value property from each case >> > * Returns an indexed array (0, 1, 2, ...) >> > * Only available on BackedEnum, not UnitEnum >> > * All tests pass >> >> I am unclear why this is a major advantage over >> array_column(Status::cases(), 'value'); >> >> > =3D=3D Backward Compatibility - Important Discussion Point =3D=3D >> > >> > This is a breaking change. Enums that already define a values() method >> > will fail with: >> > >> > Fatal error: Cannot redeclare BackedEnum::values() >> > >> > Based on ecosystem research: >> > * ~24,000-44,000 enum instances will break >> > * All implementations are functionally identical to what's being >> proposed >> > * Migration is mechanical: just delete the user-defined method >> >> This is a hard-stop. There are hundreds of thousands of packages in the >> wild that need to support multiple PHP versions. Nearly all packaglist >> packages (which I presume is where you're drawing the research from; eit= her >> that or GitHub which will give a similar result set) support at least tw= o >> consecutive versions, if not 4, 5, or 6. >> >> A hard break like this would essentially mean the packages containing >> those 40,000 enums would be unable to support both PHP 8.5 and 8.6 at th= e >> same time. That's simply not an acceptable impact on the ecosystem, >> regardless of how nice the feature may or may not be. >> >> --Larry Garfield >> > > We could add a virtual $values property. Since enum properties are not > allowed in userland, it will not break any existing code. > > =E2=80=94 > Valentin > Hi all, Thank you for the thoughtful feedback. Based on the discussion so far, the consensus seems to be: "the feature is useful, but the BC break is too large." To address this, I've adjusted the proposal so that user code is allowed to redeclare values() on backed enums. This keeps existing projects working unchanged while providing the native implementation for new code. The native values() will only be added when not already defined: ```c if (!zend_hash_exists(&ce->function_table, ZSTR_KNOWN(ZEND_STR_VALUES))) = { ... } ``` Result: * Zero BC break - existing code unchanged * New enums get values() automatically * Libraries can maintain their implementation for older PHP support Trade-off: I recognize this makes values() the only overridable enum intrinsic (unlike cases/from/tryFrom). I'll document this clearly and add tests to lock down the behavior. If needed, we can deprecate user-defined values() in a later 8.x and make it an error in PHP 9.0. Questions: 1. Is allowing values() override technically acceptable? 2. Is documenting the inconsistency sufficient? 3. Should we add deprecation? 4. Can I submit RFC for this feature? 5. Should I rather implement it via virtual property, as Valentin suggested above? I updated the PR, and also added a few tests for this behavior. Thoughts? Best regards, Savin Mikhail --000000000000f565890642f9f6a1 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=D0=BF=D1=82, 7= =D0=BD=D0=BE=D1=8F=D0=B1. 2025=E2=80=AF=D0=B3. =D0=B2 07:08, Valentin Udal= tsov <udaltsov.valentin@g= mail.com>:
=D1=87=D1=82, 6 =D0=BD=D0=BE=D1=8F=D0= =B1. 2025=E2=80=AF=D0=B3., 19:30 Larry Garfield <larry@garfieldtech.com>:
<= div class=3D"gmail_quote" dir=3D"auto">
On Wed, Nov 5, 2025, at 10:09 PM, Mikhail Savin wrote:
> Hi internals,
>
> I would like to propose adding a native values() method to the BackedE= num
> interface that returns an array of all backing values. Before creating= a
> formal RFC, I'm seeking feedback on the concept and approach.
>
> =3D=3D Summary =3D=3D
>
> The proposal adds:
>
>=C2=A0 =C2=A0 =C2=A0interface BackedEnum {
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public static function values(): arra= y;
>=C2=A0 =C2=A0 =C2=A0}
>
> This would allow:
>
>=C2=A0 =C2=A0 =C2=A0enum Status: string {
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case Active =3D 'active';
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case Inactive =3D 'inactive';=
>=C2=A0 =C2=A0 =C2=A0}
>=C2=A0 =C2=A0
>=C2=A0 =C2=A0 =C2=A0Status::values(); // ['active', 'inacti= ve']
>
> =3D=3D Motivation =3D=3D
>
> This pattern is extremely common in the wild. Based on GitHub code sea= rch:
>
>=C2=A0 =C2=A0* ~3,860+ direct implementations of this exact pattern
>=C2=A0 =C2=A0* ~20,000-40,000 estimated real usage when accounting for = shared traits
>=C2=A0 =C2=A0* Used in major frameworks: Symfony core (TypeIdentifier.p= hp),
>=C2=A0 =C2=A0 =C2=A0Laravel ecosystem
>=C2=A0 =C2=A0* Documented by PHP.net: The manual itself shows EnumValue= sTrait as
>=C2=A0 =C2=A0 =C2=A0an example

Correction: The manual does not show an EnumValuesTrait that I can find.=C2= =A0 There is a *comment* in the manual that includes this method, but that&= #39;s not part of the manual proper, and frankly 90% of comments in the man= ual should be removed.=C2=A0 (cf: https://www.php.net/manual/en/language.enumerations.traits.ph= p#129250)

> Common use cases:
>=C2=A0 =C2=A0* Database migrations: $table->enum('status', S= tatus::values())
>=C2=A0 =C2=A0* Form validation: $validator->rule('status', &= #39;in', Status::values())
>=C2=A0 =C2=A0* API responses: ['allowed_statuses' =3D> Statu= s::values()]
>
> =3D=3D Implementation =3D=3D
>
> I have a working implementation with tests:
> https://github.com/php/php-src/pull/20398
>
> The implementation:
>=C2=A0 =C2=A0* Mirrors the existing cases() method structure
>=C2=A0 =C2=A0* Extracts the value property from each case
>=C2=A0 =C2=A0* Returns an indexed array (0, 1, 2, ...)
>=C2=A0 =C2=A0* Only available on BackedEnum, not UnitEnum
>=C2=A0 =C2=A0* All tests pass

I am unclear why this is a major advantage over array_column(Status::cases(= ), 'value');

> =3D=3D Backward Compatibility - Important Discussion Point =3D=3D
>
> This is a breaking change. Enums that already define a values() method=
> will fail with:
>
>=C2=A0 =C2=A0 =C2=A0Fatal error: Cannot redeclare BackedEnum::values()<= br> >
> Based on ecosystem research:
>=C2=A0 =C2=A0* ~24,000-44,000 enum instances will break
>=C2=A0 =C2=A0* All implementations are functionally identical to what&#= 39;s being proposed
>=C2=A0 =C2=A0* Migration is mechanical: just delete the user-defined me= thod

This is a hard-stop.=C2=A0 There are hundreds of thousands of packages in t= he wild that need to support multiple PHP versions.=C2=A0 Nearly all packag= list packages (which I presume is where you're drawing the research fro= m; either that or GitHub which will give a similar result set) support at l= east two consecutive versions, if not 4, 5, or 6.

A hard break like this would essentially mean the packages containing those= 40,000 enums would be unable to support both PHP 8.5 and 8.6 at the same t= ime.=C2=A0 That's simply not an acceptable impact on the ecosystem, reg= ardless of how nice the feature may or may not be.

--Larry Garfield



Hi all,

Thank you for = the thoughtful feedback. Based on the discussion so far, the
consensus = seems to be: "the feature is useful, but the BC break is too large.&qu= ot;

To address this, I've adjusted the proposal so that user cod= e is allowed to
redeclare values() on backed enums. This keeps existing= projects working
unchanged while providing the native implementation f= or new code.

The native values() will only be added when not already= defined:

```c
=C2=A0 if (!zend_hash_exists(&ce->function_= table, ZSTR_KNOWN(ZEND_STR_VALUES))) {
=C2=A0 =C2=A0 ...
}
```

Result:
=C2=A0 * Zero BC break - existing= code unchanged
=C2=A0 * New enums get values() automatically
=C2=A0 = * Libraries can maintain their implementation for older PHP support

= Trade-off:
I recognize this makes values() the only overridable enum int= rinsic
(unlike cases/from/tryFrom). I'll document this clearly and = add tests
to lock down the behavior. If needed, we can deprecate user-d= efined
values() in a later 8.x and make it an error in PHP 9.0.

= Questions:
1. Is allowing values() override technically acceptable?
2= . Is documenting the inconsistency sufficient?
3. Should we add deprecat= ion?
4. Can I submit = RFC for this feature?
5. Should I rather implement it via virtual property, as Valentin suggeste= d above?

I update= d the PR, and also added a few tests for this behavior.=C2=A0

Though= ts?

Best regards,
Savin Mikhail=C2=A0
--000000000000f565890642f9f6a1--