[RFC] [Discussion] Literal Scalar Types

php.internals

Seifeddine Gmati

78 days ago
Hello Internals, I'd like to start the discussion on a new RFC adding literal scalar types to PHP. - RFC: https://wiki.php.net/rfc/literal_scalar_types - Implementation: https://github.com/php/php-src/pull/22314 Thanks, Seifeddine.

David Gebler

78 days ago
On Mon, Jun 15, 2026 at 2:24 AM Seifeddine Gmati <azjezz@carthage.software> wrote:
> Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. >
Prima facie, I feel like enums already do what this aims to achieve, much better.

Seifeddine Gmati

78 days ago
On Mon, 15 Jun 2026 at 03:03, David Gebler <davidgebler@gmail.com> wrote:
> > On Mon, Jun 15, 2026 at 2:24 AM Seifeddine Gmati <azjezz@carthage.software> wrote: >> >> Hello Internals, >> >> I'd like to start the discussion on a new RFC adding literal scalar >> types to PHP. > > > Prima facie, I feel like enums already do what this aims to achieve, much better. > >> >> >> - RFC: https://wiki.php.net/rfc/literal_scalar_types >> - Implementation: https://github.com/php/php-src/pull/22314 >> >> Thanks, >> Seifeddine.
Hi David, I agree enums are the better fit for a lot of cases. but not all. 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. 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. 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. so they overlap a lot, but literal unions reach things enums structurally can't: existing scalar APIs, and open sets that grow without a BC break. Cheers, Seifeddine.

David Gebler

78 days ago
On Mon, Jun 15, 2026 at 3:13 AM Seifeddine Gmati <azjezz@carthage.software> wrote:
> On Mon, 15 Jun 2026 at 03:03, David Gebler <davidgebler@gmail.com> wrote: > > > > On Mon, Jun 15, 2026 at 2:24 AM Seifeddine Gmati > <azjezz@carthage.software> wrote: > >> > >> Hello Internals, > >> > >> I'd like to start the discussion on a new RFC adding literal scalar > >> types to PHP. > > > > > > Prima facie, I feel like enums already do what this aims to achieve, > much better. > > > >> > >> > >> - RFC: https://wiki.php.net/rfc/literal_scalar_types > >> - Implementation: https://github.com/php/php-src/pull/22314 > >> > >> Thanks, > >> Seifeddine. > > Hi David, > > I agree enums are the better fit for a lot of cases. but not all. > > 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. >
This is probably the strongest case (and should be mentioned on the RFC, I think), though I'm not sure it's a sufficient justification for the scope of change.
> 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. >
I'm not so convinced on this point. You add a new case to an enum, that library's API isn't inherently broken. Users passing Ascii or Utf8 per the original case-set remain valid. The only code that breaks there is code that assumes the enum would never gain another case and the same could be said of code matching on string literals without a default. And if "ascii"|"utf-8" is what a library exposes, consumers may treat that as exhaustive, whereas an enum doesn't inherently break as a type hint. A set that's *expected* probably shouldn't be an enum, but the strength of an enum is precisely that it's a (probably) closed set of values. On the flip-side, if a set of values is genuinely open-ended, a closed set of scalar literal unions as a type isn't going to help.
> 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. >
I think backed enums already cover this through value exposure.

David Gebler

78 days ago
On Mon, Jun 15, 2026 at 3:27 AM David Gebler <davidgebler@gmail.com> wrote:
> On Mon, Jun 15, 2026 at 3:13 AM Seifeddine Gmati <azjezz@carthage.software> > wrote: > >> On Mon, 15 Jun 2026 at 03:03, David Gebler <davidgebler@gmail.com> wrote: >> > >> > On Mon, Jun 15, 2026 at 2:24 AM Seifeddine Gmati >> <azjezz@carthage.software> wrote: >> >> >> >> Hello Internals, >> >> >> >> I'd like to start the discussion on a new RFC adding literal scalar >> >> types to PHP. >> > >> > >> > Prima facie, I feel like enums already do what this aims to achieve, >> much better. >> > >> >> >> >> >> >> - RFC: https://wiki.php.net/rfc/literal_scalar_types >> >> - Implementation: https://github.com/php/php-src/pull/22314 >> >> >> >> Thanks, >> >> Seifeddine. >> >> Hi David, >> >> I agree enums are the better fit for a lot of cases. but not all. >> >> 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. >> > > This is probably the strongest case (and should be mentioned on the RFC, I > think), though I'm not sure it's a sufficient justification for the scope > of change. > > >> 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. >> > > I'm not so convinced on this point. You add a new case to an enum, that > library's API isn't inherently broken. Users passing Ascii or Utf8 per the > original case-set remain valid. The only code that breaks there is code > that assumes the enum would never gain another case and the same could be > said of code matching on string literals without a default. And if > "ascii"|"utf-8" is what a library exposes, consumers may treat that as > exhaustive, whereas an enum doesn't inherently break as a type hint. A set > that's *expected* probably shouldn't be an enum, but the strength of an > enum is precisely that it's a (probably) closed set of values. On the > flip-side, if a set of values is genuinely open-ended, a closed set of > scalar literal unions as a type isn't going to help. >
Apologies for typo of omission in the above paragraph, I meant to say "A set that's expected to grow probably shouldn't be an enum"

Tim Düsterhus

78 days ago
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

Seifeddine Gmati

77 days ago
On Mon, 15 Jun 2026 at 19:55, Tim Düsterhus <tim@bastelstu.be> wrote:
> > 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 >
Fair points. I will happily concede that for the internal flag-style APIs (rounding mode, `array_filter`, and so on) the enum migration path you describe is a good fit, and the reuse you get from a named symbol is a real benefit. I do not think literal types are the right tool for everything enums cover. On the `===` point specifically: enums compare with `===` to other enum cases, but not to the scalar values they stand for. `Status::Success === 'success'` is always false. So the moment your data is actually a scalar, a string from `json_decode`, a value in an associative array, a column from the database, the enum case is no longer interchangeable with it; you have to map back and forth with `->value` and `::from()`. That is the case literal types are really aimed at, and it is clearest with array shapes (which I have started working on and would put in future scope). Consider typing a decoded JSON response: ``` public abstract function getResponse(): ['status' => 'success' | 'error', 'message' => null | string, 'data' => null | array, ...]; ``` The values here are genuinely scalars on the wire. A `status` field that is `"success"` or `"error"` is a discriminated union you can type exactly, and it round-trips through `json_encode` / `json_decode` untouched. This is everywhere in practice: tagged event payloads (`{"type": "created" | "updated" | "deleted"}`), result envelopes (`{"ok": true, ...}` vs `{"ok": false, "error": string}`), open/closed flags, mode strings. Modelling these with enums means converting every field on the way in and on the way out, even though the data never stops being a plain string. So I see them covering different ground: enums for named, reusable, behaviour-carrying sets; literal types for describing scalar data that already exists in its raw form, particularly structured payloads like JSON.

Ben Ramsey

78 days ago
On 6/14/26 20:22, Seifeddine Gmati wrote:
> Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314 > > Thanks, > Seifeddine.
I think I'm okay with this. David mentioned enums, and I do think enums are useful in many places where you want types like this, but there's a simplicity in this that I can't deny, and I like it. Cheers, Ben

Andreas Heigl

78 days ago
On 15.06.26 06:53, Ben Ramsey wrote:
> On 6/14/26 20:22, Seifeddine Gmati wrote: >> Hello Internals, >> >> I'd like to start the discussion on a new RFC adding literal scalar >> types to PHP. >> >> - RFC: https://wiki.php.net/rfc/literal_scalar_types >> - Implementation: https://github.com/php/php-src/pull/22314 >> >> Thanks, >> Seifeddine. > > > I think I'm okay with this. David mentioned enums, and I do think enums > are useful in many places where you want types like this, but there's a > simplicity in this that I can't deny, and I like it.
I do like the simplicity of this. But - especially for floats and ints - the next level would be to allow not only ``` public function check(int -1|0|1 $minusOneThroughOne) ``` but also something like ``` public function check(int -1..1 $minusOneThrougOne) ``` which would then also allow ``` public function check (int 1..PHP_INT_MAX $positiveInt) ``` would that also be something to be considered? It seems like a logical alternate option to not have to add every value literally to the option list... Cheers Andreas
-- ,,, (o o) +---------------------------------------------------------ooO-(_)-Ooo-+ | Andreas Heigl | | mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" | | https://andreas.heigl.org | +---------------------------------------------------------------------+ | https://hei.gl/appointmentwithandreas | +---------------------------------------------------------------------+ | GPG-Key: https://hei.gl/keyandreasheiglorg | +---------------------------------------------------------------------+

Seifeddine Gmati

78 days ago
On Mon, 15 Jun 2026 at 06:23, Andreas Heigl <andreas@heigl.org> wrote:
> > > > On 15.06.26 06:53, Ben Ramsey wrote: > > On 6/14/26 20:22, Seifeddine Gmati wrote: > >> Hello Internals, > >> > >> I'd like to start the discussion on a new RFC adding literal scalar > >> types to PHP. > >> > >> - RFC: https://wiki.php.net/rfc/literal_scalar_types > >> - Implementation: https://github.com/php/php-src/pull/22314 > >> > >> Thanks, > >> Seifeddine. > > > > > > I think I'm okay with this. David mentioned enums, and I do think enums > > are useful in many places where you want types like this, but there's a > > simplicity in this that I can't deny, and I like it. > > I do like the simplicity of this. > > But - especially for floats and ints - the next level would be to allow > not only > > ``` > public function check(int -1|0|1 $minusOneThroughOne) > ``` > > but also something like > > ``` > public function check(int -1..1 $minusOneThrougOne) > ``` > > which would then also allow > > ``` > public function check (int 1..PHP_INT_MAX $positiveInt) > ``` > > would that also be something to be considered? It seems like a logical > alternate option to not have to add every value literally to the option > list... > > Cheers > > Andreas > -- > ,,, > (o o) > +---------------------------------------------------------ooO-(_)-Ooo-+ > | Andreas Heigl | > | mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" | > | https://andreas.heigl.org | > +---------------------------------------------------------------------+ > | https://hei.gl/appointmentwithandreas | > +---------------------------------------------------------------------+ > | GPG-Key: https://hei.gl/keyandreasheiglorg | > +---------------------------------------------------------------------+ >
Hi Andreas, Thanks! Your first example, `-1|0|1`, is already exactly what this RFC does: a union of three int literals (you don't even need the `int` prefix, just `function check(-1|0|1 $x): void {}`). The second part, ranges like `-1..1` or `1..PHP_INT_MAX`, is a genuinely different feature. And you're right that it's the logical next step: enumerating every value doesn't scale, and something like `1..PHP_INT_MAX` can't be written as a union at all. I'd keep it out of this RFC, though. A range isn't a set of literals, it's a constraint that has to be checked with a bounds predicate ($v
>= lo && $v <= hi), and it brings its own design questions: inclusive
vs exclusive bounds, open-ended ranges (1.., ..10), what's allowed as a bound (plain constants? expressions like PHP_INT_MAX?), and how coercion behaves at the edges. That's really a refinement-type feature sitting on top of this one. So it's a great follow-up, and a natural one once literals exist, but I think it deserves its own proposal rather than being folded in here. Cheers, Seifeddine

Jordi Kroon

78 days ago
On 15/06/2026 3:22 am, Seifeddine Gmati wrote:
> Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314 > > Thanks, > Seifeddine.
I mainly see the benefit here as being able to be more strict about what a function actually accepts and returns, in cases where a dedicated enum would be overkill. Two things I'd like to understand better: Does the RFC allow referencing constants in type positions, or only raw literal values? ``` function foo(STATUS_ACTIVE|STATUS_INACTIVE $sort): void {} ``` ``` What about enum values? For example: function bar(Status::Active->value $status): void {} // or simply as function bar(Status::Active $status): void {} ``` Also, I'm not really a fan of mixing literal types with unions. ``` function foo(int|'bar' $param): void {} ``` To me, mixing these makes it harder to reason about what a function actually accepts. The whole point of literal types is to be (more) precise but the moment you throw a wide type like int into the union, that precision goes out the window. If a function takes int|'bar', what does that really tell me? It feels like it defeats the purpose.
-- Regards, Jordi Kroon

Seifeddine Gmati

78 days ago
Hi Jordi,
> Does the RFC allow referencing constants in type positions, or only raw
literal values?
> > ``` > function foo(STATUS_ACTIVE|STATUS_INACTIVE $sort): void {} > ```
Regarding your first point, referencing constants in type positions is not supported. This RFC focuses specifically on scalar literals. A constant access is not a literal itself, and allowing something like `FOO` in a type position could cause ambiguity; at first sight, it's unclear whether it refers to a class name or a constant. While casing might suggest one or the other to a human, PHP allows both classes and constants to use any casing style, which could lead to confusion. This would also be a BC break, as the following is currently permitted in PHP: ``` const Foo = 1; class Foo {} function bar(Foo $hello): void {} ``` If `Foo` were changed to mean `1` instead of an instance of `Foo` depending on the surrounding context, this would break existing applications.
> ``` > What about enum values? For example: > > function bar(Status::Active->value $status): void {} > // or simply as > function bar(Status::Active $status): void {} > ```
For the second point, using enum values is also out of scope. `Status::Active->value` is a runtime expression, not a literal: the engine would have to confirm that `Status` is an enum, that the case exists, and that it is backed before reading `->value`. And `Status::Active` by itself is an object, a single enum case, which would be a single-case type, a separate feature from scalar literals. In both situations, requiring the enum type itself is usually the better fit.
> Also, I'm not really a fan of mixing literal types with unions. > > ``` > function foo(int|'bar' $param): void {} > ```
Finally, regarding mixing literal types with wider types like `int|'bar'`, this is supported by design. I believe restricting what may appear in a union is the wrong approach; PHP should treat types uniformly, with exceptions only for types that aren't value types and so are meaningless in a union (`void`, `never`) or that are redundant (`mixed|T`, or a literal already covered by its base type like `int|1`). If a union like `float|'cold'|'hot'` lets a user express a real requirement for their API, the type system should allow it. Cheers, Seifeddine.

Sarina Corrigan

78 days ago
It may be worth mentioning that within the Pattern Matching RFC future scope, and mentioned a couple times within the discussion thread for the RFC, there is a similar proposal that would allow for this without using specific literal types. It's outlined in https://github.com/Crell/php-rfcs/blob/master/pattern-matching/future.md under "Parameter or return guards" It would allow for: ``` function setLogLevel (string $level is 'debug' | 'info' | 'warning' | 'error'): void {} ``` Of course, I don't think that a potential future scope of an in-draft RFC is reason to dismiss a more direct implementation of literal scalar types, but it may be useful to compare other ways we could achieve the same functionality. I personally find pattern matching within parameter/return types more versatile while keeping direct typing system more simplified. Specifically for a range feature that Ben Ramsey brought up, pattern matching for parameters seems much more appropriate. All that being said, I would gladly welcome literal scalar types. On Sun, Jun 14, 2026, 21:24 Seifeddine Gmati <azjezz@carthage.software> wrote:

Lars Nielsen

78 days ago
> Den 15. jun. 2026 kl. 19.23 skrev Sarina Corrigan <sarina.corrigan@gmail.com>: > >  > It may be worth mentioning that within the Pattern Matching RFC future scope, and mentioned a couple times within the discussion thread for the RFC, there is a similar proposal that would allow for this without using specific literal types. > > It's outlined in https://github.com/Crell/php-rfcs/blob/master/pattern-matching/future.md under "Parameter or return guards" > > It would allow for: > > ``` > function setLogLevel (string $level is 'debug' | 'info' | 'warning' | 'error'): void {} > ``` > > Of course, I don't think that a potential future scope of an in-draft RFC is reason to dismiss a more direct implementation of literal scalar types, but it may be useful to compare other ways we could achieve the same functionality. I personally find pattern matching within parameter/return types more versatile while keeping direct typing system more simplified. Specifically for a range feature that Ben Ramsey brought up, pattern matching for parameters seems much more appropriate. > > All that being said, I would gladly welcome literal scalar types. > >> On Sun, Jun 14, 2026, 21:24 Seifeddine Gmati <azjezz@carthage.software> wrote: >> Hello Internals, >> >> I'd like to start the discussion on a new RFC adding literal scalar >> types to PHP. >> >> - RFC: https://wiki.php.net/rfc/literal_scalar_types >> - Implementation: https://github.com/php/php-src/pull/22314 >> >> Thanks, >> Seifeddine.
Hi, This sounds very promising. But I would be confused about receiving a TypeError when just the value of the parameter is wrong. As described in the RFC if I send 4 to a parameter that could only be 1, 2 or 3. I would expect a TypeError if I sent “abc” ? Kind regards Lars Nielsen

Seifeddine Gmati

77 days ago
On Mon, 15 Jun 2026 at 19:25, Lars Nielsen <lars@lfweb.dk> wrote:
> > > Den 15. jun. 2026 kl. 19.23 skrev Sarina Corrigan <sarina.corrigan@gmail.com>: > >  > It may be worth mentioning that within the Pattern Matching RFC future scope, and mentioned a couple times within the discussion thread for the RFC, there is a similar proposal that would allow for this without using specific literal types. > > It's outlined in https://github.com/Crell/php-rfcs/blob/master/pattern-matching/future.md under "Parameter or return guards" > > It would allow for: > > ``` > function setLogLevel (string $level is 'debug' | 'info' | 'warning' | 'error'): void {} > ``` > > Of course, I don't think that a potential future scope of an in-draft RFC is reason to dismiss a more direct implementation of literal scalar types, but it may be useful to compare other ways we could achieve the same functionality. I personally find pattern matching within parameter/return types more versatile while keeping direct typing system more simplified. Specifically for a range feature that Ben Ramsey brought up, pattern matching for parameters seems much more appropriate. > > All that being said, I would gladly welcome literal scalar types. > > On Sun, Jun 14, 2026, 21:24 Seifeddine Gmati <azjezz@carthage.software> wrote: >> >> Hello Internals, >> >> I'd like to start the discussion on a new RFC adding literal scalar >> types to PHP. >> >> - RFC: https://wiki.php.net/rfc/literal_scalar_types >> - Implementation: https://github.com/php/php-src/pull/22314 >> >> Thanks, >> Seifeddine. > > > Hi, > This sounds very promising. But I would be confused about receiving a TypeError when just the value of the parameter is wrong. > > As described in the RFC if I send 4 to a parameter that could only be 1, 2 or 3. I would expect a TypeError if I sent “abc” ? > > Kind regards > Lars Nielsen
Hi Lars, I think the confusion comes from treating "type" and "value" as two separate things, which is the usual mental model. Literal scalar types deliberately blur that line: each value is itself a type, a unit type containing exactly one value. So `1|2|3` is not "an int that happens to be restricted", it is the union of three unit types `1`, `2` and `3`. Under that view, both `4` and `"abc"` fail for the same reason: neither is a member of the declared type. There is no separate "the value is wrong" category, it is all type membership, so a `TypeError` is the consistent outcome. It is the same thing that already happens with the `true` type today: passing `false` to a `true` parameter is a `TypeError`, even though both are booleans.

Seifeddine Gmati

77 days ago
On Mon, 15 Jun 2026 at 18:20, Sarina Corrigan <sarina.corrigan@gmail.com> wrote:
> > It may be worth mentioning that within the Pattern Matching RFC future
scope, and mentioned a couple times within the discussion thread for the RFC, there is a similar proposal that would allow for this without using specific literal types.
> > It's outlined in
https://github.com/Crell/php-rfcs/blob/master/pattern-matching/future.md under "Parameter or return guards"
> > It would allow for: > > ``` > function setLogLevel (string $level is 'debug' | 'info' | 'warning' |
'error'): void {}
> ``` > > Of course, I don't think that a potential future scope of an in-draft RFC
is reason to dismiss a more direct implementation of literal scalar types, but it may be useful to compare other ways we could achieve the same functionality. I personally find pattern matching within parameter/return types more versatile while keeping direct typing system more simplified. Specifically for a range feature that Ben Ramsey brought up, pattern matching for parameters seems much more appropriate.
> > All that being said, I would gladly welcome literal scalar types. > > On Sun, Jun 14, 2026, 21:24 Seifeddine Gmati <azjezz@carthage.software>
wrote:
>> >> Hello Internals, >> >> I'd like to start the discussion on a new RFC adding literal scalar >> types to PHP. >> >> - RFC: https://wiki.php.net/rfc/literal_scalar_types >> - Implementation: https://github.com/php/php-src/pull/22314 >> >> Thanks, >> Seifeddine.
Hi Sarina, Thanks for the pointer, that is a good read. I don't think literal scalar types conflict with the pattern matching future scope at all. The way I see it, a pattern is, or at least should be, a type. `$foo is Foo { x: 10 }` is really asking "does `$foo` have the type `Foo` with `x` equal to `10`", and I would happily see us later allow `Foo { x: 10 }` as a type on its own. The one thing a pattern adds over a type is binding: capturing a value in place of a sub-type, e.g. ``` if ($foo is Foo { x: $x }) { /* $x is bound here */ } ``` which a plain type declaration cannot do. On the specific syntax in that document: ``` function setLogLevel(string $level is 'debug' | 'info' | 'warning' | 'error'): void {} ``` reads as redundant to me. The `string` contributes nothing once the value set is given, so with literal scalar types the same intent is simply: ``` function setLogLevel('debug' | 'info' | 'warning' | 'error' $level): void {} ``` So I see the two as complementary rather than competing: literal types provide the value-as-type building block, and pattern matching can build on top of it for binding and destructuring. Glad to hear you would welcome the feature. Cheers, Seifeddine

Tim Düsterhus

78 days ago
Hi On 6/15/26 03:22, Seifeddine Gmati wrote:
> I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314
I have given the RFC a quick first review pass and would suggest to leave out support for float literals. From a conceptual perspective floats are much closer to being continuous values than they are to discrete values and picking individual values from a continuous range is typically not all that useful. Support for floats is also going to invite the usual confusion about implicit rounding: function tenths( 0.0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9 $tenth ): void { var_dump($tenth); } where `tenths(0.1 + 0.2)` will result in a TypeError. The RFC is also unclear what values are valid floating point literals. As an example, is `4e3` a valid float literal? Is NAN a valid float literal? Best regards Tim Düsterhus

Seifeddine Gmati

77 days ago
On Mon, 15 Jun 2026 at 19:43, Tim Düsterhus <tim@bastelstu.be> wrote:
> > Hi > > On 6/15/26 03:22, Seifeddine Gmati wrote: > > I'd like to start the discussion on a new RFC adding literal scalar > > types to PHP. > > > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > > - Implementation: https://github.com/php/php-src/pull/22314 > > I have given the RFC a quick first review pass and would suggest to > leave out support for float literals. From a conceptual perspective > floats are much closer to being continuous values than they are to > discrete values and picking individual values from a continuous range is > typically not all that useful. > > Support for floats is also going to invite the usual confusion about > implicit rounding: > > function tenths( > 0.0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9 $tenth > ): void { var_dump($tenth); } > > where `tenths(0.1 + 0.2)` will result in a TypeError. The RFC is also > unclear what values are valid floating point literals. As an example, is > `4e3` a valid float literal? Is NAN a valid float literal? > > Best regards > Tim Düsterhus
Hi Tim, I think I might agree here, and others have raised the same concern on Discord. Dropping float literals for now simplifies the RFC, so I am inclined to do that, though I would like to hear what others think before removing them. For what it is worth, I am personally fine with `tenths(0.1 + 0.2)` failing to match `0.3`. This is not new behaviour: `0.1 + 0.2 == 0.3` is already false, and a `match (0.1 + 0.2)` already skips a `0.3` arm for exactly the same reason. So a literal float type behaves consistently with comparison and `match`, rather than introducing a new surprise. On your concrete questions: `4e3` is a valid float literal and works today; it normalizes to `4000.0` (the type also stringifies as `4000.0`). `NAN` and `INF`, on the other hand, are not literals but constant identifiers that go through constant resolution. They are out of scope for the same reason `FOO` is above: `NAN $foo` could just as well mean a class named `NAN`.

Larry Garfield

77 days ago
On Mon, Jun 15, 2026, at 2:45 PM, Seifeddine Gmati wrote:
> Hi Tim, > > I think I might agree here, and others have raised the same concern on > Discord. Dropping float literals for now simplifies the RFC, so I am > inclined to do that, though I would like to hear what others think > before removing them.
We debated allowing float as a backing type for enums, and eventually decided against it for a similar reason: It's not stable or discrete enough to be useful, and no other language we looked at supported them. I'm still undecided on the RFC as a whole (I need to fully read it first), but would support limiting the literals to just int and string. --Larry Garfield

Tim Düsterhus

77 days ago
Hi On 6/15/26 21:45, Seifeddine Gmati wrote:
> For what it is worth, I am personally fine with `tenths(0.1 + 0.2)` > failing to match `0.3`. This is not new behaviour: `0.1 + 0.2 == 0.3` > is already false, and a `match (0.1 + 0.2)` already skips a `0.3` arm > for exactly the same reason. So a literal float type behaves > consistently with comparison and `match`, rather than introducing a > new surprise.
Yes, it is consistent with the existing behavior, but I don't think that this makes it any less confusing. And for this specific proposal, adding support for floats would be a deliberate decision rather than just something that naturally follows from “existing === semantics” as with `match()` which works on two values rather than values + types.
> On your concrete questions: `4e3` is a valid float literal and works > today; it normalizes to `4000.0` (the type also stringifies as > `4000.0`). `NAN` and `INF`, on the other hand, are not literals but > constant identifiers that go through constant resolution. They are out > of scope for the same reason `FOO` is above: `NAN $foo` could just as > well mean a class named `NAN`.
Yes, I'm aware (based on a look at the implementation). This was intended to be a subtle note that this is not explicitly spelled out in the RFC. The RFC text should comprehensively explain the behavior all possible edge cases and ambiguities so that folks can form an educated opinion based on the RFC text alone without needing to be able to understand the implementation. Writing that out, I also notice that the “Ecosystem” sub-section is missing from the “RFC Impact” section (and the “To Existing Extensions ” sub-subsection would probably also be useful to know) and the voting widget is also missing. Best regards Tim Düsterhus

Seifeddine Gmati

77 days ago
On Mon, 15 Jun 2026 at 21:32, Tim Düsterhus <tim@bastelstu.be> wrote:
> > Hi > > On 6/15/26 21:45, Seifeddine Gmati wrote: > > For what it is worth, I am personally fine with `tenths(0.1 + 0.2)` > > failing to match `0.3`. This is not new behaviour: `0.1 + 0.2 == 0.3` > > is already false, and a `match (0.1 + 0.2)` already skips a `0.3` arm > > for exactly the same reason. So a literal float type behaves > > consistently with comparison and `match`, rather than introducing a > > new surprise. > > Yes, it is consistent with the existing behavior, but I don't think that > this makes it any less confusing. And for this specific proposal, adding > support for floats would be a deliberate decision rather than just > something that naturally follows from “existing === semantics” as with > `match()` which works on two values rather than values + types. > > > On your concrete questions: `4e3` is a valid float literal and works > > today; it normalizes to `4000.0` (the type also stringifies as > > `4000.0`). `NAN` and `INF`, on the other hand, are not literals but > > constant identifiers that go through constant resolution. They are out > > of scope for the same reason `FOO` is above: `NAN $foo` could just as > > well mean a class named `NAN`. > > Yes, I'm aware (based on a look at the implementation). This was > intended to be a subtle note that this is not explicitly spelled out in > the RFC. > > The RFC text should comprehensively explain the behavior all possible > edge cases and ambiguities so that folks can form an educated opinion > based on the RFC text alone without needing to be able to understand the > implementation. > > Writing that out, I also notice that the “Ecosystem” sub-section is > missing from the “RFC Impact” section (and the “To Existing Extensions > ” sub-subsection would probably also be useful to know) and the voting > widget is also missing. > > Best regards > Tim Düsterhus
Hi Tim, That is fair. Float support should be a deliberate choice rather than something that rides in on existing `===` semantics; the `match` analogy only goes so far, since `match` compares two values whereas this compares a value against a type. You are also right that the RFC text has to stand on its own. I will expand it to spell out the edge cases explicitly, including: - Which numeric forms are accepted and how they normalize. Hexadecimal, octal, binary and underscore-separated integer literals (`0x1A`, `0o17`, `0b101`, `1_000`) all canonicalize to their value, and `4e3` is a valid float literal normalizing to `4000.0`. - That `NAN` and `INF` are constants, not literals, and so are not accepted, for the same reason a bare `FOO` is not. - String literal handling: single versus double quotes, escape resolution, and the rejection of interpolation. I will also add the missing "Ecosystem" and "To Existing Extensions" subsections under "RFC Impact", and the voting widget. On voting: rather than a single yes/no, would it make sense to split it so each decision can stand on its own? 1. Add support for literal string types. (2/3) 2. Add support for literal integer types. (2/3) 3. Add support for literal float types. (2/3) 4. Coercion behaviour: coerce to the base type before checking membership (as the RFC currently describes), or always require an identity match. (1/2) The last one is worth surfacing in particular. `true`, `false` and `null` do not coerce at all today, even in coercive mode: passing `1` to a `true` parameter is a `TypeError`, not a coercion to `true`. If we want literal scalars to be consistent with the existing value types, an identity match is arguably the more natural behaviour, so I would rather put it to the list than bake it in. Best regards, Seifeddine

Bob Weinand

77 days ago
Hey Seifeddine,
> Am 15.06.2026 um 03:22 schrieb Seifeddine Gmati <azjezz@carthage.software>: > > Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314 > > Thanks, > Seifeddine.
I think you are solving the wrong (or rather: only a specific subset of the) problem. What you _actually_ probably want is pattern support in type positions. Let me know if I'm wrong in my assumption here. I.e. basically support expressions as specified by https://wiki.php.net/rfc/pattern-matching in property and function argument/return positions. Which does this, and ranges and everything else you'd need. Which is a worthwhile addition, but we should first get pattern-matching done, then we can do one RFC broadening the applicability of patterns. Bob

Seifeddine Gmati

77 days ago
On Mon, Jun 15, 2026, 11:59 PM Bob Weinand <bobwei9@hotmail.com> wrote:
> Hey Seifeddine, > > > Am 15.06.2026 um 03:22 schrieb Seifeddine Gmati <azjezz@carthage.software > >: > > > > Hello Internals, > > > > I'd like to start the discussion on a new RFC adding literal scalar > > types to PHP. > > > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > > - Implementation: https://github.com/php/php-src/pull/22314 > > > > Thanks, > > Seifeddine. > > I think you are solving the wrong (or rather: only a specific subset of > the) problem. What you _actually_ probably want is pattern support in type > positions. Let me know if I'm wrong in my assumption here. > > I.e. basically support expressions as specified by > https://wiki.php.net/rfc/pattern-matching in property and function > argument/return positions. Which does this, and ranges and everything else > you'd need. > > Which is a worthwhile addition, but we should first get pattern-matching > done, then we can do one RFC broadening the applicability of patterns. > > Bob
Hi Bob, My interest is not pattern matching; my focus is on the type system and expanding it to be more expressive. My main motivation is that static analysis tools (like PHPStan, Psalm, and Mago) already do many things that PHP itself cannot, and I believe those features belong in the engine. Literal types serve as a fundamental building block for future type system features such as array shapes, tuples, and potentially even conditional types. While this RFC works alongside the pattern matching proposal by Larry and Ilija, neither requires the other. The two are unrelated for several reasons. So to answer your question, no, I am not looking for pattern matching. I see these as two distinct features that address different needs within the language. Cheers, Seifeddine.

Sarina Corrigan

77 days ago
On Mon, Jun 15, 2026, 20:05 Seifeddine Gmati <azjezz@carthage.software> wrote:
> While this RFC works alongside the pattern matching proposal by Larry and > Ilija, neither requires the other. The two are unrelated for several > reasons. > > So to answer your question, no, I am not looking for pattern matching. I > see these as two distinct features that address different needs within the > language. >
I don't believe they are addressing entirely different needs. I believe the feature that static analysis tools support is pattern matching more than it is typing. The question for me becomes whether patterns be represented through types, or whether they should be matched against values. Strictly denoting types and validating patterns are two separate concerns. One asks "What can I do with this?" while the other asks "Is this within expected bounds?". I am personally not against validating patterns within type declarations, as is supported in Typescript, Scala (I believe), and to an extent PHP with false|true. But I do believe they are different concerns. I also think that, this being a form of pattern matching, if approved would set a direction for the pattern matching RFC to treat patterns more closely as types than assertions and guards (as their RFC currently proposes). I am again not saying this is a bad thing, but it is worth acknowledging.

Seifeddine Gmati

77 days ago
On Tue, Jun 16, 2026, 1:31 AM Sarina Corrigan <sarina.corrigan@gmail.com> wrote:
> > > > On Mon, Jun 15, 2026, 20:05 Seifeddine Gmati <azjezz@carthage.software> > wrote: > >> While this RFC works alongside the pattern matching proposal by Larry and >> Ilija, neither requires the other. The two are unrelated for several >> reasons. >> >> So to answer your question, no, I am not looking for pattern matching. I >> see these as two distinct features that address different needs within the >> language. >> > > I don't believe they are addressing entirely different needs. I believe > the feature that static analysis tools support is pattern matching more > than it is typing. The question for me becomes whether patterns be > represented through types, or whether they should be matched against values. > > Strictly denoting types and validating patterns are two separate concerns. > One asks "What can I do with this?" while the other asks "Is this within > expected bounds?". I am personally not against validating patterns within > type declarations, as is supported in Typescript, Scala (I believe), and to > an extent PHP with false|true. But I do believe they are different concerns. > > I also think that, this being a form of pattern matching, if approved > would set a direction for the pattern matching RFC to treat patterns more > closely as types than assertions and guards (as their RFC currently > proposes). I am again not saying this is a bad thing, but it is worth > acknowledging. >
Hi Sarina, I don't think we disagree here. I already said above that I think pattern matching should match against types with variable binding; this actually makes pattern matching easier because to expand it, you just have to expand the type system, and patterns get expanded for free. However, I think this is a discussion for the pattern matching RFC, not for the literal types RFC. Cheers, Seifeddine.

Larry Garfield

77 days ago
On Mon, Jun 15, 2026, at 7:44 PM, Seifeddine Gmati wrote:
>> On Mon, Jun 15, 2026, 20:05 Seifeddine Gmati <azjezz@carthage.software> wrote: >>> While this RFC works alongside the pattern matching proposal by Larry and Ilija, neither requires the other. The two are unrelated for several reasons. >>> >>> So to answer your question, no, I am not looking for pattern matching. I see these as two distinct features that address different needs within the language. >> >> I don't believe they are addressing entirely different needs. I believe the feature that static analysis tools support is pattern matching more than it is typing. The question for me becomes whether patterns be represented through types, or whether they should be matched against values. >> >> Strictly denoting types and validating patterns are two separate concerns. One asks "What can I do with this?" while the other asks "Is this within expected bounds?". I am personally not against validating patterns within type declarations, as is supported in Typescript, Scala (I believe), and to an extent PHP with false|true. But I do believe they are different concerns. >> >> I also think that, this being a form of pattern matching, if approved would set a direction for the pattern matching RFC to treat patterns more closely as types than assertions and guards (as their RFC currently proposes). I am again not saying this is a bad thing, but it is worth acknowledging. > > Hi Sarina, > > I don't think we disagree here. I already said above that I think > pattern matching should match against types with variable binding; this > actually makes pattern matching easier because to expand it, you just > have to expand the type system, and patterns get expanded for free. > > However, I think this is a discussion for the pattern matching RFC, not > for the literal types RFC. > > Cheers, > Seifeddine.
This is interesting. We've approached patterns as a coincidental superset of the type system. That is, (almost) any type declaration is a valid pattern, but not because patterns are types; because we've implemented patterns to mirror types. Patterns *as* types would be a completely different approach. I can see the appeal, but there's a number of issues there: 1. Performance. Patterns do a lot more work than a type check right now. If patterns cropped up in function signatures all over the place, that would have a notable, though currently unclear and hard to predict, impact on performance. (Far more inconsistent than, say, reified generics would have...) 2. Complexity. Expanding the type system to full patterns seems like it would be... hard. And possibly internal-API breaking. I could be completely wrong here, but it sounds like a fairly drastic change. 3. Repeatability. For this to work, I think it would *have* to include type definitions, which have always hit a discussion wall in the past as no one can agree on their design. Something like: type positiveInt = int & >0; type UserId = int; type order = 'asc'|'desc'; function foo(positiveInt $val) { ... } // Guaranteed to be an integer greater than 0 function bar(UserId $id) { ... } // Would this accept a positiveInt? Debatable. There's a sizable rabbit hole here. One could argue even that for scalar literal types we should have proper type defs. 4. Variable binding. Let me be clear: Variable binding *is* the feature that makes pattern matching worthwhile. It's the reason we started working on it; we believe it is a prerequisite for properly implementing ADTs/"tagged enums." So even if patterns become types, there will still be a need for an extended variable binding syntax that works only inline, not as part of a type declaration. What the complexity impact of that would be, I have no idea. If there is a consensus to go down this rabbit hole, I am not opposed to it. But it's a very deep rabbit hole, and exploring it would guarantee that neither patterns nor scalar literal types make it into this version. It would probably also entail 3-4 RFCs total, all of which would be kind of half-arsed on their own because they're part of a set; and PHP has been extremely, *extremely* bad at coordinating and working with that in the past. (Maybe a place for working groups?) --Larry Garfield

Bob Weinand

77 days ago
Hey Larry,
> Am 16.06.2026 um 17:16 schrieb Larry Garfield <larry@garfieldtech.com>: > > On Mon, Jun 15, 2026, at 7:44 PM, Seifeddine Gmati wrote: > >>> On Mon, Jun 15, 2026, 20:05 Seifeddine Gmati <azjezz@carthage.software> wrote: >>>> While this RFC works alongside the pattern matching proposal by Larry and Ilija, neither requires the other. The two are unrelated for several reasons. >>>> >>>> So to answer your question, no, I am not looking for pattern matching. I see these as two distinct features that address different needs within the language. >>> >>> I don't believe they are addressing entirely different needs. I believe the feature that static analysis tools support is pattern matching more than it is typing. The question for me becomes whether patterns be represented through types, or whether they should be matched against values. >>> >>> Strictly denoting types and validating patterns are two separate concerns. One asks "What can I do with this?" while the other asks "Is this within expected bounds?". I am personally not against validating patterns within type declarations, as is supported in Typescript, Scala (I believe), and to an extent PHP with false|true. But I do believe they are different concerns. >>> >>> I also think that, this being a form of pattern matching, if approved would set a direction for the pattern matching RFC to treat patterns more closely as types than assertions and guards (as their RFC currently proposes). I am again not saying this is a bad thing, but it is worth acknowledging. >> >> Hi Sarina, >> >> I don't think we disagree here. I already said above that I think >> pattern matching should match against types with variable binding; this >> actually makes pattern matching easier because to expand it, you just >> have to expand the type system, and patterns get expanded for free. >> >> However, I think this is a discussion for the pattern matching RFC, not >> for the literal types RFC. >> >> Cheers, >> Seifeddine. > > This is interesting. We've approached patterns as a coincidental superset of the type system. That is, (almost) any type declaration is a valid pattern, but not because patterns are types; because we've implemented patterns to mirror types. > > Patterns *as* types would be a completely different approach. I can see the appeal, but there's a number of issues there: > > 1. Performance. Patterns do a lot more work than a type check right now. If patterns cropped up in function signatures all over the place, that would have a notable, though currently unclear and hard to predict, impact on performance. (Far more inconsistent than, say, reified generics would have...)
The performance impact is certainly lower than manually validating all over the place. Also, this would be a good motivation to invest into moving the type checks for known functions onto the caller side, and eliding them completely if the variable is unmodified. By now we have knowledge when variables can definitely not leak (are no references, no varvars or extract etc. are used) and check whether and what they are being assigned during the lifetime of the function. In fact, using "int & < 10 & > 1" would give pretty strong bounds for opcaches data flow analysis to use too, giving much better hints at what integers will never be promoted to float, reducing the amount of guards necessary in JIT for example. An int which never can be < 0 for example saves a bounds check in one direction as well when working with packed arrays. I.e. depending on the patterns and their integration into opcache quite a bit of potential could be unlocked. But yes, you can always find worst case performances.
> 2. Complexity. Expanding the type system to full patterns seems like it would be... hard. And possibly internal-API breaking. I could be completely wrong here, but it sounds like a fairly drastic change.
Why would it be particularly hard? We already have dedicated types APIs for stuff like type intersection and union. I don't think there would be a lot of impact.
> 3. Repeatability. For this to work, I think it would *have* to include type definitions, which have always hit a discussion wall in the past as no one can agree on their design. Something like: > > type positiveInt = int & >0; > type UserId = int; > type order = 'asc'|'desc'; > > function foo(positiveInt $val) { ... } // Guaranteed to be an integer greater than 0 > > function bar(UserId $id) { ... } // Would this accept a positiveInt? Debatable. > > There's a sizable rabbit hole here. One could argue even that for scalar literal types we should have proper type defs.
From the perspective of an autoloader, a type name is just like any other class name. This is more composers problem to solve (they possibly could include a per-namespace fallback to a default.php or something, where you'd define all types of a namespace in). We should have type names eventually, but I consider them separate from the basic pattern feature. Should be done, but separate RFC with its own concerns, and I also suppose a pretty simple RFC actually!
> 4. Variable binding. Let me be clear: Variable binding *is* the feature that makes pattern matching worthwhile. It's the reason we started working on it; we believe it is a prerequisite for properly implementing ADTs/"tagged enums." So even if patterns become types, there will still be a need for an extended variable binding syntax that works only inline, not as part of a type declaration. What the complexity impact of that would be, I have no idea.
I don't think we would need to include binding in a first version of this.
> If there is a consensus to go down this rabbit hole, I am not opposed to it. But it's a very deep rabbit hole, and exploring it would guarantee that neither patterns nor scalar literal types make it into this version. It would probably also entail 3-4 RFCs total, all of which would be kind of half-arsed on their own because they're part of a set; and PHP has been extremely, *extremely* bad at coordinating and working with that in the past. (Maybe a place for working groups?)
I also don't think that pattern matching needs particular changes to fit this. The syntax of pattern matching is pretty fine and could be just 1:1 translated to function args.
> --Larry Garfield
Thanks, Bob

Seifeddine Gmati

76 days ago
On Mon, 15 Jun 2026 at 02:22, Seifeddine Gmati <azjezz@carthage.software> wrote:
> > Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314 > > Thanks, > Seifeddine.
Hi all, Based on the discussion so far, I've updated the Literal Scalar Types RFC to v0.2: https://wiki.php.net/rfc/literal_scalar_types What changed: - Strict matching is now the proposed default instead of coercion. A literal type would never coerce, in either typing mode, exactly how true, false and null already behave: passing 1 where the type is true is a TypeError even with strict_types disabled. The goal is a single rule: a type whose identity is one value matches only that value. - The vote is split into three: a 2/3 vote to add int and string literals, a separate 2/3 vote to add float literals, and a simple-majority vote for the matching semantics (strict vs coercive). - Float support is now its own, optional question. The RFC discusses the precision issue in full (0.3 won't match 0.1 + 0.2, because 0.1 + 0.2 === 0.3 is already false today) along with the arguments for and against, mirroring why floats were left out of enum backing types. - Documented the accepted literal syntax (0x.., 0b.., octal, 4e3, 1_000, and so on) and why named constants, INF and NAN are excluded. - Added RFC Impact notes for extensions and tooling (parsers, IDEs, formatters, linters, static analysers), plus a Future Scope section covering array shapes/tuples and integer range types. Thanks, Seifeddine.

Seifeddine Gmati

64 days ago
On Mon, 15 Jun 2026 at 02:22, Seifeddine Gmati <azjezz@carthage.software> wrote:
> > Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314 > > Thanks, > Seifeddine.
Hello Interansl, I'm planning to take this RFC to a vote next week. I just wanted to check in since there hasn't been any discussion after the last update. Any concerns regarding this? Cheers, Seifeddine.

Tim Düsterhus

64 days ago
Hi Am 2026-06-29 13:52, schrieb Seifeddine Gmati:
> I'm planning to take this RFC to a vote next week. I just wanted to > check in since there hasn't been any discussion after the last update. > > Any concerns regarding this?
FWIW: The list is *extremely* busy right now with (new) proposals that I feel would do well waiting until after the feature freeze. Personally I'm having troubles keeping up with all the discussion, RFCs, and code review. I assume that others feel similar, so that would explain the absence of feedback. I've given the RFC another full read and have the following remarks: 1. The RFC specifies “Integer literals, with an optional leading minus sign: 1, 0, -1.”, are leading `+` signs also legal? What about multiple signs or spaces? From what I see the tokenizer does not currently support a sign for number literals at all, so `-1` is tokenized as a (unary) minus applied to `1`, so the behavior is not immediately obvious. 2.
> PHP's direction of travel. The ecosystem has moved steadily towards > strict_types=1
I don't believe this statement is accurate. What has happened is “make the non-strict_types mode more predictable” to the extent that `strict_types=1` has actually become less safe. From what I see in the ecosystem, `strict_types=1` is also not widely used. I believe that “making literal types strict” is the correct choice (for the consistency reasons), but I don't think this specific statement is backed up by evidence. 3.
> A literal denotes a single value and carries no interface, so it cannot > take part in an intersection type. 1&Foo is a parse error.
I don't think making this a parser error is in the interest of “providing useful error messages”. This should be a compiler error that clearly indicates the issue, just like `function foo(true&Foo $foo) { }` is a compiler error. 4. For the “Ecosystem” impact: I believe there is also an impact to anything consuming types from Reflection (such as mappers). This is implied with the “Reflection impact”, but could be spelled out more explicitly. ------------------ As for the proposal itself: As already indicated in my previous emails, I don't see much value in supporting literal types for newly written code. I very likely won't vote in favor, but am still undecided about “Abstain” and “No”. I'm also concerned about folks shooting themselves in the foot with type checking performance for “wide unions of literal types”. It might make sense to provide a dedicated performance section in the RFC. From what I see using a quick glance, the current implementation is O(n) left-to-right, which means that passing 9 to `0|1|2|3|4|5|6|7|8|9` will be slower than passing 0. I understand this issue already exists with union types, but unions between two different class types are comparatively rare and unions wider then 2 are even rarer. Best regards Tim Düsterhus

Seifeddine Gmati

63 days ago
On Mon, 29 Jun 2026 at 19:18, Tim Düsterhus <tim@bastelstu.be> wrote:
> > FWIW: The list is *extremely* busy right now with (new) proposals that I > feel would do well waiting until after the feature freeze. Personally > I'm having troubles keeping up with all the discussion, RFCs, and code > review. I assume that others feel similar, so that would explain the > absence of feedback.
Hi Tim, Thanks for the read, and no worries about the timing. The list is clearly busy right now, so I appreciate you getting to it. All four remarks are addressed in v0.3, which I've just pushed:
> 1. The RFC specifies "Integer literals, with an optional leading minus > sign: 1, 0, -1.", are leading `+` signs also legal? What about multiple > signs or spaces?
A single leading unary `+` or `-` is accepted, so both `-1` and `+1` are legal, and `+1` denotes the same type as `1`.
> 2. I don't believe this statement is accurate. ... I believe that > "making literal types strict" is the correct choice (for the consistency > reasons), but I don't think this specific statement is backed up by > evidence.
Agreed, and I've dropped the claim entirely. The case for strict matching now rests only on consistency with `null`/`true`/`false`, and on the single rule that a type whose identity is one value matches only that value.
> 3. I don't think making this a parser error is in the interest of > "providing useful error messages". This should be a compiler error ...
Done, it's a compile-time error now, consistent with how `function foo(true&Foo $foo) {}` is handled.
> 4. For the "Ecosystem" impact: I believe there is also an impact to > anything consuming types from Reflection (such as mappers).
Spelled out explicitly now.
> I'm also concerned about folks shooting themselves in the foot with type > checking performance for "wide unions of literal types". It might make > sense to provide a dedicated performance section in the RFC.
Added. It states plainly that the membership check is O(n) left-to-right, uses your exact example (passing `9` to `0|1|2|3|4|5|6|7|8|9` scans all ten members, while `0` returns immediately), and notes that although this is the same behaviour every union type already has, literal unions invite much larger member counts, so the footgun is more reachable here. That example also prompted a Future Scope note, which I've added. If integer range types were later introduced, the engine can collapse a contiguous run like `0|1|...|9` into a single range `0..9` and check it with two bound comparisons instead of a linear scan, turning the wide-union cost into O(number of ranges). This is what we already do in Mago, so I'm fairly confident it will carry over to the engine. See https://github.com/carthage-software/mago/blob/14bf9e84884117ff385fa4ebfd90dd08622e7359/crates/codex/src/ttype/atomic/scalar/int.rs#L818-L958 <https://github.com/carthage-software/mago/blob/14bf9e84884117ff385fa4ebfd90dd08622e7359/crates/codex/src/ttype/atomic/scalar/int.rs#L818-L958> Note: The implementation has not been updated, I will address it later when/if the RFC is accepted Best regards, Seifeddine

Tim Düsterhus

63 days ago
Hi Am 2026-06-30 18:14, schrieb Seifeddine Gmati:
>> 1. The RFC specifies "Integer literals, with an optional leading minus >> sign: 1, 0, -1.", are leading `+` signs also legal? What about >> multiple >> signs or spaces? > > A single leading unary `+` or `-` is accepted, so both `-1` and `+1` > are > legal, and `+1` denotes the same type as `1`.
I was confused for a bit, because you didn't adjust the quoted section (https://wiki.php.net/rfc/literal_scalar_types#integer_literals) and instead only adjusted the “Accepted literal syntax” section, which is misleading / makes the RFC internally inconsistent. It probably makes sense to merge the “Accepted literal syntax” section into the three sections above as appropriate.
>> I'm also concerned about folks shooting themselves in the foot with >> type >> checking performance for "wide unions of literal types". It might make >> sense to provide a dedicated performance section in the RFC. > > Added.
Thanks that looks good to me. ------- Except for the remark about the literal syntax above, I don't have any further comments to the RFC (text). Best regards Tim Düsterhus

Joerg Sowa

61 days ago
Hello, I don't like the current state of this RFC. There is no explanation of what problem it is trying to solve. It focuses only on the proposed solution, while the problem space itself has not been properly explored. Because of that, the RFC also lacks any discussion of alternatives, even though several have already been suggested in this discussion thread. The examples in the RFC don't add much value. Hardcoded literal types such as `function f(1|2|3 $x): int { return $x; }` are generally considered an anti-pattern. Enums were specifically designed for these kinds of cases. However, the RFC does not explain why enums are insufficient for the problem it is trying to address (which, again, is never clearly defined). The performance section is also incomplete, as it does not compare the proposed feature against enums. Kind regards, Jorg

Nick

59 days ago
On 15.06.26 08:22, Seifeddine Gmati wrote:
> Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314 > > Thanks, > Seifeddine.
Hey Seifeddine, I went through the RFC and the implementation tests. A few thoughts... 1) if I did not miss anything the empty string case is not covered. Would `""` be legal? Any thoughts on providing ways to explicitly disallow empty strings? I don't think this should be further scope -- because we would need to make sure that we actually have a usable syntax to disallow empty strings. 2) having strict and non-strict mode in PHP is bad enough, IMO. Seeing this brought further, and combined with "literal" (literal should be literal) feels wrong -- a coerced `true` is de facto not a literal 1, it's a coerced `true`. I understand that you recommend strict and want to leave it to a vote, but I feel like the "Coercive matching (alternative)" section should make a stronger point against it, to discourage a tempting "always been like that" for a new concept. 3) the performance section is thin; solely addressing what Tim brought up. I am wondering, does introducing this have any implications for non-literal types? Having a benchmark would be nice. I also agree with Jorg, that a benchmark comparing the performance with enums (memory + speed) would be helpful. 4) the last part of the previous point extends to the general usefulness of the feature. I again have to agree with Jorg: it is not clear what problem it is trying to solve. Why not enums? It is a trend to get rid of stringy APIs, and magic strings and ints -- for reasons. But this feature will encourage bringing them back. With an enum you define it in one place (easier to refactor), with literal scalar types you repeat it in multiple places. Do we really want that?
-- Cheers Nick

Seifeddine Gmati

58 days ago
Hi Nick, Thanks for the feedback.
> 1) if I did not miss anything the empty string case is not covered. > Would `""` be legal? Any thoughts on providing ways to explicitly > disallow empty strings? I don't think this should be further scope -- > because we would need to make sure that we actually have a usable syntax > to disallow empty strings.
Empty strings are supported; "" is a valid literal string type. Regarding non-empty-string, that is a separate concept from literal types. However, this RFC provides the building blocks to express it in the future, through negated types (e.g., !""). There's also the option of doing refinement markers on base types (e.g., string[1..]), which IIRC Gina is working on or mentioned it somewhere. Example ( using literal types, negated types, and type aliases ): ```php type NonEmptyString = ! ""; function getNonEmptyString(): NonEmptyString { return 'ok'; // ok return ''; // err } ```
> 2) having strict and non-strict mode in PHP is bad enough, IMO. Seeing > this brought further, and combined with "literal" (literal should be > literal) feels wrong -- a coerced `true` is de facto not a literal 1, > it's a coerced `true`. I understand that you recommend strict and want > to leave it to a vote, but I feel like the "Coercive matching > (alternative)" section should make a stronger point against it, to > discourage a tempting "always been like that" for a new concept.
The RFC highlights that strict matching is already the established behavior for existing value-types in PHP, and is the preferred path. true, false, and null never coerce, even in coercive mode. Maintaining this for all literal types is the only way to ensure a consistent type system where a unit type matches exactly its identity, hence it is the preferred option.
> 3) the performance section is thin; solely addressing what Tim brought > up. I am wondering, does introducing this have any implications for > non-literal types? Having a benchmark would be nice. I also agree with > Jorg, that a benchmark comparing the performance with enums (memory + > speed) would be helpful.
This proposal has no performance impact on non-literal type checks. Comparing it to enums isn't quite apples-to-apples; enums are objects and follow standard object comparison logic, whereas these are scalar identity checks.
> 4) the last part of the previous point extends to the general usefulness > of the feature. I again have to agree with Jorg: it is not clear what > problem it is trying to solve. Why not enums? It is a trend to get rid > of stringy APIs, and magic strings and ints -- for reasons. But this > feature will encourage bringing them back. With an enum you define it in > one place (easier to refactor), with literal scalar types you repeat it > in multiple places. Do we really want that?
The core goal is to expand the type system to allow for more precise descriptions of data. While literal types can be used standalone, they are fundamental for future features like array shapes, where keys and values often need to be narrowed to specific literals (e.g., a status key being exactly 'ok' | 'error'). A richer type system, similar to what we see in TypeScript, requires literals as a foundational building block. Example ( using literal types, type aliases, array shapes, and integer ranges ): ```php type ActionResponse = ['status' => 'ok' | 'error', 'message' => string, ?'data' => array]; interface Action { public function perform(Context $ctx, Request $request): ActionResponse; } type PingActionResponse = ['status' => 'ok', 'message' => 'pong', 'data' => ['time' => 0..]]; final readonly class PingAction extends Action { public function perform(Context $ctx, Request $request): PingActionResponse { return ['status' => 'ok', 'message' => 'pong', 'data' => ['time' => get_time()]]; } } ``` Cheers, Seifeddine.

Joerg Sowa

58 days ago
Hey Seifeddine, I'm not sure why my last message did not receive a response, but I will try again.
> similar to what we see in TypeScript
This is exactly my concern. Literal types are useful in TypeScript, but they are also often directly overused. I do not think PHP should encourage the same pattern. I would prefer type aliases, such as Robert’s proposal from last year, as a foundation before considering this direction: https://wiki.php.net/rfc/typed-aliases My concern is that the RFC encourages public APIs like function f(1|2|3 $x) or function sort('asc'|'desc' $direction), where magic scalar values become part of the runtime API instead of being represented by a properly domain modeled entity. Modern AI-assisted engineering makes semantic naming even more important. Inline literal types such as 1|2|3 or 'asc'|'desc' provide less semantic signal than named concepts such as enums or type aliases. This can make code harder not only for humans, but also for AI tools that rely heavily on names and surrounding context. However, I'm aware that this secondary argument is not very plausible in this group. Language features should be evaluated from the long-term user and API-design perspective, not only from the static-analysis perspective. From that perspective, I think this RFC approaches the problem from the wrong side. Kind regards, Jorg

Seifeddine Gmati

58 days ago
Hi Jorg, Apologies for missing your first message, It slipped past me. The problem is typing a value whose valid set is a few specific primitives you don't own and shouldn't model nominally: a flag that comes back as the int 1, 2 or 3 from a database or C extension, an HTTP status, or an 'asc'|'desc' that exists only at one API boundary. Enums fit when you own the type and want a named domain model with backing and methods. They're a poor fit when the values originate outside your control, or when you need a structural building block rather than a new named type, because an enum then forces you to define, import and convert to and from a nominal type for what is only "one of these primitive values." Enums stay the right tool for domain modelling; this covers the cases they handle badly, the way `int` and an `Age` value object coexist.
> I would prefer type aliases, such as Robert’s proposal from last year, as
a foundation before considering this direction: https://wiki.php.net/rfc/typed-aliases I'd love type aliases too, but they're orthogonal, and for the case you care about they build on this feature. An alias names a type that must already be expressible: `type Direction = 'asc'|'desc'` requires `'asc'|'desc'` to be a valid type first. So literal types are the prerequisite for aliasing the very `'asc'|'desc'` you'd want to name.
> This is exactly my concern. Literal types are useful in TypeScript, but
they are also often directly overused. I do not think PHP should encourage the same pattern.
> My concern is that the RFC encourages public APIs like function f(1|2|3
$x) or function sort('asc'|'desc' $direction), where magic scalar values become part of the runtime API instead of being represented by a properly domain modeled entity. A tool existing doesn't mandate its misuse. Enums remain available and are still the recommendation for domain modelling; nothing here changes that. Reaching a genuinely expressive type system means considering many features: generics, literals, array shapes, typed arrays, aliases, ranges, and more, most of which developers already rely on today through PHPDoc and static analysers because they add value. We can't land them in one massive overhaul; each needs its own focused discussion, and several (negated types, conditional types) would look out of place in PHP on their own. Literals are one of the foundational pieces the rest build on.
> Modern AI-assisted engineering makes semantic naming even more important.
Inline literal types such as 1|2|3 or 'asc'|'desc' provide less semantic signal than named concepts such as enums or type aliases. This can make code harder not only for humans, but also for AI tools that rely heavily on names and surrounding context. However, I'm aware that this secondary argument is not very plausible in this group. I'm not sure about this. `function move('up'|'down'|'left'|'right' $d)` states its valid inputs right at the signature. `function move(Direction $d)` requires resolving what `Direction` is, class, interface or enum, and what it permits, and how to obtain an instance of it before you do anything. The literal form arguably carries more local signal, for a human or an AI.
> Language features should be evaluated from the long-term user and
API-design perspective, not only from the static-analysis perspective. From that perspective, I think this RFC approaches the problem from the wrong side. Agreed, and that's the motivation here. The feature is API-design driven, with the future scope spelling it out: array shapes, integer ranges and similar build directly on literal types as their foundation. That's the long-term type-system frame I'd like the RFC judged on, rather than viewing it as a static-analysis convenience. Cheers, Seifeddine

Nick

58 days ago
Het Seifeddine, On 05.07.26 03:25, Seifeddine Gmati wrote:
> Hi Nick, > > Thanks for the feedback. > > > 1) if I did not miss anything the empty string case is not covered. > > Would `""` be legal? Any thoughts on providing ways to explicitly > > disallow empty strings? I don't think this should be further scope -- > > because we would need to make sure that we actually have a usable syntax > > to disallow empty strings. > > Empty strings are supported; "" is a valid literal string type. > Regarding non-empty-string, that is a separate concept from literal > types. However, this RFC provides the building blocks to express it in > the future, through negated types (e.g., !""). There's also the option > of doing refinement markers on base types (e.g., string[1..]), which > IIRC Gina is working on or mentioned it somewhere. > > Example ( using literal types, negated types, and type aliases ): > > ```php > type NonEmptyString = ! ""; > > function getNonEmptyString(): NonEmptyString { >   return 'ok'; // ok >   return ''; // err > } > ```
Sounds good, thanks. Would be great if this would be spelled out in the RFC.
> > 2) having strict and non-strict mode in PHP is bad enough, IMO. Seeing > > this brought further, and combined with "literal" (literal should be > > literal) feels wrong -- a coerced `true` is de facto not a literal 1, > > it's a coerced `true`. I understand that you recommend strict and want > > to leave it to a vote, but I feel like the "Coercive matching > > (alternative)" section should make a stronger point against it, to > > discourage a tempting "always been like that" for a new concept. > > The RFC highlights that strict matching is already the established > behavior for existing value-types in PHP, and is the preferred path. > true, false, and null never coerce, even in coercive mode. Maintaining > this for all literal types is the only way to ensure a consistent type > system where a unit type matches exactly its identity, hence it is the > preferred option.
I believe we speak past each other here. With you that it should be strict matching, which you also prefer! I am just asking to make the "Coercive matching (alternative)" section more explicitly spell out why it isn't a good choice. As in: literal types would be a new feature for PHP, why would we apply and carry over lose semantics to something that's called "literal"? Not sure if I express myself good enough here, get what I mean? In short: make a stronger point for strict!
> > 3) the performance section is thin; solely addressing what Tim brought > > up. I am wondering, does introducing this have any implications for > > non-literal types? Having a benchmark would be nice. I also agree with > > Jorg, that a benchmark comparing the performance with enums (memory + > > speed) would be helpful. > > This proposal has no performance impact on non-literal type checks. > Comparing it to enums isn't quite apples-to-apples; enums are objects > and follow standard object comparison logic, whereas these are scalar > identity checks.
Maybe a benchmark would make a strong point for your proposal? Maybe better performance? Likely mess memory usage?
> > 4) the last part of the previous point extends to the general usefulness > > of the feature. I again have to agree with Jorg: it is not clear what > > problem it is trying to solve. Why not enums? It is a trend to get rid > > of stringy APIs, and magic strings and ints -- for reasons. But this > > feature will encourage bringing them back. With an enum you define it in > > one place (easier to refactor), with literal scalar types you repeat it > > in multiple places. Do we really want that? > > The core goal is to expand the type system to allow for more precise > descriptions of data. While literal types can be used standalone, they > are fundamental for future features like array shapes, where keys and > values often need to be narrowed to specific literals (e.g., a status > key being exactly 'ok' | 'error'). A richer type system, similar to > what we see in TypeScript, requires literals as a foundational > building block. > > Example ( using literal types, type aliases, array shapes, and integer > ranges ): > > ```php > type ActionResponse = ['status' => 'ok' | 'error', 'message' => > string, ?'data' => array]; > > interface Action { >   public function perform(Context $ctx, Request $request): ActionResponse; > } > > type PingActionResponse = ['status' => 'ok', 'message' => 'pong', > 'data' => ['time' => 0..]]; > > final readonly class PingAction extends Action { >   public function perform(Context $ctx, Request $request): > PingActionResponse { >     return ['status' => 'ok', 'message' => 'pong', 'data' => ['time' > => get_time()]]; >   } > } > ```
Well, I believe you know my opinion here, and that we have fundamentally different ones -- which is fine. Before we bake anything more types into the language we should standardise PHPDoc, make an official spec. We got so many things wrong over the recent years that it would be the more sensible approach. And then the question remains, should we even inline types? Or is PHPDoc just great with some improvements and official acknowledgement? Not sure if potential future language features, that might never come, are a good selling point that make an argument for getting literal types in now. RE type aliases specifically... Something like: "we need to inline types to have them right where they are used, but then we want to have type aliases to move them away elsewhere right after" does make zero sense to me. How does both fit in the very same argument? Some years ago I wrote the following somewhere (a "thing" refers to an "object"):
> This is one of the reasons I don’t like TypeScript. They put type > definitions everywhere. > > My thinking: > An email is a thing. By having that thing we can describe it, with a > type. In real life, something that doesn’t exist, can’t have a type. > > So why would we do it differently in programming? > > A dedicated file/class for each thing is imho better. Eventually, the > thing is the type by itself. Because if you need the type, you need > the thing. > > What would be cool, is to have a type standard library so that not > everyone must write it on their own.
Just TypeScript having all these things doesn't mean that they are good for PHP. We should not make PHP another TypeScript.
> > Cheers, > Seifeddine.
-- Cheers Nick

Seifeddine Gmati

40 days ago
Subject: Re: [RFC] Literal Scalar Types On Mon, 15 Jun 2026 at 02:22, Seifeddine Gmati <azjezz@carthage.software> wrote:
> Hello Internals, > > I'd like to start the discussion on a new RFC adding literal scalar > types to PHP. > > - RFC: https://wiki.php.net/rfc/literal_scalar_types > - Implementation: https://github.com/php/php-src/pull/22314 > > Thanks, > Seifeddine. >
Hi internals, I have updated the Literal Scalar Types RFC to version 1.0, which I consider the final revision: https://wiki.php.net/rfc/literal_scalar_types The changes are editorial, based on feedback in this thread. I merged the "Accepted literal syntax" section into the per-type sections so each literal kind is described in one place, added a paragraph to the introduction on where literal types fit next to enums, tightened the reasoning in the matching semantics section, and added a short note on enum comparisons to the Performance section. The proposal itself is unchanged: the syntax, the semantics and the three votes are the same as in 0.3. Since this is a minor revision, I am opening voting shortly. A separate [VOTE] thread will follow. Thanks to everyone who took part in the discussion. Seifeddine

Seifeddine Gmati

40 days ago
On Thu, 23 Jul 2026 at 06:13, Seifeddine Gmati <azjezz@carthage.software> wrote:
> Subject: Re: [RFC] Literal Scalar Types > On Mon, 15 Jun 2026 at 02:22, Seifeddine Gmati <azjezz@carthage.software> > wrote: > >> Hello Internals, >> >> I'd like to start the discussion on a new RFC adding literal scalar >> types to PHP. >> >> - RFC: https://wiki.php.net/rfc/literal_scalar_types >> - Implementation: https://github.com/php/php-src/pull/22314 >> >> Thanks, >> Seifeddine. >> > > Hi internals, > > I have updated the Literal Scalar Types RFC to version 1.0, which I > consider the final revision: https://wiki.php.net/rfc/literal_scalar_types > > The changes are editorial, based on feedback in this thread. I merged the > "Accepted literal syntax" section into the per-type sections so each > literal kind is described in one place, added a paragraph to the > introduction on where literal types fit next to enums, tightened the > reasoning in the matching semantics section, and added a short note on enum > comparisons to the Performance section. The proposal itself is unchanged: > the syntax, the semantics and the three votes are the same as in 0.3. > > Since this is a minor revision, I am opening voting shortly. A separate > [VOTE] thread will follow. > > Thanks to everyone who took part in the discussion. > > Seifeddine >
Hello internals, A procedural update, following my retraction in the [VOTE] thread. Earlier I opened voting on this RFC without meeting the prerequisites: no Intent to Vote had been posted two days in advance, and the 1.0 update I announced earlier is a Minor change, which starts a seven-day Cooldown Period. I retracted the vote. The widgets are closed, and the RFC is back to Under Discussion. The text is unchanged since the 1.0 announcement and I intend to keep it frozen until the vote. Please consider this message my formal Intent to Vote. The Cooldown Period ends on July 30, and I plan to reopen the vote that morning (UTC), running for the standard fourteen days within the Intent to Vote lifetime. One open question is the PHP 8.6 feature freeze. A vote opened on July 30 closes on August 13, the day of beta1, and the tag is created on August 11, so a merge would happen during the beta period and needs Release Manager approval. I am checking with the 8.6 RMs whether that is acceptable. If it is not, I will retarget the RFC to PHP 8.7 (or 9.0?) instead. Retargeting changes the voting widgets, so it would be announced here as a change, trigger its own Cooldown Period, and be followed by a fresh Intent to Vote, with no deadline pressure. Thanks for your patience. Seifeddine.

Seifeddine Gmati

33 days ago
On Thu, 23 Jul 2026 at 06:47, Seifeddine Gmati <azjezz@carthage.software> wrote:
> > On Thu, 23 Jul 2026 at 06:13, Seifeddine Gmati <azjezz@carthage.software> wrote: >> >> Subject: Re: [RFC] Literal Scalar Types >> On Mon, 15 Jun 2026 at 02:22, Seifeddine Gmati <azjezz@carthage.software> wrote: >>> >>> Hello Internals, >>> >>> I'd like to start the discussion on a new RFC adding literal scalar >>> types to PHP. >>> >>> - RFC: https://wiki.php.net/rfc/literal_scalar_types >>> - Implementation: https://github.com/php/php-src/pull/22314 >>> >>> Thanks, >>> Seifeddine. >> >> >> Hi internals, >> >> I have updated the Literal Scalar Types RFC to version 1.0, which I consider the final revision: https://wiki.php.net/rfc/literal_scalar_types >> >> The changes are editorial, based on feedback in this thread. I merged the "Accepted literal syntax" section into the per-type sections so each literal kind is described in one place, added a paragraph to the introduction on where literal types fit next to enums, tightened the reasoning in the matching semantics section, and added a short note on enum comparisons to the Performance section. The proposal itself is unchanged: the syntax, the semantics and the three votes are the same as in 0.3. >> >> Since this is a minor revision, I am opening voting shortly. A separate [VOTE] thread will follow. >> >> Thanks to everyone who took part in the discussion. >> >> Seifeddine > > > Hello internals, > > A procedural update, following my retraction in the [VOTE] thread. > > Earlier I opened voting on this RFC without meeting the prerequisites: no Intent to Vote had been posted two days in advance, and the 1.0 update I announced earlier is a Minor change, which starts a seven-day Cooldown Period. I retracted the vote. The widgets are closed, and the RFC is back to Under Discussion. The text is unchanged since the 1.0 announcement and I intend to keep it frozen until the vote. > > Please consider this message my formal Intent to Vote. The Cooldown Period ends on July 30, and I plan to reopen the vote that morning (UTC), running for the standard fourteen days within the Intent to Vote lifetime. > > One open question is the PHP 8.6 feature freeze. A vote opened on July 30 closes on August 13, the day of beta1, and the tag is created on August 11, so a merge would happen during the beta period and needs Release Manager approval. I am checking with the 8.6 RMs whether that is acceptable. If it is not, I will retarget the RFC to PHP 8.7 (or 9.0?) instead. Retargeting changes the voting widgets, so it would be announced here as a change, trigger its own Cooldown Period, and be followed by a fresh Intent to Vote, with no deadline pressure. > > Thanks for your patience. > > Seifeddine.
Hello internals, An update on timing: this RFC will no longer target PHP 8.6. I asked the 8.6 Release Manager about the feature freeze. The effective cutoff is not the beta1 announcement on August 13 but the creation of the beta1 tag, which happens on Tuesday, August 11, mid-morning US Central time. A vote opened today would close on August 13, after the tag exists, so inclusion in 8.6 is not possible. I will therefore not reopen the vote, and the Intent to Vote I posted on July 23 is withdrawn. I have retargeted the RFC to the next PHP version, whether that is 8.7 or 9.0. RFC text is otherwise unchanged and remains final: https://wiki.php.net/rfc/literal_scalar_types Seifeddine

Matteo Beccati

33 days ago
Hi Seifeddine, Il 30/07/2026 01:59, Seifeddine Gmati ha scritto:
> I asked the 8.6 Release Manager about the feature freeze. The > effective cutoff is not the beta1 announcement on August 13 but the > creation of the beta1 tag, which happens on Tuesday, August 11, > mid-morning US Central time. A vote opened today would close on August > 13, after the tag exists, so inclusion in 8.6 is not possible. I will > therefore not reopen the vote, and the Intent to Vote I posted on July > 23 is withdrawn. > > I have retargeted the RFC to the next PHP version, whether that is 8.7 > or 9.0. RFC text is otherwise unchanged and remains final: > https://wiki.php.net/rfc/literal_scalar_types
I could have made an honest mistake when announcing the deadlines, but all my emails were pointing the 13th as deadline for RFCs. The rationale being that the implementation didn't have to land in beta1 necessarily, so the beta1 release date seemed better than "tagging day" as deadline. I think that having RFCs end voting so close to the feature freeze is a terrible idea as it gives very little wiggle room in case something unexpected comes out once implemented and more widely tested. That said, another RFC went to voting a few minutes ago, so we'd better be consistent. I apologise for the confusion! Cheers
-- Matteo Beccati

Seifeddine Gmati

33 days ago
On Thu, 30 Jul 2026 at 16:28, Matteo Beccati <php@beccati.com> wrote:
> > Hi Seifeddine, > > Il 30/07/2026 01:59, Seifeddine Gmati ha scritto: > > I asked the 8.6 Release Manager about the feature freeze. The > > effective cutoff is not the beta1 announcement on August 13 but the > > creation of the beta1 tag, which happens on Tuesday, August 11, > > mid-morning US Central time. A vote opened today would close on August > > 13, after the tag exists, so inclusion in 8.6 is not possible. I will > > therefore not reopen the vote, and the Intent to Vote I posted on July > > 23 is withdrawn. > > > > I have retargeted the RFC to the next PHP version, whether that is 8.7 > > or 9.0. RFC text is otherwise unchanged and remains final: > > https://wiki.php.net/rfc/literal_scalar_types > I could have made an honest mistake when announcing the deadlines, but > all my emails were pointing the 13th as deadline for RFCs. > > The rationale being that the implementation didn't have to land in beta1 > necessarily, so the beta1 release date seemed better than "tagging day" > as deadline. > > I think that having RFCs end voting so close to the feature freeze is a > terrible idea as it gives very little wiggle room in case something > unexpected comes out once implemented and more widely tested. > > That said, another RFC went to voting a few minutes ago, so we'd better > be consistent. I apologise for the confusion! > > > Cheers > -- > Matteo Beccati
Hi Matteo, Thanks for clarifying, and no apology needed. For this RFC the point is moot either way. My retarget announcement is already out and, being a change to the voting widgets, it started a fourteen-day Cooldown Period under the Feature Proposals policy. Even under the August 13 reading, the earliest the vote could now open is mid-August, so 8.6 was out of reach the moment that email hit the list. The RFC stays targeted at the next PHP version. For what it's worth, I agree with your wider point: a vote that closes at the freeze boundary leaves no room to react if implementation or wider testing turns something up. Landing early in the next cycle is the better home for a type-system change in any case. Good luck with the 8.6 releases. Cheers, Seifeddine

Tim Düsterhus

32 days ago
Hi On 2026-07-30 18:55, Seifeddine Gmati wrote:
> For what it's worth, I agree with your wider point: a vote that closes > at the freeze boundary leaves no room to react if implementation or > wider testing turns something up. Landing early in the next cycle is > the better home for a type-system change in any case.
I also agree here: This very much feels like a proposal that should land early in the cycle, particularly due to the impact on existing extensions and the ecosystem, which might have already started preparing the adjustments for PHP 8.6. I have just given the RFC another read and just have a very minor comment regarding the vote: The “Matching semantics for literal scalar types?” vote has no explicitly stated tie-breaker. The RFC states “The author recommends strict matching” which *could* be considered a tie-breaker in favor of “strict matching”. But since we now have some time, you could as well spell that out explicitly. No further comments to the contents of the actual proposal. The cleanup of the “accepted literal syntax“ section made the RFC much easier to follow. Best regards Tim Düsterhus

Pierre Joye

32 days ago
Hey Matteo :) On Fri, Jul 31, 2026, 12:00 AM Matteo Beccati <php@beccati.com> wrote:
> Hi Seifeddine, > > Il 30/07/2026 01:59, Seifeddine Gmati ha scritto: > > I asked the 8.6 Release Manager about the feature freeze. The > > effective cutoff is not the beta1 announcement on August 13 but the > > creation of the beta1 tag, which happens on Tuesday, August 11, > > mid-morning US Central time. A vote opened today would close on August > > 13, after the tag exists, so inclusion in 8.6 is not possible. I will > > therefore not reopen the vote, and the Intent to Vote I posted on July > > 23 is withdrawn. > > > > I have retargeted the RFC to the next PHP version, whether that is 8.7 > > or 9.0. RFC text is otherwise unchanged and remains final: > > https://wiki.php.net/rfc/literal_scalar_types > I could have made an honest mistake when announcing the deadlines, but > all my emails were pointing the 13th as deadline for RFCs. > > The rationale being that the implementation didn't have to land in beta1 > necessarily, so the beta1 release date seemed better than "tagging day" > as deadline. > > I think that having RFCs end voting so close to the feature freeze is a > terrible idea as it gives very little wiggle room in case something > unexpected comes out once implemented and more widely tested. >
beta phases exist exactly for this reason. wider base of testers. alpha is, heh, this is what we have so far, more or less stable, go test it already! RCs are everything should be stable, that's the final state, last stage(s) for any bugs or breaks that didn't get caught earlier. we should not expect "widely tested" new features, never released before per se. between the limited time induced by the updated policies, the Christmas break "pls don't", and this, one needs to take holidays to make it somehow as the time left in a year is low, very low, now. That said, another RFC went to voting a few minutes ago, so we'd better
> be consistent. I apologise for the confusion! >
I can't talk for the author, you are in your role here :) applying policies, even broken ones :)
-- Pierre @pierrejoye

Matteo Beccati

32 days ago
Hi Pierre, Il 31/07/2026 13:06, Pierre Joye ha scritto:
> beta phases exist exactly for this reason. wider base of testers. > > alpha is, heh, this is what we have so far, more or less stable, go test > it already! > > RCs are everything should be stable, that's the final state, last > stage(s) for any bugs or breaks that didn't get caught earlier. > > we should not expect "widely tested" new features, never released before > per se.
Yes indeed. But often unexpected side effects and breakages are caught by various CIs running daily snapshots with OSS projects.
> between the limited time induced by the updated policies, the Christmas > break "pls don't", and this, one needs to take holidays to make it > somehow as the time left in a year is low, very low, now.
I hear you, it's just that undoing or amending an RFC is a convoluted process too, especially very late in the release cycle. Cheers
-- Matteo Beccati

Pierre Joye

32 days ago
Hey Matteo :) On Fri, Jul 31, 2026, 8:39 PM Matteo Beccati <php@beccati.com> wrote:
> Hi Pierre, > > Il 31/07/2026 13:06, Pierre Joye ha scritto: > > beta phases exist exactly for this reason. wider base of testers. > > > > alpha is, heh, this is what we have so far, more or less stable, go test > > it already! > > > > RCs are everything should be stable, that's the final state, last > > stage(s) for any bugs or breaks that didn't get caught earlier. > > > > we should not expect "widely tested" new features, never released before > > per se. > > Yes indeed. But often unexpected side effects and breakages are caught > by various CIs running daily snapshots with OSS projects
right, and many use snapshot. and our ci is already very complete tbh. took a while to make it green from a greenfield additions. and yes, that's what beta are for :)
> > > between the limited time induced by the updated policies, the Christmas > > break "pls don't", and this, one needs to take holidays to make it > > somehow as the time left in a year is low, very low, now. > > I hear you, it's just that undoing or amending an RFC is a convoluted > process too, especially very late in the release cycle.
we are technically still in alpha.... it is not late. It is early in my book ;-)