如何让PHPStan识别断言数组键存在的自定义函数
让PHPStan识别自定义的数组键断言函数
PHPStan可以识别直接使用array_key_exists进行数组键存在性检查的代码,示例如下:
<?php declare(strict_types = 1); /** @return array<mixed> */ function returnMixedArray(): array { return []; } /** * @return array{ * existing_key: mixed * } */ function narrowMixedArrayKeys(): array { $foo = returnMixedArray(); assert(array_key_exists('existing_key', $foo)); return $foo; }
但当需要批量检查多个数组键的存在性时,我编写了一个通用断言函数,PHPStan却无法理解其类型约束意图:
<?php declare(strict_types = 1); /** * @param array<int|string> $array * @param string ...$keys */ function assertKeys(array $array, string ...$keys): void { foreach ($keys as $key) { assert(array_key_exists($key, $array)); } }
请问有没有办法让PHPStan识别这个自定义的数组键断言函数?
我明白应该使用对象而非数组,但目前需要维护遗留代码并保障其安全性。
解决方案:利用PHPStan的@phpstan-assert注解与泛型
要让PHPStan理解assertKeys的断言逻辑,需要通过泛型定义和PHPStan专用注解明确函数对数组类型的约束:
1. 修改函数的PHPDoc与实现
更新assertKeys的注解,用泛型绑定键类型,并通过@phpstan-assert告知PHPStan函数执行后的数组类型变化:
<?php declare(strict_types = 1); /** * @template TKey of string * @template TValue * @param array<TKey|string, TValue> $array * @param TKey ...$keys * @phpstan-assert array<TKey, TValue> $array */ function assertKeys(array $array, string ...$keys): void { foreach ($keys as $key) { if (!array_key_exists($key, $array)) { throw new \InvalidArgumentException("Missing required array key: {$key}"); } } }
2. 关键细节说明
- 泛型
TKey:约束传入的键与数组键的类型一致性,避免类型不匹配的问题。 @phpstan-assert注解:明确告诉PHPStan,执行此函数后,输入的$array必然包含所有传入的$keys,从而完成类型收窄。- 替换
assert为异常抛出:assert在生产环境可能被禁用,改用抛出InvalidArgumentException既保证运行时安全,也不影响PHPStan的类型推断。
3. 使用示例
调用修改后的assertKeys后,PHPStan会自动识别数组的键存在性:
/** @return array<mixed> */ function getLegacyData(): array { return ['id' => 123, 'name' => 'test']; } function processLegacyData(): void { $data = getLegacyData(); assertKeys($data, 'id', 'name'); // PHPStan会自动识别$data包含'id'和'name'键,不会报未定义键的错误 echo $data['id']; echo $data['name']; }
备选方案:自定义PHPStan规则
如果需要处理更复杂的断言逻辑(如混合类型键、嵌套数组),可以编写自定义PHPStan规则,但上述注解方案已经能覆盖绝大多数遗留代码的场景。
内容的提问来源于stack exchange,提问作者Kamil Dzielinski
相关产品推荐
相关产品推荐

