Re: [RFC] [Discussion] Literal Scalar Types

From: Date: Mon, 15 Jun 2026 18:55:23 +0000
Subject: Re: [RFC] [Discussion] Literal Scalar Types
References: 1 2 3  Groups: php.internals 
Request: Send a blank email to internals+get-131294@lists.php.net to get a copy of this message
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

« previous php.internals (#131294) next »