Skip to content
7 changes: 5 additions & 2 deletions src/Rules/Arrays/NonexistentOffsetInArrayDimFetchRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ public function processNode(Node $node, Scope $scope): array

$isOffsetAccessible = $isOffsetAccessibleType->isOffsetAccessible();

if (($scope->isInExpressionAssign($node) || $scope->isUndefinedExpressionAllowed($node)) && $isOffsetAccessible->yes()) {
if (
$scope->isInExpressionAssign($node) && $isOffsetAccessible->yes()
|| ($scope->isUndefinedExpressionAllowed($node) || $scope->isSpecified($node)) && !$isOffsetAccessible->no()
) {
return [];
}

Expand All @@ -83,7 +86,7 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

if ($dimType === null || $scope->isSpecified($node)) {
if ($dimType === null) {
return [];
}

Expand Down
8 changes: 8 additions & 0 deletions tests/PHPStan/Levels/data/arrayDimFetches.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ public function doBaz($a, $b): void
* @param iterable<int|string, object> $iterable
*/
public function iterableOffset($iterable): void
{
var_dump($iterable['foo']);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover Debug output?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I just wanted to change this line to some kind of function call that doesn’t cause isUndefinedExpressionAllowed === true (that means something except isset, unset, empty)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add new code instead of changing existing tests :) Thanks.

}

/**
* @param iterable<int|string, object> $iterable
*/
public function iterableOffsetWithUnset($iterable): void
{
unset($iterable['foo']);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@ public function testRule(): void
'Cannot access offset \'a\' on Closure(): void.',
253,
],
[
'Cannot access offset \'a\' on array{a: 1, b: 1}|(Closure(): void).',
258,
],
[
'Offset string does not exist on array<int, string>.',
308,
Expand Down Expand Up @@ -397,4 +393,9 @@ public function testBug6508(): void
$this->analyse([__DIR__ . '/data/bug-6508.php'], []);
}

public function testBug7229(): void
{
$this->analyse([__DIR__ . '/data/bug-7229.php'], []);
}

}
37 changes: 37 additions & 0 deletions tests/PHPStan/Rules/Arrays/data/bug-7229.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);

namespace Bug7229;

class Config
{
// HINTS: a config comes from a config file which always will be an array.
// E.g: return require 'config.php'; // => array<int|string, mixed>
// where mixed is rather the short write for scalar|array<mixed>|null which
// can be nested in N-depth (merging several configs)

/**
* Returns the value from the given array.
*
* @param array<int|string, mixed>|mixed $config The array to search in
* @param array<int|string, string> $parts Parts to look for inside the array
*
* @return array<int|string, mixed>|mixed Found value or null if not available
*/
protected function _getValueFromArray( $config, $parts )
{
// $config type is mixed or array !?

if ( ( $key = array_shift( $parts ) ) !== null && isset( $config[$key] ) ) {

// $config type NOT mixed

if ( count( $parts ) > 0 ) {
return $this->_getValueFromArray( $config[$key], $parts );
}

return $config[$key];
}

return null;
}
}