Skip to content

Conversation

@thekid
Copy link
Collaborator

@thekid thekid commented Dec 21, 2025

This PR extends the basic type matching capabilities and implements all of https://wiki.php.net/rfc/pattern-matching

Implementation status

Notes

  • The PHP PR currently uses a different match syntax as the one described in the RFC, see here
  • The PHP PR currently includes the range syntax listed under Future scope in the RFC

See also

@thekid thekid marked this pull request as draft December 21, 2025 15:48
@thekid thekid added the enhancement New feature or request label Dec 21, 2025
@thekid thekid marked this pull request as ready for review December 21, 2025 19:19
@thekid
Copy link
Collaborator Author

thekid commented Dec 21, 2025

In Python, the following works:

match command.split():
    case ["drop", *objects]:
        for obj in objects:
            character.drop(obj, current_room)
    # The rest of your commands go here

Maybe we should be able to bind to variables when using the spread operator, too?


The rest pattern gathers all unmatched values into an array and passes it to the binding variable:

if ([1, 2, 3] is [1, ...$rest]) {
  var_dump($rest); // [2, 3]
}

if (['one' => 1, 'two' => 2] is (['one' => 1, ...$rest]) {
  var_dump($rest); // ['two' => 2]
}

🧪 https://gist.github.com/thekid/5f85c8ec6e14644b06cb9b100644529e


See also:

@thekid
Copy link
Collaborator Author

thekid commented Dec 23, 2025

Variable bindings can replace destructuring assignments in most cases:

list() is Note
[$a, $b]= $list $list is [$a, $b]
[$a, , $b]= $list $list is [$a, mixed, $b]
[[$a, $b], $c]= $nested $nested is [[$a, $b], $c]
['key' => $key]= $map $map is ['key' => $key]
[$a[]]= $list *Can only bind variables *
[$a['key']]= $list *Can only bind variables *
[$this->member]= $list *Can only bind variables *

The is operator also has the benefit of not raising errors when $list does not contain the number of elements necessary for destructuring, and instead returns false:

if ($list is [$a, $b, ...]) {
  var_dump($a, $b);
}

// Equivalent of, guarded by type and length checks to prevent errors
if (is_array($list) && sizeof($list) >= 2) {
  [$a, $b]= $list;
  var_dump($a, $b);
}

@thekid
Copy link
Collaborator Author

thekid commented Dec 24, 2025

Following the idea of replacing destructuring assignments, I wanted to see whether I would be able to parse Composer files. Here's what I came up with:

use lang\FormatException;
use util\cmd\Console;

class ComposerFile {

  private function __construct(
    public private(set) string $name,
    public private(set) ?string $type,
    public private(set) array $dependencies,
    public private(set) array $development= [],
    public private(set) array $scripts= [],
  ) { }

  private static function parse(string $input): self {
    if (json_decode($input, true) is [
      'name'        => $name & string,
      'type'        => $type & ?string ?? null,
      'require'     => $dependencies & array,
      'require-dev' => $development & array ?? [],
      'scripts'     => $scripts & array ?? [],
      ...
    ]) return new self($name, $type, $dependencies, $development, $scripts);

    throw new FormatException('Cannnot parse given input');
  }

  public static function main(array $args): void {
    file_get_contents($args[0])
      |> self::parse(...)
      |> Console::writeLine(...)
    ;
  }
}

The missing piece is having a way to make certain keys optional, which I implemented using the null-coalescing operator, inspired by https://wiki.php.net/rfc/destructuring_coalesce. Note: This RFC was declined for confusing syntax, see https://externals.io/message/118829, we might need a different approach here too. JS uses = <default>, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring


🧪 https://gist.github.com/thekid/97646f250ac2868fb98052019fe8973d (with ?? <default>)
🧪 https://gist.github.com/thekid/12b2eae6cce613b9f75928f93f246370 (with = <default>)

@thekid
Copy link
Collaborator Author

thekid commented Dec 25, 2025

Another interesting idea is integrating is with catch, see https://github.com/tc39/proposal-pattern-matching?tab=readme-ov-file#integration-with-catch

try {
  // ...
} catch ($e is Error(status: 404)) {
  echo 'Not found!';
}

🧪 https://gist.github.com/thekid/6f6798bc05995caac6b7e825d6d5a76f

@thekid
Copy link
Collaborator Author

thekid commented Dec 28, 2025

Supporting implementations of the ArrayAccess and Countable interfaces in array structure patterns may be worthwhile:

if (new ArrayObject([1, 2, 3]) is [$a, $b, $c]) {
  var_dump($a, $b, $c); // 1 2 3
}

🧪 https://gist.github.com/thekid/773803d84b357799ffd65f855bc0181c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants