Hi

On 6/15/26 04:12, Seifeddine Gmati wrote:
1. describing APIs that already exist. `array_filter`'s `$mode` really
accepts `0|1|2`, but it's typed `int` because that's all the type
system can say today. we can't retype it as an enum without breaking
every caller. a literal union lets the signature state the actual
contract.

We can retype this kind of API with enums.

See the “Correctly name the rounding mode and make it an Enum” RFC (https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum) for an example: We first widen the parameter to accept the enum so that folks can opt-in to the new API. At a later point we alias the constants to the corresponding enum cases and deprecate passing the integers and then we remove the support for the integers (and constants).

Using literal types is going to result in a terrible user-experience, because the signature does not provide any hint as to which constants are supposed to be used with the API which means that the resulting error message is also useless to the user. Enums - or the existing manual validation - is much preferable here.

2. ad-hoc / open value sets. for a library, "ascii"|"utf-8" would need
its own named symbol ( `enum BorderStyle { case Ascii; case Utf8 }`, a
new file, an import ) for what is really two strings. and because an
enum is a closed set, adding a third style later breaks any consumer
that match-es over it without a default. widening the union on a
parameter ( "ascii"|"utf-8"|"unicode" ) is contravariant, so it breaks
nobody.

The existing \RoundingMode enum is already intended to be a non-exhaustive (parameter-only) enum where users are expected to include a `default` case in case new values are being added.

I have a *very* rough draft in https://wiki.php.net/rfc/non_exhaustive_marker to make this type of contract more explicit.

Having an “own named symbol” for the allowed values is a benefit to me, because this makes it easy to reuse the list of allowed values in different locations without needing to resort to copy and paste, for example in decorators that just pass through the values without touching them.

3. scalar interop. a literal value is the scalar, so it works as an
array key, compares with ===, round-trips through json, etc. enum
cases are objects and don't.

Enums can be compared with `===`.

Best regards
Tim Düsterhus

Reply via email to