# [RFC] Pattern Matching — missing keys and strict int

- Thread: https://externals.io/message/132899
- Original archive: https://news-web.php.net/php.internals/132899
- Started by kevinblakelopez@gmail.com on 2026-10-22T12:30:14+00:00
- Messages: 1
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #132899 — kevinblakelopez@gmail.com — 2026-10-22T12:30:14+00:00

URL: https://externals.io/message/132899#132899

> Hello Larry and Ilija,
> 
> I maintain a production PHP application that rejects a model’s JSON reply unless every field is present and of an exact type. Pattern matching is the language feature that would replace those checks. Two outcomes in the current draft would make that replacement accept replies we currently reject. Both are constraints on the rules you already wrote, not requests for new syntax.
> 
> The RFC’s array-pattern equivalents use `array_key_exists()` before comparing the value. A missing key therefore fails the pattern. The implementation note on https://github.com/php/php-src/pull/20574 says the draft reads a missing element as null, so this is true:
> 
>     ['a' => 42] is ['b' => null]
> 
> Please keep the RFC text. For this parser, a missing key and an explicit null are different successful results, and a value of any other type is a failure:
> 
>     function partQuote(array $p): ?array {
>         if (!is_string($p['part'] ?? null)) {
>             return null;
>         }
>         if (array_key_exists('quote', $p) && $p['quote'] !== null && !is_string($p['quote'])) {
>             return null;
>         }
>         $out = ['part' => $p['part']];
>         if (array_key_exists('quote', $p)) {
>             $out['quote'] = $p['quote']; // null is kept; the key was present
>         }
>         return $out;
>     }
> 
>     partQuote(['part' => 'A']);                    // ['part' => 'A']
>     partQuote(['part' => 'A', 'quote' => null]);   // ['part' => 'A', 'quote' => null]
>     partQuote(['part' => 'A', 'quote' => 1]);      // null
> 
> If ['quote' => null] matched a missing quote, the first two calls would become the same pattern and the stored shape would be wrong.
> 
> The same parser requires a real int for a score-like field, in 0..100 inclusive. `is_int()` is the type check; the range is applied only after that. These all fail:
> 
>     ['strength' => '80']
>     ['strength' => 80.0]
>     ['strength' => 101]
>     ['relevant' => 1]       // `is_bool()`, same rule: no coercion
>     ['relevant' => 'true']
> 
> and ['strength' => 80] with ['relevant' => true] passes. An int pattern therefore has to reject a numeric string and a float. A range pattern such as >=0 & <=100 has to do the same when it is standing in for that field; "80" and 80.0 are inside the numeric range and are still the wrong type.
> 
> I am not asking for a change to the feature set. The written array patterns already say `array_key_exists()`, and an int pattern that follows `is_int()` is what this code can adopt.
> 
> Kevin Lopez
