diff --git a/README.md b/README.md index 5062e0a..69cd304 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ parameters: forbidUnsafeArrayKey: enabled: true reportMixed: true + reportInsideIsset: true forbidVariableTypeOverwriting: enabled: true forbidUnsetClassField: @@ -619,7 +620,8 @@ parameters: - PHP casts `null`, `float` and `bool` to some nearest int/string - You should rather do that intentionally and explicitly - Those types are the main difference to default PHPStan behaviour which allows using them as array keys -- You can exclude reporting `mixed` keys via configuration +- You can exclude reporting `mixed` keys via `reportMixed` configuration +- You can exclude reporting `isset($array[$invalid])` and `$array[$invalid] ?? null` via `reportInsideIsset` configuration ```php $taxRates = [ // denied, float key gets casted to int (causing $taxRates to contain one item) @@ -633,6 +635,7 @@ parameters: shipmonkRules: forbidUnsafeArrayKey: reportMixed: false # defaults to true + reportInsideIsset: false # defaults to true ``` diff --git a/rules.neon b/rules.neon index 200759e..b7e7145 100644 --- a/rules.neon +++ b/rules.neon @@ -68,6 +68,7 @@ parameters: forbidUnsafeArrayKey: enabled: true reportMixed: true + reportInsideIsset: true forbidVariableTypeOverwriting: enabled: true forbidUnsetClassField: @@ -183,6 +184,7 @@ parametersSchema: forbidUnsafeArrayKey: structure([ enabled: bool() reportMixed: bool() + reportInsideIsset: bool() ]) forbidVariableTypeOverwriting: structure([ enabled: bool() @@ -382,6 +384,7 @@ services: class: ShipMonk\PHPStan\Rule\ForbidUnsafeArrayKeyRule arguments: reportMixed: %shipmonkRules.forbidUnsafeArrayKey.reportMixed% + reportInsideIsset: %shipmonkRules.forbidUnsafeArrayKey.reportInsideIsset% - class: ShipMonk\PHPStan\Rule\ForbidVariableTypeOverwritingRule - diff --git a/src/Rule/ForbidUnsafeArrayKeyRule.php b/src/Rule/ForbidUnsafeArrayKeyRule.php index 03865f0..5edeb76 100644 --- a/src/Rule/ForbidUnsafeArrayKeyRule.php +++ b/src/Rule/ForbidUnsafeArrayKeyRule.php @@ -24,9 +24,15 @@ class ForbidUnsafeArrayKeyRule implements Rule private bool $reportMixed; - public function __construct(bool $reportMixed) + private bool $reportInsideIsset; + + public function __construct( + bool $reportMixed, + bool $reportInsideIsset + ) { $this->reportMixed = $reportMixed; + $this->reportInsideIsset = $reportInsideIsset; } public function getNodeType(): string @@ -59,6 +65,10 @@ public function processNode( && $node->dim !== null && !$scope->getType($node->var)->isArray()->no() ) { + if (!$this->reportInsideIsset && $scope->isUndefinedExpressionAllowed($node)) { + return []; + } + $dimType = $scope->getType($node->dim); if (!$this->isArrayKey($dimType)) { diff --git a/tests/Rule/ForbidUnsafeArrayKeyRuleTest.php b/tests/Rule/ForbidUnsafeArrayKeyRuleTest.php index a102c52..2f303c0 100644 --- a/tests/Rule/ForbidUnsafeArrayKeyRuleTest.php +++ b/tests/Rule/ForbidUnsafeArrayKeyRuleTest.php @@ -15,27 +15,36 @@ class ForbidUnsafeArrayKeyRuleTest extends RuleTestCase private ?bool $checkMixed = null; + private ?bool $checkInsideIsset = null; + protected function getRule(): Rule { if ($this->checkMixed === null) { throw new LogicException('Property checkMixed must be set'); } + if ($this->checkInsideIsset === null) { + throw new LogicException('Property checkInsideIsset must be set'); + } + return new ForbidUnsafeArrayKeyRule( $this->checkMixed, + $this->checkInsideIsset, ); } - public function testMixedDisabled(): void + public function testStrict(): void { - $this->checkMixed = false; - $this->analyseFile(__DIR__ . '/data/ForbidUnsafeArrayKeyRule/mixed-disabled.php'); + $this->checkMixed = true; + $this->checkInsideIsset = true; + $this->analyseFile(__DIR__ . '/data/ForbidUnsafeArrayKeyRule/default.php'); } - public function testMixedEnabled(): void + public function testLessStrict(): void { - $this->checkMixed = true; - $this->analyseFile(__DIR__ . '/data/ForbidUnsafeArrayKeyRule/mixed-enabled.php'); + $this->checkMixed = false; + $this->checkInsideIsset = false; + $this->analyseFile(__DIR__ . '/data/ForbidUnsafeArrayKeyRule/less-strict.php'); } } diff --git a/tests/Rule/data/ForbidUnsafeArrayKeyRule/mixed-enabled.php b/tests/Rule/data/ForbidUnsafeArrayKeyRule/default.php similarity index 82% rename from tests/Rule/data/ForbidUnsafeArrayKeyRule/mixed-enabled.php rename to tests/Rule/data/ForbidUnsafeArrayKeyRule/default.php index 89cdd5d..cd878fb 100644 --- a/tests/Rule/data/ForbidUnsafeArrayKeyRule/mixed-enabled.php +++ b/tests/Rule/data/ForbidUnsafeArrayKeyRule/default.php @@ -1,6 +1,6 @@ $int, $string => $string, diff --git a/tests/Rule/data/ForbidUnsafeArrayKeyRule/mixed-disabled.php b/tests/Rule/data/ForbidUnsafeArrayKeyRule/less-strict.php similarity index 89% rename from tests/Rule/data/ForbidUnsafeArrayKeyRule/mixed-disabled.php rename to tests/Rule/data/ForbidUnsafeArrayKeyRule/less-strict.php index dd7485e..c45b202 100644 --- a/tests/Rule/data/ForbidUnsafeArrayKeyRule/mixed-disabled.php +++ b/tests/Rule/data/ForbidUnsafeArrayKeyRule/less-strict.php @@ -1,6 +1,6 @@ $int, $string => $string,