Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129193 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 8D59C1A00BC for ; Tue, 11 Nov 2025 08:26:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1762849566; bh=K2K+nhJ4pjehga4MvYYjrbAo6UP2lUmMXJYkAxEKpbc=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=KKdRRPSI/3T4y5IwMROMcjNeO6rMINVTL2jmZb/pxwPH8BQrAewNZDXHfLP5Xwvnq NTLdl3wOGfr91PYt7u9jpGV1pqV09fJKvwQz62uYD+ROVC+4tqftQOqrd6HDBSNCRf wBZE/i0D1aYAkNeR5JQQabA3ngG+vg6Y8hdNS7c95SVqw8yohXyb9OaiCrX/6EAXww SzUeH2jaWt8u7r9FrNBPRcLBFAO9sAb3lxleeBfKgFxTf7D1dQ614hPBLsyt43NWjy vgHw1iblz1M59OU0ZRUtyyTfl2SGo4K6bMLFMflwFgTkwS63bAdbYZ8BlEj4MdEZ4M ruOomxy8o7E6g== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id C247C18003E for ; Tue, 11 Nov 2025 08:26:05 +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.8 required=5.0 tests=BAYES_20,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-ej1-f43.google.com (mail-ej1-f43.google.com [209.85.218.43]) (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 ; Tue, 11 Nov 2025 08:26:05 +0000 (UTC) Received: by mail-ej1-f43.google.com with SMTP id a640c23a62f3a-b72cbc24637so727773266b.0 for ; Tue, 11 Nov 2025 00:26:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762849559; x=1763454359; 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=j4el2v7FgbVYWFnzJ+qhyXoabLjuudpLTSnYg4I1mys=; b=ZdMrBDoLU1LFPvTRVRUWgdCg7CZ4nBnpkvjGUintSvsVmTvO7wz4Ccc4/B5/2oOg+l IQVEMacwZBPe1JQPNflv4ShWnL1FXXuhHeanuH0Xmad2DdMsDySXpk/g4vXzSle8laLg JyFQlEohYq618XynVTT0Xk7f4Id8yuS0jT2zSbmDJfQlUpReInbrZqR3d1BCN5Rgy7fo 1eG2K8aSSGz/a59yoLVDvlyErM07w1NwWHcv7fNXxzm6tG2+0X3Vg9EIoKF3xcExeIaI /BQmmGEWsarvNl+YQL6wElsggaPNiQihGRovq/QH1RtXJG8GtSE7yPQOKzaIqCYDGV5W XO2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762849559; x=1763454359; 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=j4el2v7FgbVYWFnzJ+qhyXoabLjuudpLTSnYg4I1mys=; b=quVoGnjNwIgFxSVx7Hu5ceclVagvDiG7LFzfCOY5r/FGzzEXB6VjjwkI3NgSL+dptN 44wt5h9SxHZ20K9dovKQejHFv7eSk1bOJLnFHrcXWT1Hk00mEjtRePukskSpeQgqG+I9 aMAwxCVhlFDmCLO+MURgjySTKpx67nahlYRwE7DjQWsD9Fh2MvZxK/E3o8Zqgj2kB+CG GXIFCMDijoxouYz2NpvnBaOAyUtF/5kgm528yTKw9vbKgh/WdSw0aBidOfOimdJi7GDJ 8OhC0MzVPqRIOlqN9omslqFSKByZ7PnVJYmVLQd+9YpF42/51kIkDrIA6seh2mMMpxZK m4Og== X-Gm-Message-State: AOJu0YxnfLbIS0AZIxJ8EA3J4v8MWOcsCQx3vLnHBztikJOVL+GdSkkT CYOrT00pnzAJXMfoh9mwG8ZqLak0EeAyaRNRVgBSiCMLr8F2b+fb1lYJR1UnA5YDgd3Z6VsDAFM XuvQI1i2khz/0R5RJUcBoHOUvSpmtEagv78MWmWDRMw== X-Gm-Gg: ASbGncs7LKpw/Fi6bs2WgI0yFBHNgxqCxB2nuvYzSxH891rJvMO3hVLZ0RbiFmiiZVA OwbpS09zhjOl9CDvJvnR0Yo2bN+qYOP/k6AsjEnENquE9r1YEJx1qMIT0MPamu4giukuTOhXeBU dLWFRC/iOtzSEyazIWu9iQC16uQ013ICbmnLntwUhfR6fjD1SZXSUwOpXcDT4eSApgMtXs8+361 hkdJsFBKRiWr8YkpvxQ52KFFy+ECAm0xnU/hdzVLVHZcOzDOynlsum+FZAq X-Google-Smtp-Source: AGHT+IGf8xZZhPrw0t5judrCiQmJ4P3+ZrXNZtCQQ7bjinHSg78MaxmBBUUguN717pX81+9kMfeA9PEzxv0p31WYZsY= X-Received: by 2002:a17:907:969e:b0:b6d:6a35:9996 with SMTP id a640c23a62f3a-b72e058dff0mr1062631166b.58.1762849558954; Tue, 11 Nov 2025 00:25:58 -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: Tue, 11 Nov 2025 11:25:46 +0300 X-Gm-Features: AWmQ_bm5Lm7lpnan3fnC6Rglq8TPXxTwL75dMh6Hj7Gc1PC7XozksCUt9FLkOm4 Message-ID: Subject: Re: [PHP-DEV] [RFC] [Discussion] Add values() Method to BackedEnum To: Valentin Udaltsov Cc: php internals Content-Type: multipart/alternative; boundary="000000000000c106dc06434d65d1" From: mikhail.d.savin@gmail.com (Mikhail Savin) --000000000000c106dc06434d65d1 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable =D0=BF=D0=BD, 10 =D0=BD=D0=BE=D1=8F=D0=B1. 2025=E2=80=AF=D0=B3. =D0=B2 23:5= 5, Valentin Udaltsov : > =D0=BF=D0=BD, 10 =D0=BD=D0=BE=D1=8F=D0=B1. 2025=E2=80=AF=D0=B3. =D0=B2 08= :44, Mikhail Savin : > >> Hi internals, >> >> I've created an RFC to add a native values() method to BackedEnum: >> >> https://wiki.php.net/rfc/add_values_method_to_backed_enum >> >> =3D=3D Summary =3D=3D >> >> The RFC proposes adding BackedEnum::values() that returns an indexed >> array >> of all backing values. The implementation uses conditional registration = - >> the native method is only added when the enum doesn't already define >> values(), ensuring ZERO backward compatibility breaks. >> >> Key points: >> * Native values() added automatically to new enums >> * Existing enums with custom values() continue working unchanged >> * Trait-based implementations are respected >> * Libraries can maintain their implementation for older PHP versions >> * Solves boilerplate problem (3,860+ implementations, ~24k-44k real >> usage) >> >> Common use cases: >> * Database migrations: $table->enum('status', Status::values()) >> * Form validation: in_array($input, Status::values()) >> * API responses: ['allowed_values' =3D> Status::values()] >> >> =3D=3D Implementation =3D=3D >> >> Working implementation with conditional registration: >> https://github.com/php/php-src/pull/20398 >> >> The engine checks if values() exists before registering the native >> version: >> - User-defined values() present? Use it. >> - No user-defined values()? Add native implementation. >> >> =3D=3D No BC Breaks =3D=3D >> >> This approach ensures: >> * Existing code works unchanged (no migration needed) >> * Immediate benefit for new code (automatic values()) >> * Gradual adoption possible (libraries can migrate at their pace) >> >> Trade-off: Makes values() the only overridable enum method (unlike >> cases/from/tryFrom). The RFC documents this as a pragmatic choice - >> solving real problems without forced migrations. >> >> =3D=3D Questions =3D=3D >> >> 1. Is the conditional approach technically sound? >> 2. Is the API consistency trade-off acceptable given the zero BC breaks? >> 3. Any concerns with the implementation approach? >> 4. Should I implement some steps from "Future scope" of the rfc now? >> >> Discussion period: 2 weeks minimum before voting. >> >> Looking forward to your feedback! >> >> Best regards, >> Savin Mikhail >> > > Hi, Mikhail! > > Thank you for the RFC. > > Consider this code if this RFC is accepted: > > enum EnumWithUserDefinedValuesMethod: string > { > case X =3D 'x'; > > public static function values(): string > { > return 'values'; > } > } > > function getBackedEnumValues(BackedEnum $enum): array > { > return $enum::values(); > } > > getBackedEnumValues(EnumWithUserDefinedValuesMethod::X); > > > This code will suddenly break, because inside getBackedEnumValues I can > safely assume that BackedEnum::values() returns an array, since it's a pa= rt > of the interface contract. > > However, EnumWithUserDefinedValuesMethod breaks the Liskov Substitution > Principle by defining a method with a non-compatible return type and give= s > a runtime error. > > What you've basically suggested is to ignore the LSP. This is not a good > idea. > > -- > Best regards, Valentin > Thanks for raising the LSP concern. You=E2=80=99re right that today a userl= and enum can define a values() with an arbitrary signature, and a helper like: function getBackedEnumValues(BackedEnum $enum): array { return $enum::values(); } could blow up at runtime if that user method returns a non-array. The RFC addresses this by adding "public static function values(): array" to the BackedEnum interface itself. With that in place, an enum that defines an incompatible values() will fail at compile time with a normal method-signature incompatibility, exactly like it would for from()/tryFrom(). Here=E2=80=99s a .phpt demonstrating the intended behavior: --TEST-- Backed enums: user-defined values() incompatible with interface signature --FILE-- --EXPECTF-- Fatal error: Declaration of E::values(): string must be compatible with BackedEnum::values(): array in %s on line %d Run with: TEST_PHP_EXECUTABLE=3Dsapi/cli/php sapi/cli/php -n run-tests.php -q Zend/tests/enum/backed-values-user-defined-incompatible.phpt So LSP isn=E2=80=99t being ignored; it=E2=80=99s enforced by the interface = method. That said, adding values() to BackedEnum is a source-level BC break for codebases that already define conflicting values() on backed enums. But, as we can see from GitHub searches that I posted in PR, most of the implementations is exactly the same, so BC break is very small --000000000000c106dc06434d65d1 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=D0=BF=D0=BD, 1= 0 =D0=BD=D0=BE=D1=8F=D0=B1. 2025=E2=80=AF=D0=B3. =D0=B2 23:55, Valentin Uda= ltsov <udaltsov.valentin@= gmail.com>:
=D0=BF=D0=BD, 10 =D0=BD=D0=BE=D1=8F=D0= =B1. 2025=E2=80=AF=D0=B3. =D0=B2 08:44, Mikhail Savin <mikhail.d.savin@gmail.com= >:
Hi internals,

I've created an RFC = to add a native values() method to BackedEnum:

https://= wiki.php.net/rfc/add_values_method_to_backed_enum

=3D=3D Summary= =3D=3D

The RFC proposes adding BackedEnum::values() that returns an= indexed array
of all backing values. The implementation uses condition= al registration -
the native method is only added when the enum doesn&#= 39;t already define
values(), ensuring ZERO backward compatibility brea= ks.

Key points:
=C2=A0 * Native values() added automatically to n= ew enums
=C2=A0 * Existing enums with custom values() continue working u= nchanged
=C2=A0 * Trait-based implementations are respected
=C2=A0 * = Libraries can maintain their implementation for older PHP versions
=C2= =A0 * Solves boilerplate problem (3,860+ implementations, ~24k-44k real usa= ge)

Common use cases:
=C2=A0 * Database migrations: $table->en= um('status', Status::values())
=C2=A0 * Form validation: in_arra= y($input, Status::values())
=C2=A0 * API responses: ['allowed_values= ' =3D> Status::values()]

=3D=3D Implementation =3D=3D

= Working implementation with conditional registration:
https://github.com/p= hp/php-src/pull/20398

The engine checks if values() exists befor= e registering the native version:
=C2=A0 - User-defined values() present= ? Use it.
=C2=A0 - No user-defined values()? Add native implementation.<= br>
=3D=3D No BC Breaks =3D=3D

This approach ensures:
=C2=A0 *= Existing code works unchanged (no migration needed)
=C2=A0 * Immediate = benefit for new code (automatic values())
=C2=A0 * Gradual adoption poss= ible (libraries can migrate at their pace)

Trade-off: Makes values()= the only overridable enum method (unlike
cases/from/tryFrom). The RFC = documents this as a pragmatic choice -
solving real problems without fo= rced migrations.

=3D=3D Questions =3D=3D

1. Is the conditiona= l approach technically sound?
2. Is the API consistency trade-off accept= able given the zero BC breaks?
3. Any concerns with the implementation a= pproach?
4. Should I implement some steps from "Future scope"= of the rfc now?=C2=A0

Discussion period: 2 weeks minimum before vot= ing.

Looking forward to your feedback!

Best regards,
Savin= Mikhail

Hi, Mikhail!
<= br>Thank you for the RFC.

Consider this code if th= is RFC is accepted:

enum EnumWithUserDefinedVa=
luesMethod: string
{ case X =3D 'x';

public static function values(): string
{
return 'values'= ;;
}
}

function= getBackedEnumValues(BackedEnum
$enum): array
{=
return $enum::values();
}

getBackedEnumValues(EnumWithUserDe= finedValuesMethod::X);

This code will suddenly break, because inside= =C2=A0getBackedEnumValues I can safely assume that BackedEnum::values() ret= urns an array, since it's a part of the interface contract.

However,=C2=A0EnumWithUserDefinedValuesMetho= d breaks the Liskov Substitution Principle by defining a method with a non-= compatible return type and gives a runtime error.

What you've basically suggested is to ignore the LSP. = This is not a good idea.

--

Best regards, Valentin

Thanks for raising the LSP concern. You=E2=80=99re ri= ght that today a userland enum can define a values() with an arbitrary sign= ature, and a helper like:

function getBackedEnumValues(BackedEnum $e= num): array {
=C2=A0 =C2=A0 return $enum::values();
}

could bl= ow up at runtime if that user method returns a non-array.

The RFC ad= dresses this by adding "public static function values(): array" t= o the BackedEnum interface itself.
With that in place, an enum that defi= nes an incompatible values() will fail at compile time with a normal method= -signature incompatibility, exactly like it would for from()/tryFrom().
=
Here=E2=80=99s a .phpt demonstrating the intended behavior:

--TE= ST--
Backed enums: user-defined values() incompatible with interface sig= nature
--FILE--
<?php

enum E: string {
=C2=A0 =C2=A0 cas= e A =3D 'a';

=C2=A0 =C2=A0 // Intentional incompatibility: i= nterface requires array return type
=C2=A0 =C2=A0 public static function= values(): string {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 'values';=
=C2=A0 =C2=A0 }
}

?>
--EXPECTF--
Fatal error: Declar= ation of E::values(): string must be compatible with BackedEnum::values(): = array in %s on line %d

Run with:
TEST_PHP_EXECUTABLE=3Dsapi/cli/p= hp sapi/cli/php -n run-tests.php -q Zend/tests/enum/backed-values-user-defi= ned-incompatible.phpt

So LSP isn=E2=80=99t being ignored; it=E2=80= =99s enforced by the interface method.

That said, adding values() to= BackedEnum is a source-level BC break for codebases that already define co= nflicting values() on backed enums.

But, as we can see from GitHub s= earches that I posted in PR, most of the implementations is exactly the sam= e, so BC break is very small
--000000000000c106dc06434d65d1--