[RFC] Pattern Matching

php.internals

Larry Garfield

273 days ago
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: https://wiki.php.net/rfc/pattern-matching You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons. This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.) Nonetheless, there are two outstanding questions on which we are looking for feedback. Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
-- Larry Garfield larry@garfieldtech.com

Paul Dragoonis

273 days ago
On Mon, Dec 1, 2025, 9:39 PM Larry Garfield <larry@garfieldtech.com> wrote:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, > pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had > this one in-progress for 5 years. :-) (Though it was inactive for many of > those years, in fairness.) Pattern matching was intended as the next > follow up to Enums, as it's a stepping stone toward full ADT support. > However, we also feel it has enormous benefit on its own for simplifying > complex comparisons. > > This RFC has been through numerous iterations, including a full > implementation rewrite just recently that made a number of features much > easier. We have therefore included two patterns that were previously > slated for later inclusion but turned out to be trivially easy in the new > approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are looking > for feedback. > > Naturally given the timing, we will not be calling a vote until at least > late January, regardless of how the discussion goes. So, plenty of time to > express your support. :-) >
Hi Larry, Looking decent so far. match() seems a bit clunky in current state. Is it just your code example, and you could come with another one? The match(true) and repeating of $somevar over and over is the clunky part. Normal match($somevar) .. this is clean and works like switch() so people are happy adopting it .. whereas match() and match() is, look similar but you have to use them differently ? Can you clarify? Many thanks, Paul

Larry Garfield

273 days ago
On Mon, Dec 1, 2025, at 3:57 PM, Paul Dragoonis wrote:
> On Mon, Dec 1, 2025, 9:39 PM Larry Garfield <larry@garfieldtech.com> wrote: >> Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: >> >> https://wiki.php.net/rfc/pattern-matching >> >> You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons. >> >> This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.) >> >> Nonetheless, there are two outstanding questions on which we are looking for feedback. >> >> Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-) > > Hi Larry, > > Looking decent so far. > > match() seems a bit clunky in current state. Is it just your code > example, and you could come with another one? > > The match(true) and repeating of $somevar over and over is the clunky part. > > Normal match($somevar) .. this is clean and works like switch() so > people are happy adopting it .. whereas match() and match() is, look > similar but you have to use them differently ? > > Can you clarify?
I think you're misinterpreting the example. The first block under "match() enhancement" is the syntax we propose to have. The second block is what it would decompose to, aka, an explanation of what it means. While you could write the second block, you really shouldn't when the first block is available and better. --Larry Garfield

Paul Dragoonis

273 days ago
On Mon, Dec 1, 2025, 10:26 PM Larry Garfield <larry@garfieldtech.com> wrote:
> On Mon, Dec 1, 2025, at 3:57 PM, Paul Dragoonis wrote: > > On Mon, Dec 1, 2025, 9:39 PM Larry Garfield <larry@garfieldtech.com> > wrote: > >> Hi folks. Ilija and I would like to present our latest RFC endeavor, > pattern matching: > >> > >> https://wiki.php.net/rfc/pattern-matching > >> > >> You may note the date on the RFC is from 2020. Yes, we really have had > this one in-progress for 5 years. :-) (Though it was inactive for many of > those years, in fairness.) Pattern matching was intended as the next > follow up to Enums, as it's a stepping stone toward full ADT support. > However, we also feel it has enormous benefit on its own for simplifying > complex comparisons. > >> > >> This RFC has been through numerous iterations, including a full > implementation rewrite just recently that made a number of features much > easier. We have therefore included two patterns that were previously > slated for later inclusion but turned out to be trivially easy in the new > approach. (Variable pinning and numeric comparison.) > >> > >> Nonetheless, there are two outstanding questions on which we are > looking for feedback. > >> > >> Naturally given the timing, we will not be calling a vote until at > least late January, regardless of how the discussion goes. So, plenty of > time to express your support. :-) > > > > Hi Larry, > > > > Looking decent so far. > > > > match() seems a bit clunky in current state. Is it just your code > > example, and you could come with another one? > > > > The match(true) and repeating of $somevar over and over is the clunky > part. > > > > Normal match($somevar) .. this is clean and works like switch() so > > people are happy adopting it .. whereas match() and match() is, look > > similar but you have to use them differently ? > > > > Can you clarify? > > I think you're misinterpreting the example. > > The first block under "match() enhancement" is the syntax we propose to > have. > > The second block is what it would decompose to, aka, an explanation of > what it means. While you could write the second block, you really > shouldn't when the first block is available and better. >
Hi Larry, You are correct. Ignore my comment I thought it was showing the opposite. match() looks good 👍 Many thanks, Paul

Bob Weinand

273 days ago
Hey Larry, On 1.12.2025 22:36:21, Larry Garfield wrote:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons. > > This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are looking for feedback. > > Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
Thanks for bringing pattern matching up for discussion again. ---- I'd like to note that the class-access is very ugly. // Shorthand if ($p is Point(:$z, x: 3, :$y)) { print "x is 3 and y is $y and z is $z."; } The RFC gives as reasoning that the colon prefix is needed for support of positional parameters in ADTs. Sure. That's fine to anticipate these. But what's not fine is using an inconsistent syntax for variable bindings across different contexts. In arrays binding is just a bare variable. In objects it suddenly needs a colon? What. Also, a colon is very prone to being missed in the future with ADTs. Point::2D($y, $x) vs Point::2D(:$y, :$x). Means something completely different, but if you mess up just having the colon there or not, is a serious problem. Can we instead find some solution, which satisfies both and still delivers consistency? An earlier iteration of the RFC had the following very nice construction: $p is Point&{ $z, x: 3, $y } This just worked. It's a Point class, and then it matches the properties of the object. Nice. This also works for future ADTs. Move::Forward&{ $amount }. Then, if there's a desire to actually *positionally* match an object. Then it's logical to use a parenthesized expression, for a tuple. I.e.: $move is Move::Forward($a) Where $a is assigned the first value passed to Move::Forward. Similarly for destructuring without class name no longer works: $json = json_decode($myInput); if ($json is stdClass(type: "store", :$value)) {    // why do I need to know/specify that it's a stdclass?! I'm just interested in the properties. } vs. if ($json is { type: "store", $value }) {    // } This satisfies the requirements of keep the language clear and intuitive: - Any standalone variable is bound. No weird colon shenanigans. The syntax is consistent. - Positional binding is quite intuitively using parenthesis - you construct the enum with Foo::Bar($var) and you read it back on the right hand side with Foo::Bar($var). - It naturally allows destructuring without class name. - It makes it hard to accidentally write something totally different to what was meant. (Also, it's likely more intuitive to users from other languages, like rust, which also has {} for named stuff and () for positional stuff.) Further this particular syntax works nicely with a future scope of object destructuring, akin to array destructuring. As an example: function addVec(Point $p, Vec $v) {   Point(:$px, :$py) = $p; // I already know this is a Point, why do I need to repeat it. It also looks ugly and quite a bit like a left-hand function call. Like... assigning something to a returned reference?   // Or would you do {$px, $py} for object destructuring? Well that's now truly inconsistent.   Vec(:$vx, :$vy) = $v;   return new Point($px + $vx, $py + $vy); } vs. function addVec(Point $p, Vec $v) {   {$px, $py} = $p; // Plain and simple. Perfectly straightforward.   {$vx, $vy} = $v;   return new Point($px + $vx, $py + $vy); } I've also heard a consideration about "Foo::Bar & { $var }" being ambiguous with respect to "is Foo::Bar now a const or an ADT class". This may be resolved in the VM. I don't consider this a major issue, and is simply something which can be disambiguated at optimizer-time or run-time, depending on what type of symbol it is. ---- I'm deeply unsatisfied by the handling of object properties: "Note that matching against a property's value implies reading that property's value", "If the property is uninitialized, an error will be thrown." and "If the property is undefined and none of the above apply, it will evaluate to null and a Warning will be issued." This is wildly inconsistent with arrays: "Of particular note, the pattern matching approach automatically handles array_key_exists() checking. That means a missing array element will not trigger a warning, whereas with a traditional if ($foo['bar'] === 'baz') approach missing values must be accounted for by the developer manually." Sure, a pattern match will read an objects property. Just like it reads an arrays entry. I assume the goal is "let's warn when an object property is typoed". But it just makes for two tiers. arrays get key_exists(), properties do not get property_exists(). I welcome surprises. From my point of view, pattern matching is an "is" operation. Thus it ought expressing isset-like semantics. I.e. the approach for arrays is correct, and should be mirrored to objects. I definitely think the approach of "let's warn about typos" is laudable, but consistency is important. It also means that uninitialized properties forcibly throw. It also has subtle ordering implications on the semantics, given that the implementation internally short-circuits. E.g. (assuming something like "class ResponseOrError { string $type; Exception $e; string $response; }"): if ($obj is ResponseOrError { type: "error", exception: $e }) { throw $e; } does not throw if $exception is uninitialized. and $type is not error. But $obj is ResponseOrError { exception: $e, type: "error" } will certainly throw. It further means that there needs to be some internal checked and you cannot simply write: if ($obj is ResponseOrError { exception: $e }) { throw $e; } This is bad design and takes a lot of flexibility, just for being typo-safe. There are better approaches towards typo-safety, e.g. in future (PHP 9) we could change isset() and all other similar checks (coalesce and this proposal) to immediately throw when a property is checked for existence, whose name does not exist on a class which is not marked #[\AllowDynamicProperties]. We should make use of that instead of shoe-horning this into this proposal. ---- Open questions: - match() "is" placement: I prefer match() is {}  rather than an "is" inside the construct. Simpler to me, but I think either choice is fine. - Positional array enforcement: It's relatively simple to intentionally get positional arrays via array_values(). I also don't think it's unexpected. That's just how PHP's arrays work. Enforcing positional arrays however will be quite surprising if e.g. an entry was removed: $a = [1, 2, 3]; unset($a[1]); if ($a is [1, 3]) {    // huh? It's [1, 2 => 3], not [1, 3]. } Thanks, Bob

Larry Garfield

266 days ago
Hi Bob
> I'd like to note that the class-access is very ugly. > ... > But what's not fine is using an inconsistent syntax for variable bindings across different contexts. In arrays binding is just a bare variable. In objects it suddenly needs a colon? What. > ... > This also works for future ADTs. Move::Forward&{ $amount }. Then, if there's a desire to actually *positionally* match an object. Then it's logical to use a parenthesized expression, for a tuple. I.e.:
We'd argue that using `()` and `{}` to differentiate between positional and named parameters is no more intuitive than a `:` prefix. To look at preexisting concepts in the language: Named vs. positional arguments aren't determined by the type of braces, but by whether they are preceded by the argument name. `(:$param)` can be viewed as an extension of `(param: $param)` where the redundant argument name is dropped, while `{ $param }` looks like something completely different. (Technnically, adding this shorthand to named argument calls would be possible, though we are not proposing it here.) We also recently discovered that this is exactly the approach Dart takes: https://dart.dev/language/patterns#destructuring-class-instances So there is prior art. Also, this is only a question for the shorthand syntax. If using the full version, there is no room for confusion. Additionally, as noted in the RFC, we ran into issues for `{}` both when using and omitting `&`. Without a `&`, `{}` is confused for a hook when in the default property value position. With a `&`, runtime disambiguation is required for ADTs and a class constants, which is inconsistent with the rest of the language. (Unless you can suggest a way to resolve that ambiguity, in which case we can consider it.) More on this topic below.
> Positional binding is quite intuitively using parenthesis - you construct the enum with Foo::Bar($var) and you read it back on the right hand side with Foo::Bar($var).
Yes, this is precisely the syntax we had in mind for ADTs. So we're in agreement here.
> It naturally allows destructuring without class name.
This is an upside of the split syntax, yes. However, as noted, that led to parsing issues, which is why we moved away from it. While a bit less visually pleasing, it would be possible to allow `_` or `*`, or even `object` as a pseudo-class name to indicate "any class." That would be a much simpler solution to the "I don't care about the object type" question. Eg: `$p is _(x: 5)`.
> I've also heard a consideration about "Foo::Bar & { $var }" being ambiguous with respect to "is Foo::Bar now a const or an ADT class". This may be resolved in the VM. I don't consider this a major issue, and is simply something which can be disambiguated at optimizer-time or run-time, depending on what type of symbol it is.
PHP is very explicit when it comes to distinguishing member types syntactically. For example, in many languages, `foo.bar` could access a field, static field, constant, function reference, static function reference, subtype, etc. PHP doesn't do that, it uses `$foo->bar`, `foo::$bar`, `foo::bar`, `$foo->bar(...)`, `foo::bar(...)`, `foo\bar`, etc. It goes out of its way to make it obvious what kind of member is being accessed. This makes a lot of sense for PHP, because each PHP file is compiled in a fully isolated context where we frequently don't know what the class `Foo` looks like. This avoids a lot of guesswork for the engine. There are very few exceptions to this rule, one being `Foo::bar()`, which is normally a static call but _can_ also be an instance call when `Foo` is an ancestor of the class of the current instance. This has already caused some issues in the past. We tried to automatically make closures static that don't use `$this`, which is unsound because of these hidden instance call. We feel it's wise to avoid adding more such cases. An alternative would be to disallow matching against an ADT's case name entirely, allowing `$p is Point & {$x}` but not `$x is Option::Some & {$val}`. The latter would have to be positional only, `$x is Option::Some($y)`. That seems like a rather arbitrary and unexpected restriction, however.
> I'm deeply unsatisfied by the handling of object properties: > > "Note that matching against a property's value implies reading that property's value", "If the property is uninitialized, an error will be thrown." and "If the property is undefined and none of the above apply, it will evaluate to null and a Warning will be issued." > > This is wildly inconsistent with arrays:
The rationale for treating objects and arrays differently is that objects are almost always structured (meaning we know and should be able to rely on which properties it defines), while arrays are frequently not. The obvious exception you already mentioned is `stdClass`, although how useful or common that is in practice at this point is debatable. (I cannot recall the last time I saw `json_decode()` called without the flag to use an associative array instead.) `stdClass` should not be fundamental in shaping how this pattern works. You can also efficiently cast `stdClass` to `array` (because that's how `stdClass` is implemented anyway) to use the array pattern. That said, this isn't a hill either of us wants to die on, so if the consensus is to swallow such cases and just return false, we will go with that. (Meaning, other people, please weigh in here.)
> It also means that uninitialized properties forcibly throw.
With fairly few exceptions, an uninitialized property on an object that is passed back to a caller is a sign of a design flaw. Even if the construction process is multi-step -- as it is in Crell/AttributeUtils or deserializers, factories, ORMs, etc. -- everything is initialized by the time the object is returned to the caller. Leaving properties uninitialized, or unsetting them manually implicitly adds an undocumented "undefined" type to your type union. There's absolutely no indication that accessing a property may be unsafe, but it is. There are niche cases where unsetting properties is useful (e.g. breaking cycles, as you've mentioned privately), but such objects shouldn't escape to users with a half-initialized state. So there's a 99% chance that if the developer pattern matches against an uninitialized property, it's a developer error and should be corrected, not silently suppressed. The other issue is that `$p is Point(:$x)` looks like an infallible pattern (assuming `$p` is an instance of `Point`) where we just want to extract `$x`, but if `$point->x` is uninitialized this pattern would fail. Given this is almost certainly a mistake, it seems better to inform the user about it rather than silently returning `false`.
> E.g. (assuming something like "class ResponseOrError { string $type; Exception $e; string $response; }"):
This is a classic example of when ADTs would be useful.
> Enforcing positional arrays however will be quite surprising if e.g. an entry was removed: > $a = [1, 2, 3]; > unset($a[1]); > if ($a is [1, 3]) { > // huh? It's [1, 2 => 3], not [1, 3]. > }
I think you misunderstood the question here. `[1, 2]` and `[0 => 1, 1 => 2]` should always be equivalent in terms of key behavior. What we weren't in agreement about is whether `[1, 2]` should have additional guarantees about order, i.e. about whether `[1, 2]` need to appear in that order, or whether `[1 => 2, 0 => 1]` is also acceptable. Normally, values are accessed by key which makes order mostly irrelevant, but there are cases where the user might expect to see values in a specific order when iterating over the array. Implementing this order-consistency check in a performant way is also somewhat tricky, and it would always add some overhead no matter what. --Larry Garfield

Vinicius Dias

265 days ago
I would just like to point something out regarding this sentence in the RFC on the "Variable pinning" section:
> This particular syntax was chosen as it is the same as in Ruby, the only other language we know of that has this functionality.
I don't know how helpful this info is, but I'll provide it anyway. Elixir and Erlang also have the functionality and its syntax is the same, using the ^ character. https://hexdocs.pm/elixir/pattern-matching.html#the-pin-operator

Tim Düsterhus

265 days ago
Hi Am 2025-12-10 14:37, schrieb Vinicius Dias:
>> This particular syntax was chosen as it is the same as in Ruby, the >> only other language we know of that has this functionality. > > I don't know how helpful this info is, but I'll provide it anyway. > Elixir and Erlang also have the functionality and its syntax is the > same, using the ^ character. > > https://hexdocs.pm/elixir/pattern-matching.html#the-pin-operator
AFAICT this is not quite right. It is supported in Elixir, but not in Erlang. There is a proposal to add it to Erlang, but the corresponding PR is still open, which indicates that it is not accepted yet: https://www.erlang.org/eeps/eep-0055 It's not surprising to me that Elixir supports it, since Elixir is heavily inspired / influenced by Ruby. Best regards Tim Düsterhus

Larry Garfield

264 days ago
On Wed, Dec 10, 2025, at 12:20 PM, Tim Düsterhus wrote:
> Hi > > Am 2025-12-10 14:37, schrieb Vinicius Dias: >>> This particular syntax was chosen as it is the same as in Ruby, the >>> only other language we know of that has this functionality. >> >> I don't know how helpful this info is, but I'll provide it anyway. >> Elixir and Erlang also have the functionality and its syntax is the >> same, using the ^ character. >> >> https://hexdocs.pm/elixir/pattern-matching.html#the-pin-operator > > AFAICT this is not quite right. It is supported in Elixir, but not in > Erlang. There is a proposal to add it to Erlang, but the corresponding > PR is still open, which indicates that it is not accepted yet: > https://www.erlang.org/eeps/eep-0055 > > It's not surprising to me that Elixir supports it, since Elixir is > heavily inspired / influenced by Ruby. > > Best regards > Tim Düsterhus
Still, it's good information to have. That means two languages using the ^ character, and no one using anything else instead. We went with ^ largely because "well, that's what Ruby does, and it's easy to implement that way." So if it's more of a common standard (the way |> is the standard spelling of pipe), that's another argument to follow suit and not complicate things. Thanks, Vinicius! --Larry Tarfield

Tim Düsterhus

264 days ago
Hi Am 2025-12-10 20:06, schrieb Larry Garfield:
> Still, it's good information to have. That means two languages using > the ^ character, and no one using anything else instead.
I wouldn't call it "two languages" due to the close relationship of Elixir and Ruby. It would probably not entirely wrong to say that Elixir is Ruby running on BEAM. It's somewhat equivalent to saying that two languages support file inclusion `#include`, just because C and C++ both do [1]. Best regards Tim Düsterhus [1] Yes, yes. Technically it's the preprocessor that does.

Stephen Reay

273 days ago
> On 2 Dec 2025, at 04:45, Larry Garfield <larry@garfieldtech.com> wrote: > > Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons. > > This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are looking for feedback. > > Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-) > > -- > Larry Garfield > larry@garfieldtech.com >
Hi Larry, Is there a specific reason why less than/etc comparison (and the future scope of ranges) is limited to numerics? 'A' < 'K' comparisons are already in the language, and range() accepts string arguments to produce an array of bytes. Cheers Stephen

Larry Garfield

272 days ago
On Mon, Dec 1, 2025, at 7:24 PM, Stephen Reay wrote:
> Hi Larry, > > Is there a specific reason why less than/etc comparison (and the future > scope of ranges) is limited to numerics? > > 'A' < 'K' comparisons are already in the language, and range() accepts > string arguments to produce an array of bytes. > > Cheers > > Stephen
Because neither of us ever use comparisons on strings, as it's not super reliable, so it never came up. :-) We'd prefer to keep it numeric, as the logic otherwise could get confusing. (Personally I'd prefer to even exclude numeric strings, but that ends up being more work.) --Larry Garfield

Dmitry Derepko

273 days ago
> On Dec 2, 2025, at 1:38 AM, Larry Garfield <larry@garfieldtech.com> wrote: > > Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons. > > This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are looking for feedback. > > Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-) > > -- > Larry Garfield > larry@garfieldtech.com
Thank you for the RFC and that you included option with match and individual IS’s, I think it would be preferable option not to break the match construction. Regarding the patterns, is there a way to declare and reuse the patterns? Assign it to a variable and use it as “is $pattern” or something like that. Maybe as a separate class “$pattern->match($var)”. While it’s not reusable it looks like it has a limited scope to use it, otherwise regular replacements preferred.
-- Best regards, Dmitrii Derepko. @xepozz

Weedpacket

273 days ago
On 2025-12-02 19:26, Dmitry Derepko wrote:
> > > Regarding the patterns, is there a way to declare and reuse the patterns? Assign it to a variable and use it as “is $pattern” or something like that. Maybe as a separate class “$pattern->match($var)”. > >
There is an annex document on more speculative extensions linked in the RFC that mentions this: https://github.com/Crell/php-rfcs/blob/master/pattern-matching/future.md I was going to raise the thought of using these patterns to constrain function parameters (despite the heavy overlap of "$var is type" and "type $var"), but found that already included in the annex as well.

Larry Garfield

272 days ago
On Tue, Dec 2, 2025, at 12:26 AM, Dmitry Derepko wrote:
> Thank you for the RFC and that you included option with match and > individual IS’s, I think it would be preferable option not to break the > match construction. > > Regarding the patterns, is there a way to declare and reuse the > patterns? Assign it to a variable and use it as “is $pattern” or > something like that. Maybe as a separate class “$pattern->match($var)”. > While it’s not reusable it looks like it has a limited scope to use it, > otherwise regular replacements preferred.
Not currently, no. It's something we've discussed, but it's also kinda hard, and I don't believe any other languages do it. What you could do is just wrap it in a short closure. $pattern = fn($x) => $x is some|Pattern|null; $pattern($something); I'd love to have a way to shorten that, but so far we haven't come up with one. Suggestions welcome, as I'd love to be able to do something like this (obviously not exactly this): $filtered = array_filter($arr, is Point(x: <10)); --Larry Garfield

Dmitry Derepko

272 days ago
> On Dec 3, 2025, at 12:03 AM, Larry Garfield <larry@garfieldtech.com> wrote: > > $filtered = array_filter($arr, is Point(x: <10));
Btw, it looks incorrect and short closure is preferable here array_filter($arr, fn($x) => $x is Point()) Or if it works with DFA array_filter($arr, ? is Point()) If your example illustrated a pattern as an argument I think it’s not valid case, because a pattern is something after “is”? The point, enumerated types or values. And “is” here is an action/function/intention to match left and right operands. But kotlin style is perfectly matched here array_filter($arr, { $it is Point() })
-- Best regards, Dmitrii Derepko. @xepozz

Larry Garfield

272 days ago
On Tue, Dec 2, 2025, at 10:32 PM, Dmitry Derepko wrote:
>> On Dec 3, 2025, at 12:03 AM, Larry Garfield <larry@garfieldtech.com> wrote: >> >> $filtered = array_filter($arr, is Point(x: <10)); > > Btw, it looks incorrect and short closure is preferable here > array_filter($arr, fn($x) => $x is Point())
As I said, that's not actually a syntax I'm proposing. Just spitballing that it would be nice to be able to remove the `fn($x) => $x` boilerplate at some point. But that's not for this RFC; for now, the fact that you can wrap a pattern up into a short closure is sufficient for reuse.
> Or if it works with DFA > array_filter($arr, ? is Point()) > > If your example illustrated a pattern as an argument I think it’s not > valid case, because a pattern is something after “is”? The point, > enumerated types or values. And “is” here is an > action/function/intention to match left and right operands. > > But kotlin style is perfectly matched here > > array_filter($arr, { $it is Point() })
Ilija would agree with you, and has been arguing for a { $0 + 3 } type syntax for extra-short closures for a while now. :-) But that's not in scope for the moment. --Larry Garfield

Rob Landers

273 days ago
On Mon, Dec 1, 2025, at 22:36, Larry Garfield wrote:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons. > > This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are looking for feedback. > > Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-) > > -- > Larry Garfield > larry@garfieldtech.com
One thing I didn't see covered: accessing static variables in a class: class Point { public static $origin = new Point(0,0,0); } How do we pattern match to this? Is it the obvious Point::$origin? Same with class constants? — Rob

Rob Landers

273 days ago
On Tue, Dec 2, 2025, at 07:51, Rob Landers wrote:
> On Mon, Dec 1, 2025, at 22:36, Larry Garfield wrote: >> Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: >> >> https://wiki.php.net/rfc/pattern-matching >> >> You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons. >> >> This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.) >> >> Nonetheless, there are two outstanding questions on which we are looking for feedback. >> >> Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-) >> >> -- >> Larry Garfield >> larry@garfieldtech.com > > One thing I didn't see covered: accessing static variables in a class: > > class Point { > public static $origin = new Point(0,0,0); > } > > How do we pattern match to this? Is it the obvious Point::$origin? Same with class constants? > > — Rob
I literally saw it just after sending the email and missed the "undo send" by milliseconds. Sorry for the noise. — Rob

Alexandre Daubois

273 days ago
Hi Larry and Ilija, Le lun. 1 déc. 2025 à 22:37, Larry Garfield <larry@garfieldtech.com> a écrit :
> > Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: > > https://wiki.php.net/rfc/pattern-matching
Thanks for this RFC, it looks really great! I see that resources are never mentioned. I know that a lot of resources are being converted to opaque classes in recent versions, but they still exist. Would it be possible to use the "resource" type with pattern matching, e.g. `$r is resource`? I would assume that the answer is yes because the RFC mentions the support of `$foo is iterable`. — Alexandre Daubois

Larry Garfield

272 days ago
On Tue, Dec 2, 2025, at 3:13 AM, Alexandre Daubois wrote:
> Hi Larry and Ilija, > > Le lun. 1 déc. 2025 à 22:37, Larry Garfield <larry@garfieldtech.com> a écrit : >> >> Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: >> >> https://wiki.php.net/rfc/pattern-matching > > Thanks for this RFC, it looks really great! > > I see that resources are never mentioned. I know that a lot of > resources are being converted to opaque classes in recent versions, > but they still exist. Would it be possible to use the "resource" type > with pattern matching, e.g. `$r is resource`? I would assume that the > answer is yes because the RFC mentions the support of `$foo is > iterable`.
`iterable` is already a type used elsewhere. `resource` is not something you can specify anywhere in code right now. Making pattern matching the one place you can actually type check against `resource`, which is not an inconsistency we want. Also, resources are on their way out, gradually, so we'd just need to remove support from pattern matching again in the future sometime. In all, it's just not worth the effort and inconsistency. --Larry Garfield

Alexandru Pătrănescu

273 days ago
On Mon, Dec 1, 2025 at 11:39 PM Larry Garfield <larry@garfieldtech.com> wrote:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, > pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > >
Great work, very detailed and well written. 1. There are mentions that any type present in a function parameter signature and return value is a valid pattern matching. But are there any plans to extend the pattern-matching-syntax to parameter types and return values? Of course, with some restrictions: without variable pinning.And without variable binding on return type, but that could work on parameter types. If that's possible or already planned, I think it's worth mentioning it in the future scope section. 2. Maybe some parts of the RFC could be separated to reduce complexity. Thinking about variable pinning. 3. For match() "is" placement, I would go with the second form as it's more flexible, but I think allowing both forms might be nice if the complexity is not too high. Or go only with the second form now, and add the first form later in a smaller RFC.
-- Alex

Larry Garfield

272 days ago
On Tue, Dec 2, 2025, at 6:28 AM, Alexandru Pătrănescu wrote:
> On Mon, Dec 1, 2025 at 11:39 PM Larry Garfield <larry@garfieldtech.com> wrote: >> Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching: >> >> https://wiki.php.net/rfc/pattern-matching >> > > Great work, very detailed and well written.
Thanks!
> 1. There are mentions that any type present in a function parameter > signature and return value is a valid pattern matching. > But are there any plans to extend the pattern-matching-syntax to > parameter types and return values? > > Of course, with some restrictions: without variable pinning.And without > variable binding on return type, but that could work on parameter types. > If that's possible or already planned, I think it's worth mentioning it > in the future scope section.
See the linked "speculative extensions" document. It's mentioned there, but it hasn't gone beyond "Larry thinks it would be kinda cool to write `public int $x is >0`. That's definitely out of scope for now, but I'm very open to discussing it in the future.
> 2. Maybe some parts of the RFC could be separated to reduce complexity. > Thinking about variable pinning.
Actually, with the new implementation variable pinning turned out to be stupidly easy. That was a convenient bonus, which is why we included it in the initial RFC. It was originally in future scope, but the diff to add it was like 10 lines or something, so we included it now. The future-scopes that are still in future-scope are all "harder than they look" or "deciding what exactly to do will be a lot of discussion", which is why they're there and not in the base release.
> 3. For match() "is" placement, I would go with the second form as it's > more flexible, but I think allowing both forms might be nice if the > complexity is not too high. > Or go only with the second form now, and add the first form later in a > smaller RFC.
Noted! I think that's one vote for a single is, and one vote for a separate one for each arm. :-) --Larry Garfield

Alexandru Pătrănescu

272 days ago
On Tue, Dec 2, 2025 at 10:09 PM Larry Garfield <larry@garfieldtech.com> wrote:
> > > > 1. There are mentions that any type present in a function parameter > > signature and return value is a valid pattern matching. > > But are there any plans to extend the pattern-matching-syntax to > > parameter types and return values? > > > > Of course, with some restrictions: without variable pinning.And without > > variable binding on return type, but that could work on parameter types. > > If that's possible or already planned, I think it's worth mentioning it > > in the future scope section. > > See the linked "speculative extensions" document. It's mentioned there, > but it hasn't gone beyond "Larry thinks it would be kinda cool to write > `public int $x is >0`. That's definitely out of scope for now, but I'm > very open to discussing it in the future. >
That is not exactly what I meant. Since all types are valid patterns, can't we enlarge the type set to include more things from the pattern set? in future RFCs. Something like this: `function process(Point(x: 3|4, y: $y) $p): ['a' => int, 'b' => float & >0]`.
-- Alex

Larry Garfield

272 days ago
On Wed, Dec 3, 2025, at 2:21 AM, Alexandru Pătrănescu wrote:
> On Tue, Dec 2, 2025 at 10:09 PM Larry Garfield <larry@garfieldtech.com> wrote: >> >> >> > 1. There are mentions that any type present in a function parameter >> > signature and return value is a valid pattern matching. >> > But are there any plans to extend the pattern-matching-syntax to >> > parameter types and return values? >> > >> > Of course, with some restrictions: without variable pinning.And without >> > variable binding on return type, but that could work on parameter types. >> > If that's possible or already planned, I think it's worth mentioning it >> > in the future scope section. >> >> See the linked "speculative extensions" document. It's mentioned there, but it hasn't gone beyond "Larry thinks it would be kinda cool to write `public int $x is >0`. That's definitely out of scope for now, but I'm very open to discussing it in the future. >> > > That is not exactly what I meant. > Since all types are valid patterns, can't we enlarge the type set to > include more things from the pattern set? in future RFCs. > Something like this: `function process(Point(x: 3|4, y: $y) $p): ['a' > => int, 'b' => float & >0]`. > > -- > Alex
Yes, that's also in speculative extensions, under the "Parameter or return guards" section. :-) I think it would be cool (especially if combined with Rob's type aliases), but that's out of scope for now. --Larry Garfield

Tim Düsterhus

271 days ago
Hi Am 2025-12-02 21:06, schrieb Larry Garfield:
>> 2. Maybe some parts of the RFC could be separated to reduce >> complexity. >> Thinking about variable pinning. > > Actually, with the new implementation variable pinning turned out to be > stupidly easy. That was a convenient bonus, which is why we included > it in the initial RFC. It was originally in future scope, but the diff > to add it was like 10 lines or something, so we included it now. The > future-scopes that are still in future-scope are all "harder than they > look" or "deciding what exactly to do will be a lot of discussion", > which is why they're there and not in the base release.
There are different dimensions to complexity. Your reply focuses on just “implementation complexity”, but there's also “documentation complexity” or “deciding-how-the-syntax-should-look complexity”. I feel that the syntax for “variable pinning” specifically has the weakest arguments in favor of it, with the main argument being “Ruby has it” rather than “it makes sense”. The other syntax choices either are “obviously correct” or have good arguments backing the choice. Best regards Tim Düsterhus

Tim Düsterhus

267 days ago
Hi Am 2025-12-01 22:36, schrieb Larry Garfield:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, > pattern matching: > > https://wiki.php.net/rfc/pattern-matching
Thank you. I've already provided some feedback on the PR (and in Ilija's DMs) after seeing it “show up” in php-src, but I promised to put it on-list. I'm doing that now. This email is not a full review of the RFC, just the parts that I already noted. I think I also noted them before in the previous on-list “pre RFC” discussion. 1. I'd like to see `$foo is $bar` with a single top-level variable binding on the right side disallowed at compile time. This pattern is just an elaborate way of writing an assignment, thus (almost) never useful, but possibly confusing to folks coming from Python or just generally unfamiliar with PHP's pattern matching. I'd be okay with allowing `$foo is ($bar)` with the explicit parentheses, since this is an established pattern to indicate “yes, I meant it like this” when using assignments inside of conditionals, such as `while (($row = $statement->fetch()))` as a short form of `while (($row = $statement->fetch()) !== false)`. 2. I'd like to see required parentheses around “combinator patterns” (specifically `&` and `|`). The RFC currently claims:
> While patterns may resemble other language constructs, whatever follows > is is a pattern, not some other instruction.
which is false. “Whatever follows is a pattern, unless it no longer is a pattern” would be more accurate, which is not very helpful to intuitively determine the end of a pattern when visually scanning the code, particularly with the spacing around the combinators that the RFC suggests. The main issue is that pattern matching embeds a DSL with its own syntax inside of PHP, but without having a clear “pattern ends here delimiter”. I've also looked at other programming languages with pattern matching and most only support pattern matching at a small number of “special locations” (e.g. as part of a `match()` construct or only within a function signature) and also do not have a concept of “union” or “intersection” patterns. PHP allows to embed patterns (and the associated DSL) into arbitrary (complex) expressions, which is not something that is commonly seen as far as I can tell. C# appears to support it as well, but the overall syntax for pattern matching is quite different there, which makes it hard to directly compare it. The “atomic patterns” as a top-level are fine, since they are either a single “word” without any spaces or already have clear delimiters (such as the array pattern). To give an example: Consider line wrapping within a single pattern (e.g. because the class names get long or because the pattern matching expression is deeply indented). The most natural way I can come up with, without introducing parentheses, is the following: if ( $foo is Foo(some: "stuff", :$here) & Bar(with: "more_stuff") ) { } I find it incredibly non-obvious that `& Bar` still belongs to the pattern. And with just a single non-whitespace character change it becomes something entirely different, but also valid: if ( $foo is Foo(some: "stuff", :$here) && Bar(with: "more_stuff") ) { } I believe it is not unlikely that the former is interpreted as a typo for the latter by someone inexperienced with pattern matching. For `$foo is $bar&?User` there is some ambiguity if that should have been `($foo is $bar) ? User : …` (i.e. a ternary with a constant `User` in the “then” part). It might be clear grammar-wise, but not necessarily immediately to a reader. The proposed pattern matching semantics are extremely powerful, but do not come with any integrated guardrails, which make them extremely complex and easy to “hold wrong”. I find this a step backwards from the recent developments making PHP safer and more predictable without forcing users to learn all rules by heart. We already had issues with “gobble up everything until you can't” with short closures and pipes in 8.5. I fear the same happening with pattern matching. As an example the (future scope) range pattern `$foo is 1..=10` can easily be typoed or misremembered as `$foo is 1...10` which to my understanding would be valid PHP code equivalent to `($foo is 1.0) . (0.10)` (which is not useful, but valid). Taking into account possible guardrails right away can help ensure that future additions can be added in a way that is consistent with existing “look and feel”. Best regards Tim Düsterhus

Larry Garfield

220 days ago
On Mon, Dec 1, 2025, at 3:36 PM, Larry Garfield wrote:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, > pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had > this one in-progress for 5 years. :-) (Though it was inactive for many > of those years, in fairness.) Pattern matching was intended as the > next follow up to Enums, as it's a stepping stone toward full ADT > support. However, we also feel it has enormous benefit on its own for > simplifying complex comparisons. > > This RFC has been through numerous iterations, including a full > implementation rewrite just recently that made a number of features > much easier. We have therefore included two patterns that were > previously slated for later inclusion but turned out to be trivially > easy in the new approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are > looking for feedback. > > Naturally given the timing, we will not be calling a vote until at > least late January, regardless of how the discussion goes. So, plenty > of time to express your support. :-)
Happy New Year! Tim brought up some interesting points around parsing edge cases, which we're still discussing off list. Once we have a proposal (or multiple) to consider, we'll get back to that part. Meanwhile, there's a few outstanding issues that we do still want/need broader feedback on. Even if you just want to state a bikeshed preference, please do so. (Though an explanation of why is always helpful.) 1. match() statements. There's two ways we could do this: All-branches, or individual branches. This is particularly important as, in practice, we expect match() to be the most common use of patterns. // All-branches: $result = match ($somevar) is { Foo => 'foo', Bar => 'bar', Baz|Beep => 'baz', Qix, Nox => 'qix', }; // Individual branches $result = match ($somevar) { is Foo => 'foo', is Bar => 'bar', is Baz|Beep => 'baz', is Qix, Nox => 'qix', }; The idea of the second being that, if you want just an identity match, you can skip adding `is` on just that one arm. While it would be lovely to do that automatically, we cannot differentiate between a class name and a constant so that's not really feasible. Of note, most language with this functionality have no "non-pattern" equivalent of match(), so the issue doesn't come up. The only one that does, Ruby, uses a different prefix keyword depending if it's pattern or equality based, BUT you have to use the same keyword for all branches. 2. Positional array patterns. How picky should the pattern matching be for list-esque arrays? There's 3 options listed in the RFC as possible ways forward, so I won't repeat them here but just provide a link: https://wiki.php.net/rfc/pattern-matching#positional_array_enforcement 3. Do we want to have a pattern for "match an object's properties without specifying what type the object has to be?" If so, what syntax should it be? Just omitting the class name (producing `$o is {x: 1}` ) is not possible. We tried. :-) Options include: `$o is _(x: 1)` `$o is object(x: 1)` // Your suggestion here. 4. The variable pinning syntax. There's been some concerns about it being non-obvious, which is valid. The main argument for it is "it's what Ruby does", which is not the most compelling argument, but it's not invalid. Is there some other syntax that would work better? Ilija has pondered `{$x}`, which would then potentially allow simple expressions inside the pattern within the {}. I am a little scared of the scope creep potential. :-) But we're open to having this discussion, so please discuss. Please do weigh in and help us decide on a way forward for these. --Larry Garfield

Tim Düsterhus

220 days ago
Hi Am 2026-01-23 20:48, schrieb Larry Garfield:
> The idea of the second being that, if you want just an identity match, > you can skip adding `is` on just that one arm.
I don't understand what that is trying to say.
> 2. Positional array patterns. How picky should the pattern matching be > for list-esque arrays? There's 3 options listed in the RFC as possible > ways forward, so I won't repeat them here but just provide a link: > > https://wiki.php.net/rfc/pattern-matching#positional_array_enforcement
I expect "list patterns" to be reasonably short, such that `array_is_list()` would effectively be constant-time after checking whether the number of elements matches up. As such I would prefer (1) "Include the list check, accept the cost".
> `$o is object(x: 1)`
That would be okay, but no strong feelings.
> 4. The variable pinning syntax. There's been some concerns about it > being non-obvious, which is valid. The main argument for it is "it's > what Ruby does", which is not the most compelling argument, but it's > not invalid. > > Is there some other syntax that would work better? Ilija has pondered > `{$x}`, which would then potentially allow simple expressions inside > the pattern within the {}. I am a little scared of the scope creep > potential. :-) But we're open to having this discussion, so please > discuss.
As I think I said before, I don't need variable pinning at all, but `^` in particular is terrible. `{}` would be okay, but I prefer just not making a decision for now. Best regards Tim Düsterhus

Larry Garfield

219 days ago
On Sat, Jan 24, 2026, at 12:25 PM, Tim Düsterhus wrote:
> Hi > > Am 2026-01-23 20:48, schrieb Larry Garfield: >> The idea of the second being that, if you want just an identity match, >> you can skip adding `is` on just that one arm. > > I don't understand what that is trying to say.
With a per-arm keyword, one could do this: $result = match ($somevar) { is Foo => 'foo', // This matches against the pattern `Foo`, which is a class name. Bar => 'bar', // This matches === against the constant `Bar`, whose value is whatever. }; The main reason we cannot just auto-detect whether it's a pattern is the confusion between a class name and constant, since a constant is a legal literal value for an identity match. With per-arm, you can opt-in to pattern matching individually, at the cost of having to remember to repeat the keyword on every line. With per-block, you only have to add the keyword once at the cost of not being able to selectively use patterns or identity matching. Which tradeoff is better is the option question. (Ilija and I disagree on which to go with.) Or, if someone can suggest a way to allow automatic detection reliably, that would also be most welcome. :-) --Larry Garfield

Weedpacket

218 days ago
On 2026-01-26 07:44, Larry Garfield wrote:
> > With per-arm, you can opt-in to pattern matching individually, at the cost of having to remember to repeat the keyword on every line. With per-block, you only have to add the keyword once at the cost of not being able to selectively use patterns or identity matching. > > Which tradeoff is better is the option question. (Ilija and I disagree on which to go with.) Or, if someone can suggest a way to allow automatic detection reliably, that would also be most welcome. :-) >
If it had to be one or the other, then arms-only would be my choice as the more flexible of the two - and it's the one we have now, so the real question is if "match($foo) is {..." should be added as a shorthand for when all matching is on patterns. I'm leaning toward "yes", especially as scalar literals would pattern match by value identity anyway. Falling back on per-arm "is" would only be necessary to address those class/constant confusions if and when they arise - but when they do you'll want it to be possible to address them. But if "is" were required on every pattern match, that's a lot of "is"ing. People would get into the habit of using it like "case" in a switch statement (or putting backticks around every single identifier in a MySQL query), using it even when a value match is sufficient. And then having it bite them precisely in those cases when there is something like a class/constant confusion.

Weirdan

218 days ago
On Mon, Jan 26, 2026 at 5:48 AM Morgan <weedpacket@varteg.nz> wrote:
> On 2026-01-26 07:44, Larry Garfield wrote: > > > > With per-arm, you can opt-in to pattern matching individually, at the > cost of having to remember to repeat the keyword on every line. With > per-block, you only have to add the keyword once at the cost of not being > able to selectively use patterns or identity matching. > > > > Which tradeoff is better is the option question. (Ilija and I disagree > on which to go with.) Or, if someone can suggest a way to allow automatic > detection reliably, that would also be most welcome. :-) > > > > If it had to be one or the other [...]
Does it have to be one of the other though? Can't we have both? ```php $result = match ($somevar) { is Foo => 'foo', // This matches against the pattern `Foo`, which is a class name. Bar => 'bar', // This matches === against the constant `Bar`, whose value is whatever. }; ```php ```php $result = match ($somevar) is { Foo => 'foo', // This matches against the pattern `Foo`, which is a class name. Bar => 'bar', // This matches against the pattern `Bar`, which is also a class name }; ``` ```php $result = match ($somevar) is { is Foo => 'foo', // This is an error (?) }; ```php We already have somewhat similar behaviour with `readonly` (for properties) and `abstract` (for methods) class modifiers. When you specify it on a class, it applies to all members, but you can also specify it on an individual member.
-- Best regards, Bruce Weirdan mailto: weirdan@gmail.com

Weedpacket

218 days ago
On 2026-01-26 23:53, Bruce Weirdan wrote:
> > > On Mon, Jan 26, 2026 at 5:48 AM Morgan <weedpacket@varteg.nz > <mailto:weedpacket@varteg.nz>> wrote: > > > On 2026-01-26 07:44, Larry Garfield wrote: > > > > > > With per-arm, you can opt-in to pattern matching individually, at > > the cost of having to remember to repeat the keyword on every line. > > With per-block, you only have to add the keyword once at the cost of > > > not being able to selectively use patterns or identity matching. > > > > > > Which tradeoff is better is the option question.  (Ilija and I > > disagree on which to go with.)  Or, if someone can suggest a way to > > allow automatic detection reliably, that would also be most welcome. :-) > > > > > > > If it had to be one or the other [...] > > > Does it have to be one of the other though? Can't we have both? >
> > ...so the real question is if "match($foo) is {..." should be added
as a shorthand for when all matching is on patterns.

Larry Garfield

218 days ago
On Mon, Jan 26, 2026, at 7:33 AM, Morgan wrote:
> On 2026-01-26 23:53, Bruce Weirdan wrote: >> >> >> On Mon, Jan 26, 2026 at 5:48 AM Morgan <weedpacket@varteg.nz >> <mailto:weedpacket@varteg.nz>> wrote: >> >> > On 2026-01-26 07:44, Larry Garfield wrote: >> > > >> > > With per-arm, you can opt-in to pattern matching individually, at >> > the cost of having to remember to repeat the keyword on every line. >> > With per-block, you only have to add the keyword once at the cost of >> > > not being able to selectively use patterns or identity matching. >> > > >> > > Which tradeoff is better is the option question.  (Ilija and I >> > disagree on which to go with.)  Or, if someone can suggest a way to >> > allow automatic detection reliably, that would also be most welcome. :-) >> > > >> > >> > If it had to be one or the other [...] >> >> >> Does it have to be one of the other though? Can't we have both? >> > > > > ...so the real question is if "match($foo) is {..." should be added > as a shorthand for when all matching is on patterns.
It is technically possible to implement it both ways, I believe. Whether that is too confusing and messy is another question. --Larry Garfield

Rowan Tommins [IMSoP]

217 days ago
On 23 January 2026 19:48:31 GMT, Larry Garfield <larry@garfieldtech.com> wrote:
> >4. The variable pinning syntax. There's been some concerns about it being non-obvious, which is valid. The main argument for it is "it's what Ruby does", which is not the most compelling argument, but it's not invalid.
As I think I've said before, for the average PHP user, the question isn't going to be "what's the syntax for variable pinning?", it's going to be "how do I tell if this variable is input to or output from the pattern?" In other words, variable binding and variable pinning syntaxes need to be designed as a pair. Take this example from the RFC: if ($p is Point(z: $z, x: 3, y: $y)) To a PHP programmer who has never seen pattern matching before, that looks like it's testing three properties, not doing some destructuring assignments. If variable pinning isn't supported, they'll soon learn; but if both interpretations are supported with subtly different syntax, I can see it becoming a major source of bugs. Maybe that's why a lot of languages don't support pinning at all? The only way I can think to make this less painful is to have some pair of syntaxes that signals "in" and "out", or "read" and "set", in some way. For example, this looks rather ugly, and may not even be parseable, but it signals the behaviour fairly well without having to read the manual: if ($p is Point(z: $z=, x: 3, y: $y=)) if ($p is Point(z: ==$z, x: 3, y: ==$y)) if ($p is Point(z: ==$z, x: 3, y: $y=)) Regards, Rowan Tommins [IMSoP]

Jordi Kroon

215 days ago
Hi,
> 1. match() statements. There's two ways we could do this: All-branches, or individual branches. This is particularly important as, in practice, we expect match() to be the most common use of patterns.
I prefer to have one syntax. The most flexible one is individual arms. And to me it also looks more clean from the functional side. If we support both we should also disallow the use of mixing all arms and individual arms. Thus this syntax should return an error. $result = match ($somevar) is { // Error! is Foo => 'foo', is Bar => 'bar', is Baz|Beep => 'baz', };
> Positional array patterns. How picky should the pattern matching be for list-esque arrays? There's 3 options listed in the RFC as possible ways forward, so I won't repeat them here but just provide a link: > > https://wiki.php.net/rfc/pattern-matching#positional_array_enforcement
From a cost perspective it would be best to keep it to a minimum. But array’s exist in different styles where it matters to have at least a few edge-cases covered. Especially when converting between data formats (Serialise, JSON, ..) I would personally discourage to return true with the following cases: Given: $a = ['a', 'b', 'c']; $a is [2 => 'c', 0 => 'a', 1 => 'b']; Should return `false`, because when converting to other data formats. The style will change from numerical to key'ed (associative style). The same applies to the inverse. Though given: $a = ['a', 'b', 'c']; $a is [0 => 'a', 1 => 'b', 2 => 'c']; Should return `true`, because they keep the numerical order. And when converted it will be the same.
> 3. Do we want to have a pattern for "match an object's properties without specifying what type the object has to be?" If so, what syntax should it be? Just omitting the class name (producing `$o is {x: 1}` ) is not possible. We tried. :-) Options include: > > `$o is _(x: 1)` > `$o is object(x: 1)`
I am not a favour of using shorthands. They tend to make code more difficult to read and understand.  Thus `$o is object(x: 1)` is what I suggest as well. 4.
>  The variable pinning syntax. There's been some concerns about it being non-obvious, which is valid. The main argument for it is "it's what Ruby does", which is not the most compelling argument, but it's not invalid.
I strongly agree that the the `^` is not a good choice. It is difficult to understand,I personally think pinning at all is not important. Though if we proceed forward on this `{$x}` would make most sense since it is more in line with string interpolation syntax. And like you mentioned it opens more possibilities. Regards, Jordi

Larry Garfield

110 days ago
On Mon, Dec 1, 2025, at 3:36 PM, Larry Garfield wrote:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, > pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had > this one in-progress for 5 years. :-) (Though it was inactive for many > of those years, in fairness.) Pattern matching was intended as the > next follow up to Enums, as it's a stepping stone toward full ADT > support. However, we also feel it has enormous benefit on its own for > simplifying complex comparisons. > > This RFC has been through numerous iterations, including a full > implementation rewrite just recently that made a number of features > much easier. We have therefore included two patterns that were > previously slated for later inclusion but turned out to be trivially > easy in the new approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are > looking for feedback. > > Naturally given the timing, we will not be calling a vote until at > least late January, regardless of how the discussion goes. So, plenty > of time to express your support. :-) > > -- > Larry Garfield > larry@garfieldtech.com
Hi all. Just an update here; we've not forgotten this RFC. Ilija has just been swamped with other PHP work of late, and hasn't been able to get back to some further investigation we want to do around precedence handling. We should be able to get back to this RFC in a few weeks, hopefully with enough time to get it into 8.6 still. Stay tuned. :-) --Larry Garfield

Sarina Corrigan

81 days ago
On Mon, Dec 1, 2025 at 4:38 PM Larry Garfield <larry@garfieldtech.com> wrote:
> Hi folks. Ilija and I would like to present our latest RFC endeavor, > pattern matching: > > https://wiki.php.net/rfc/pattern-matching > > You may note the date on the RFC is from 2020. Yes, we really have had > this one in-progress for 5 years. :-) (Though it was inactive for many of > those years, in fairness.) Pattern matching was intended as the next > follow up to Enums, as it's a stepping stone toward full ADT support. > However, we also feel it has enormous benefit on its own for simplifying > complex comparisons. > > This RFC has been through numerous iterations, including a full > implementation rewrite just recently that made a number of features much > easier. We have therefore included two patterns that were previously > slated for later inclusion but turned out to be trivially easy in the new > approach. (Variable pinning and numeric comparison.) > > Nonetheless, there are two outstanding questions on which we are looking > for feedback. > > Naturally given the timing, we will not be calling a vote until at least > late January, regardless of how the discussion goes. So, plenty of time to > express your support. :-) > > -- > Larry Garfield > larry@garfieldtech.com
Thanks for putting this together! It's clear a lot of thought went into this. I am not a regular on this list but there are a few thoughts I had reading through it that I wanted to share. One of the stated prime use cases of this RFC is destructuring, but the main behavior is to resolve to a boolean. It feels like these two ideas, destructuring and validating, are at odds with each other at times.
> The is operator is technically a comparison operator, and always returns
a boolean true or false.
> The entire pattern either succeeds or fails. No variables will be bound
unless the entire pattern matches When the only failure signal is a false return, every destructuring site has to check it manually. 9 times out of 10 when you are destructuring, you already know the shape of your data. With the proposed implementation, outside of match(), that looks like: if (!($point is ['x' => $x, 'y' => $y])) { throw new InvalidArgumentException(); } Or more simply: $point is ['x' => $x, 'y' => $y] ?: throw new InvalidArgumentException(); This will get quite repetitive. Most languages I've looked at provide at least one form where a failed match cannot pass silently. - Elixir/Erlang, Scala, and Haskell all error when structure doesn't match - Rust and Swift's bind-into-scope forms require an else block that must exit the scope (`let ... else` and `guard let ... else`) - Ruby allows for silently failing with `in`, and throwing with `=>` - C#, the exception, relies on compiler definite-assignment analysis to prevent code from ever reading a maybe-unbound variable, which is a safety net that PHP doesn't have I propose we follow Ruby in this, and allow both. Perhaps on failure `is` returns false, while something like `is!` throws (exact syntax aside, as I recognize ! means not in PHP). In addition to reducing repetition, an engine-thrown error can say exactly which part of the pattern failed. That behavior can't be reasonably reproduced with a manually thrown error.
> For object patterns (only), if the variable name to extract to is the
same as the name of the property, then the property name may be omitted. I like the :$id shorthand, but since object patterns mirror named argument syntax (albeit extracting rather than passing values), this effectively introduces argument punning syntax into PHP. Whatever is decided here likely becomes the standard for punning in calls (new Point(:$x)), since anything else would be inconsistent. So this feels like a separate named-argument decision being settled inside a pattern-matching RFC. I'd suggest that it instead belongs in its own punning proposal that naturally applies to pattern matching. That aside, would an equivalent shorthand be available with variable pinning, such as :^$x?
> Some languages support a dedicated syntax to apply additional
restrictions (pattern based or otherwise) to a matched variable. For example, only match if a given string property is one of a particular set of values, but if it is, then bind that value to the variable.
> > Because the proposed syntax supports full DNF pattern combinations, such
behavior is achievable without a dedicated syntax. Specifically, a bind pattern and a filtering pattern may be combined with an &. This approach falls apart with union types and DNF. Binding a value while filtering it against a union type requires $x & (int|string), which isn't valid DNF. The DNF form, ($x & int) | ($x & string), is explicitly forbidden in the RFC as a binding cannot be nested within a union. Other languages use @ for this, which is already taken in PHP. My suggestion would be to use `is` when validating and binding, such as $p is Point(x: $x is int, y: $y). This feels natural, although it does differ from the base usage of `is`, since the left operand goes from a tested value to a binding target. Perhaps a dedicated keyword like `of` ($x of int) avoids that at the cost of reserving another word.
> By default, array matching is exhaustive. That is, the arity of the array
and pattern must match The "array shape" example with patterns (['a' => 'A'|'a', 'b' => string]) omits the count($assoc) === 2 check in its stated equivalent, while the sequential shape example ([int, int, int, mixed]) includes one. Is the associative example missing the arity check, or is exhaustiveness intentionally relaxed there?

Alexander Egorov

79 days ago
Greetings! Thanks for the awesome RFC, I very much like the suggestion and would highly welcome it in general. But since the proposed update is really huge, I've got some concerns about some specific use-cases of it. My main concern is about variable binding / pinning. To me personally your current suggestion looks very counter-intuitive, which means that when I see a variable inside the patter, I would strongly expect it as a reference to an existing variable as part of the pattern: $b = 5; $a = 3; if ($a is $b) {...} Which translates to if $a === $b. Of course, same relates to more complex patterns. On Sun, Jun 14, 2026 at 9:08 PM Larry Garfield <larry@garfieldtech.com> wrote:

Alexander Egorov

79 days ago
Sorry for accidentally sending the letter :( But I will continue the message. In other words, all "mentions" of a variable, since they follow the "usual" syntax of PHP, are expected to be the usages of these variables. On the contrary: using them as variable-bindings would be error-prone. In the example above, an existing variable $b would be silently overwritten by whatever matched. So, from my point of view, "pinning" should be the default behaviour. As for the binding itself, I'm not really against it, but I personally don't see much use of it, even if we come up with some dedicated syntax for it. For example, if I match a variable against some pattern, I don't really need to bind those values further, because they are very much deterministic. What I mean is that in your proposal in general, we don't have very complex patterns (compared to regexp, for example), to remember the exact matched value. And also, it looks like it always easy to obtain the needed value ad-hoc: if ($p is Point(x: $x)) { // We can always access $p->x } Variable binding would be most useful, as I've said, if we had complex patterns and we don't know beforehand WHAT exactly matched. Like, if we were searching for something which matches the pattern. For example, if we "matched" an array against some pattern, and would want to "capture" all the elements that did match. But as I understand it, it is currently beyond the scope of current RFC. My other concern is about object-matching. In your examples, you give the following: $e is Colorable(color: 'green', age: 4)&Example; My belief is that such an approach - checking for a non-existent property of a contract ("age") is kinda dirty. We should not allow that. If we're checking specifically against a defined contract, which does not have an $age property, we should not be able to include this "property" in the pattern, and such pattern should give a "property undefined" error. Another concern about object-matching is positional matching of properties. Same as above, to me it is kinda dirty. What I mean is that if we're working with some "key" => "value" structure, the exact position of the "key" in this structure must be irrelevant to us. If we want to get $array['key'] or $object->property, we should never rely on how exactly this key is "positioned" inside the data-structure. On Sun, Jun 14, 2026 at 9:22 PM Alexander Egorov <amego2006@gmail.com> wrote: