Skip to content

Support the PHP 8.5 pipe operator - #12964

Open
alies-dev wants to merge 2 commits into
vimeo:masterfrom
alies-dev:alies-dev/support-pipe-operator
Open

alies-dev wants to merge 2 commits into
vimeo:masterfrom
alies-dev:alies-dev/support-pipe-operator

Conversation

@alies-dev

Copy link
Copy Markdown
Contributor

Psalm infers mixed for every pipe (|>) expression, and crashes when the right-hand side is a partial application.

Result type

Before

<?php
/**
 * @psalm-pure
 * @return list<array-key>
 */
function keys(array $array): array {
    return $array |> array_keys(...); // MixedReturnStatement
}

After

    return $array |> array_keys(...); // no issue

Partial application on the right-hand side

Before

<?php
function add(int $a, int $b): int { return $a + $b; }

$first = 3 |> add(?, 1); // crash: AssertionError assert(!$this->isPartialFunctionApplication())

After

$first = 3 |> add(?, 1); // int

With two or more ?, or named arguments before ..., Psalm analyses the operands but infers mixed until #12915 lands. A partial application inside an operand (add(?, 1) |> $f) still crashes, like $f = add(?, 1); does on master; #12915 fixes both.

Fixes #11865

- The pipe is lowered to one virtual call analysed by the existing call
  analysers, so its type, argument checks, purity, unused-variable
  tracking and left-hand taint match the hand-desugared call
- A partial application PHP compiles to a direct call gets the piped
  value in its placeholder; other partial applications stay mixed until
  closures for them are supported
- The left-hand side is analysed once, before the right-hand side
  whenever the order can matter
- Each call of a chain starts at its own callee, as calls are told apart
  by their start (issue dedupe, taint specialization)
- `??` and psalter's unused-assignment removal treat a pipe as a call
- The scanner and visitor guards against partial applications are the
  ones of vimeo#12915, so neither PR conflicts with the other
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

✅ Performance: unchanged · Issues: unchanged

Project Memory (MB) Wall (s) Issues Weighted
✅ laravel 1 906 120.0 30 850 51 851
✅ symfony 3 378 123.4 $\color{gray}{\textsf{+14.9\%}}$ re-run 52 855 88 405
✅ doctrine-orm 443 17.9 $\color{gray}{\textsf{-1.1\%}}$ 4 601 5 589
✅ psl 663 22.7 $\color{gray}{\textsf{-0.3\%}}$ 3 622 5 569
✅ phpunit 528 18.4 $\color{gray}{\textsf{-0.1\%}}$ 4 195 1 285
✅ php-parser 255 15.9 $\color{gray}{\textsf{-1.3\%}}$ 18 020 20 643
✅ amphp-amp 60 1.3 $\color{gray}{\textsf{-0.8\%}}$ 69 79
✅ webmozarts-assert 269 2.8 $\color{gray}{\textsf{-1.4\%}}$ 642 2 717
✅ drupal 5 388 174.5 $\color{gray}{\textsf{+0.2\%}}$ 107 684 162 779
Total 12 889 497.0 $\color{gray}{\textsf{+3.3\%}}$ 222 538 338 917
How to read this table
  • Memory: Psalm's own peak figure, threads=1. More than +5 % fails, unless the PR changes composer.json.
  • Wall: base and head run on the same runner; ±10 % is noise. Above +20 % warns, never fails. re-run: a reversed second run was taken and the faster kept.
  • Issues: head count, then $\color{red}{\textsf{+added}}$ $\color{green}{\textsf{-removed}}$ against base.
  • Weighted: issues weighted by error level (levels 1–8 as is, always-errors 10, feature-specific and purity suggestions 0). The projects' own code is pinned and their dependencies resolve identically for base and head, so a change means Psalm reports more, fewer or differently serious issues. A rise is not necessarily a regression.
  • Total: sum over the projects compared on both sides. Thresholds apply per project, not to the total.
  • Colors: $\color{red}{\textsf{red}}$ past a fail or warn threshold (or more issues), $\color{green}{\textsf{green}}$ an improvement past noise (or fewer issues), $\color{gray}{\textsf{gray}}$ noise.
  • Performance: regressed if a head run crashed or timed out or a memory or wall delta is red, else improved if one is green, else unchanged. Its icon is the check result: ❌ fail · ⚠️ warn · ✅ pass; the icon next to each project is that project's result, 💥 = not comparable: base crashed or timed out (counts as ⚠️).
  • Issues (heading): the total weighted change, increased · decreased · unchanged. Informational, never fails the check.

head be58353 · base cee37c4 (merge-base) · workflow run

…-pipe-operator

# Conflicts:
#	src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant