[PHP-DEV] [RFC] [Discussion] Literal Scalar Types

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: PHP: rfc:literal_scalar_types
- Implementation: [RFC]: Literal Scalar Types by azjezz · Pull Request #22314 · php/php-src · GitHub

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 PHP: 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

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.

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.

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.

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.

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

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

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: PHP: rfc:literal_scalar_types
- Implementation: [RFC]: Literal Scalar Types by azjezz · Pull Request #22314 · php/php-src · GitHub

Thanks,
Seifeddine.

Hi all,

Based on the discussion so far, I've updated the Literal Scalar Types
RFC to v0.2:

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.