rectorphp/rector · error · ShouldNotHappenException

"%s" is deprecated, as the pipe chain length that stays read

Error message

"%s" is deprecated, as the pipe chain length that stays readable depends on the context of surrounding code

What it means

Rector\Php85\Rector\Expression\NestedFuncCallsToPipeOperatorRector is deprecated: refactor() throws Rector\Exception\ShouldNotHappenException on the first Expression or Return_ node. The rule rewrote nested calls like strtolower(trim($s)) into PHP 8.5 pipe chains, but how long a chain stays readable depends entirely on the surrounding code, so the old MINIMUM_DEPTH threshold (default 3, shown in the rule's own configured code sample) cannot be a safe default. The rule only loads on PhpVersionFeature::PIPE_OPERATOER (PHP 8.5) targets.

Source

Thrown at rules/Php85/Rector/Expression/NestedFuncCallsToPipeOperatorRector.php:67

        $result = $input
            |> htmlspecialchars(...)
            |> strtolower(...)
            |> trim(...);
    }
}
CODE_SAMPLE
, [self::MINIMUM_DEPTH => 3])]);
    }
    public function getNodeTypes(): array
    {
        return [Expression::class, Return_::class];
    }
    /**
     * @param Expression|Return_ $node
     */
    public function refactor(Node $node): ?Node
    {
        throw new ShouldNotHappenException(sprintf('"%s" is deprecated, as the pipe chain length that stays readable depends on the context of surrounding code', self::class));
    }
    public function provideMinPhpVersion(): int
    {
        return PhpVersionFeature::PIPE_OPERATOER;
    }
}

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Remove NestedFuncCallsToPipeOperatorRector from rector.php.
  2. Rewrite specific hot spots into pipe chains by hand where the chain actually improves readability.
  3. If your team has a hard style rule, encode it in a custom rule scoped to your codebase instead of re-enabling this one.

Example fix

// before (rector.php)
->withConfiguredRule(NestedFuncCallsToPipeOperatorRector::class, [
    NestedFuncCallsToPipeOperatorRector::MINIMUM_DEPTH => 3,
])

// after (rector.php) - removed; convert by hand where it reads better

// manual (PHP 8.5):
$name = $input |> trim(...) |> strtolower(...);
Defensive patterns

Strategy: validation

Validate before calling

use Rector\Php85\Rector\Expression\NestedFuncCallsToPipeOperatorRector;

$rules = [/* your list */ NestedFuncCallsToPipeOperatorRector::class];
if (in_array(NestedFuncCallsToPipeOperatorRector::class, $rules, true)) {
    throw new InvalidArgumentException('Rule deprecated; write pipe chains manually where readability improves');
}

Try / catch

try {
    exit($rectorApplication->run());
} catch (\Rector\Exception\ShouldNotHappenException $e) {
    if (str_contains($e->getMessage(), 'NestedFuncCallsToPipeOperator')) {
        fwrite(STDERR, 'Remove the rule; chain length is a per-case readability judgement, not a fixed threshold.' . PHP_EOL);
        exit(1);
    }
    throw $e;
}

Prevention

When it happens

Trigger: The rule is still listed in a PHP 8.5 rule list and rector visits any expression statement or return statement; refactor() throws on the first match, even if the old MINIMUM_DEPTH configuration was passed (it is ignored).

Common situations: Enabling all Php85 rules after a rector upgrade; configs copied from pipe-operator announcement posts; CI failing on the very first analyzed statement.

Related errors


AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21). Data as JSON: /api/errors/018b9db63796a376. Report an issue: GitHub.