diff --git a/conf/config.neon b/conf/config.neon index ec0c6b03ac..d2cb8433b1 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -79,6 +79,7 @@ parameters: reportStaticMethodSignatures: false reportWrongPhpDocTypeInVarTag: false reportAnyTypeWideningInVarTag: false + allowFloatBoolNullAsArrayKey: true reportPossiblyNonexistentGeneralArrayOffset: false reportPossiblyNonexistentConstantArrayOffset: false checkMissingOverrideMethodAttribute: false diff --git a/conf/parametersSchema.neon b/conf/parametersSchema.neon index 33fdd37b68..389815ec3d 100644 --- a/conf/parametersSchema.neon +++ b/conf/parametersSchema.neon @@ -88,6 +88,7 @@ parametersSchema: reportStaticMethodSignatures: bool() reportWrongPhpDocTypeInVarTag: bool() reportAnyTypeWideningInVarTag: bool() + allowFloatBoolNullAsArrayKey: bool() reportPossiblyNonexistentGeneralArrayOffset: bool() reportPossiblyNonexistentConstantArrayOffset: bool() checkMissingOverrideMethodAttribute: bool() diff --git a/src/Rules/Arrays/AllowedArrayKeysTypes.php b/src/Rules/Arrays/AllowedArrayKeysTypes.php index b670dbb891..3cae964952 100644 --- a/src/Rules/Arrays/AllowedArrayKeysTypes.php +++ b/src/Rules/Arrays/AllowedArrayKeysTypes.php @@ -22,19 +22,22 @@ final class AllowedArrayKeysTypes { - public static function getType(?PhpVersion $phpVersion = null): Type + public static function getType(?PhpVersion $phpVersion = null, bool $allowFloatBoolNull = true): Type { $types = [ new IntegerType(), new StringType(), - new BooleanType(), ]; - if ($phpVersion === null || !$phpVersion->deprecatesImplicitlyFloatConversionToInt()) { - $types[] = new FloatType(); - } - if ($phpVersion === null || !$phpVersion->deprecatesNullArrayOffset()) { - $types[] = new NullType(); + if ($allowFloatBoolNull) { + $types[] = new BooleanType(); + + if ($phpVersion === null || !$phpVersion->deprecatesImplicitlyFloatConversionToInt()) { + $types[] = new FloatType(); + } + if ($phpVersion === null || !$phpVersion->deprecatesNullArrayOffset()) { + $types[] = new NullType(); + } } return new UnionType($types); diff --git a/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php b/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php index 0723234b36..120a98ce58 100644 --- a/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php +++ b/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php @@ -27,6 +27,8 @@ public function __construct( private PhpVersion $phpVersion, #[AutowiredParameter] private bool $reportMaybes, + #[AutowiredParameter] + private bool $allowFloatBoolNullAsArrayKey, ) { } @@ -58,7 +60,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $allowedArrayKeys = AllowedArrayKeysTypes::getType($this->phpVersion); + $allowedArrayKeys = AllowedArrayKeysTypes::getType($this->phpVersion, $this->allowFloatBoolNullAsArrayKey); $dimensionType = $this->ruleLevelHelper->findTypeToCheck( $scope, $node->dim, diff --git a/src/Rules/Arrays/InvalidKeyInArrayItemRule.php b/src/Rules/Arrays/InvalidKeyInArrayItemRule.php index 298c65b565..79216ed3a3 100644 --- a/src/Rules/Arrays/InvalidKeyInArrayItemRule.php +++ b/src/Rules/Arrays/InvalidKeyInArrayItemRule.php @@ -4,6 +4,7 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; +use PHPStan\DependencyInjection\AutowiredParameter; use PHPStan\DependencyInjection\RegisteredRule; use PHPStan\Php\PhpVersion; use PHPStan\Rules\Rule; @@ -24,6 +25,8 @@ final class InvalidKeyInArrayItemRule implements Rule public function __construct( private RuleLevelHelper $ruleLevelHelper, private PhpVersion $phpVersion, + #[AutowiredParameter] + private bool $allowFloatBoolNullAsArrayKey, ) { } @@ -39,7 +42,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $allowedArrayKeys = AllowedArrayKeysTypes::getType($this->phpVersion); + $allowedArrayKeys = AllowedArrayKeysTypes::getType($this->phpVersion, $this->allowFloatBoolNullAsArrayKey); $dimensionType = $this->ruleLevelHelper->findTypeToCheck( $scope, $node->key, diff --git a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php index 38baa0026d..fb9ba3dceb 100644 --- a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php @@ -15,6 +15,8 @@ class InvalidKeyInArrayDimFetchRuleTest extends RuleTestCase { + private bool $allowFloatBoolNullAsArrayKey = true; + protected function getRule(): Rule { $ruleLevelHelper = new RuleLevelHelper(self::createReflectionProvider(), true, false, true, true, true, false, true); @@ -22,6 +24,7 @@ protected function getRule(): Rule $ruleLevelHelper, self::getContainer()->getByType(PhpVersion::class), true, + $this->allowFloatBoolNullAsArrayKey, ); } @@ -163,4 +166,20 @@ public function testBug12981(): void ]); } + public function testUnsetFalseKey(): void + { + $this->allowFloatBoolNullAsArrayKey = false; + + $this->analyse([__DIR__ . '/data/unset-false-key.php'], [ + [ + 'Invalid array key type false.', + 6, + ], + [ + 'Invalid array key type false.', + 13, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php index 1c01cf62ca..cd2c5d45b5 100644 --- a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php @@ -15,6 +15,8 @@ class InvalidKeyInArrayItemRuleTest extends RuleTestCase { + private bool $allowFloatBoolNullAsArrayKey = true; + private bool $checkExplicitMixed = false; private bool $checkImplicitMixed = false; @@ -26,6 +28,7 @@ protected function getRule(): Rule return new InvalidKeyInArrayItemRule( $ruleLevelHelper, self::getContainer()->getByType(PhpVersion::class), + $this->allowFloatBoolNullAsArrayKey, ); } @@ -102,6 +105,38 @@ public function testInvalidMixedKey(): void $this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], $errors); } + public function testInvalidKeyReportingCastedArrayKey(): void + { + $this->allowFloatBoolNullAsArrayKey = false; + + $this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], [ + [ + 'Invalid array key type DateTimeImmutable.', + 12, + ], + [ + 'Invalid array key type array.', + 13, + ], + [ + 'Possibly invalid array key type stdClass|string.', + 14, + ], + [ + 'Invalid array key type float.', + 26, + ], + [ + 'Invalid array key type null.', + 27, + ], + [ + 'Invalid array key type false.', + 31, + ], + ]); + } + public function testInvalidKeyInList(): void { $this->analyse([__DIR__ . '/data/invalid-key-list.php'], [ diff --git a/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php b/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php index 1c734968b2..e7ebc67ad6 100644 --- a/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php +++ b/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php @@ -26,3 +26,7 @@ 1.0 => 'aaa', null => 'aaa', ]; + +$d = [ + false => 'aaa', +]; diff --git a/tests/PHPStan/Rules/Arrays/data/unset-false-key.php b/tests/PHPStan/Rules/Arrays/data/unset-false-key.php new file mode 100644 index 0000000000..d47f059600 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/unset-false-key.php @@ -0,0 +1,16 @@ + $data */ +unset($data[false]); + +function test_remove_element(): void { + $modified = [1, 4, 6, 8]; + + // this would happen in the SUT + unset($modified[array_search(4, $modified, true)]); + unset($modified[array_search(5, $modified, true)]); // bug is here - will unset key `0` by accident + + assert([1, 6, 8] === $modified); // actually is [6, 8] +}

AltStyle によって変換されたページ (->オリジナル) /