Skip to content

Load the ParameterBag stub until symfony/http-foundation 8.2 - #519

Open
ChrisAbner wants to merge 1 commit into
phpstan:2.1.xfrom
ChrisAbner:parameter-bag-keys-stub-until-8.2
Open

ChrisAbner wants to merge 1 commit into
phpstan:2.1.xfrom
ChrisAbner:parameter-bag-keys-stub-until-8.2

Conversation

@ChrisAbner

Copy link
Copy Markdown

Closes #510

ParameterBag::keys() and all() return the parameters with their own keys, and PHP turns numeric array keys into integers, so ?0=x gives the key 0 as an int on every released symfony/http-foundation version. Symfony 7.4+ documents keys() as list<string>, all() as array<string, mixed> and the iterator as <string, mixed>, and StubFilesExtensionLoader stopped loading ParameterBag.stub from 7.4 on. So a correct is_string($key) guard over those keys is reported as always true, and removing it lets an int reach a string parameter. This PR loads ParameterBag.stub below 8.2.0.0, and the stub now covers all() too (array<mixed>).

Why 8.2: symfony/symfony#66127, merged into 8.2, makes keys() and the iterator cast the keys to string and documents all() as array<mixed>, so from 8.2 on the upstream types match what the code returns and should win, as #481 intends. 8.2 is not released yet (the latest is 8.1.10). Raising the existing < 7.4.0.0 block would also bring back the InputBag and Request stubs, so ParameterBag.stub gets its own condition and the other two keep theirs. The issue suggests < 8.3.0.0, but 8.2 is where the runtime behaviour changes. The all() entry uses the same type as 8.2 upstream.

Verification. An external project (phpstan/phpstan 2.3.4, this extension through a path repository, level 8), with the issue's snippet, the same guard over array_keys($bag->all()), the key type of foreach ($request->query as $key => $value), and the actual keys of Request::create('/search?0=x&78=y&q=z')->query->keys():

symfony/http-foundation Keys at runtime keys() before → after $bag->all() before → after is_string($key) reported (keys(), all()) before → after
6.4.49 0, 78 are int list<int|string> → same array → array<mixed> no → no
7.4.22 int list<string> → list<int|string> array<string, mixed> → array<mixed> yes → no
8.0.x-dev int list<string> → list<int|string> array<string, mixed> → array<mixed> yes → no
8.1.10 int list<string> → list<int|string> array<string, mixed> → array<mixed> yes → no
8.2.x-dev '0', '78' are string list<string> → same array<mixed> → same yes → yes (the guard is redundant there)

On 7.4–8.1 the foreach key goes from string to int|string; on 8.2 it stays string. InputBag::get(), $request->request->get('a', 'x') and $bag->all('foo') give the same types before and after on every version.

tests/Type/Symfony/data/parameter_bag.php asserts the types of keys(), all(), all('foo') and the foreach key. With the suite's symfony/http-foundation 6.4 the stub was already loaded, so only the all() assertions fail without this change; they pass with it, on the highest dependencies (6.4.49) and with --prefer-lowest (5.4.54, PHPStan 2.3.0). phpunit (448 tests), parallel-lint, PHPStan level 8 and phpcs (build-cs) pass.

Not covered: the suite never installs symfony/http-foundation 7.4 or later (dev constraint ^5.4 || ^6.1), so no test reaches the new version condition itself; once #483 widens that constraint, parameter_bag.php covers it as is. InputBag::all() without arguments still has string keys on every Symfony version, because InputBagDynamicReturnTypeExtension builds that type itself; that is older than the 7.4 gate and changing it would affect all versions, so I left it out.

ParameterBag::keys() and all() return the parameters' own keys, so a
numeric parameter name comes back as an int on every released version,
while Symfony 7.4+ documents keys() as list<string> and all() as
array<string, mixed>. The stub was only loaded below 7.4, so a correct
is_string() guard over those keys was reported as always true.
Symfony 8.2 casts the keys to strings and documents all() as array<mixed>
(symfony/symfony#66127), so the stub is now loaded below 8.2, and it
covers all() as well. The InputBag and Request stubs keep their 7.4 gate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ParameterBag::keys() stub not applied for Symfony ≥ 7.4 → false-positive is_string() alwaysNarrowedType

1 participant