phpstan-symfony ships a stub correcting Symfony\Component\HttpFoundation\ParameterBag::keys() to @return list<array-key>, but StubFilesExtensionLoader only loads it for symfony/http-foundation < 7.4:
// src/Stubs/Symfony/StubFilesExtensionLoader.php
if ($this->isInstalledVersionBelow('symfony/http-foundation', '7.4.0.0')) {
$files[] = $stubsDir . '/Symfony/Component/HttpFoundation/ParameterBag.stub';
}
From Symfony 7.4 on, Symfony declares its own @return list<string> on keys(), so PHPStan uses that instead. But that PHPDoc is inaccurate: PHP normalises numeric-string array keys to integers, so keys are int|string at runtime (e.g. ?0[]=x produces the int key 0). Symfony declined to correct it (symfony/symfony#60411).
The result is a false positive: a correct runtime guard over ->keys() is reported as always-true, and removing it reintroduces a real TypeError (a numeric param name → int key passed to a string parameter). This is a regression — the stub made this correct for < 7.4.
Code snippet that reproduces the problem
Not reproducible on phpstan.org/r — it needs the phpstan-symfony extension and symfony/http-foundation >= 7.4. Minimal case:
<?php declare(strict_types=1);
use Symfony\Component\HttpFoundation\Request;
function coreField(string $name): bool { return false; }
function f(Request $request): void
{
foreach ($request->query->keys() as $key) {
if (!is_string($key)) {
continue;
}
coreField($key);
}
}
Run PHPStan (level 8, phpstan-symfony enabled) with symfony/http-foundation >= 7.4.
Expected output
No error — keys() returns list<int|string> at runtime, so is_string($key) is a valid narrowing (as it is on symfony/http-foundation < 7.4, where the stub is applied).
Actual output
Call to function is_string() with string will always evaluate to true. function.alreadyNarrowedType
Environment
- phpstan/phpstan-symfony: 2.0.20 (also reproduced on
2.0.x-dev)
- phpstan/phpstan: 2.2.x — level 8, bleedingEdge (
treatPhpDocTypesAsCertain)
- symfony/http-foundation: 7.4.x and 8.1.x
- PHP 8.x
Suggested fix
The ParameterBag.stub is still accurate for 7.4+, so keep applying it — e.g. bump the gate from < 7.4.0.0 to < 8.3.0.0 (8.2 still ships the inaccurate PHPDoc). Related: #439 (original report), #443 (the stub).
phpstan-symfony ships a stub correcting
Symfony\Component\HttpFoundation\ParameterBag::keys()to@return list<array-key>, butStubFilesExtensionLoaderonly loads it forsymfony/http-foundation < 7.4:From Symfony 7.4 on, Symfony declares its own
@return list<string>onkeys(), so PHPStan uses that instead. But that PHPDoc is inaccurate: PHP normalises numeric-string array keys to integers, so keys areint|stringat runtime (e.g.?0[]=xproduces the int key0). Symfony declined to correct it (symfony/symfony#60411).The result is a false positive: a correct runtime guard over
->keys()is reported as always-true, and removing it reintroduces a realTypeError(a numeric param name → int key passed to astringparameter). This is a regression — the stub made this correct for< 7.4.Code snippet that reproduces the problem
Run PHPStan (level 8, phpstan-symfony enabled) with
symfony/http-foundation >= 7.4.Expected output
No error —
keys()returnslist<int|string>at runtime, sois_string($key)is a valid narrowing (as it is onsymfony/http-foundation < 7.4, where the stub is applied).Actual output
Environment
2.0.x-dev)treatPhpDocTypesAsCertain)Suggested fix
The
ParameterBag.stubis still accurate for 7.4+, so keep applying it — e.g. bump the gate from< 7.4.0.0to< 8.3.0.0(8.2 still ships the inaccurate PHPDoc). Related: #439 (original report), #443 (the stub).