Skip to content
11 changes: 11 additions & 0 deletions src/Analyser/ExprHandler/ArrayHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use PHPStan\Node\LiteralArrayItem;
use PHPStan\Node\LiteralArrayNode;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use function array_merge;

Expand Down Expand Up @@ -60,12 +61,22 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$scope = $keyResult->getScope();
}

if ($arrayItem->byRef) {
$scope = $nodeScopeResolver->lookForSetAllowedUndefinedExpressions($scope, $arrayItem->value);
}

$valueResult = $nodeScopeResolver->processExprNode($stmt, $arrayItem->value, $scope, $storage, $nodeCallback, $context->enterDeep());
$hasYield = $hasYield || $valueResult->hasYield();
$throwPoints = array_merge($throwPoints, $valueResult->getThrowPoints());
$impurePoints = array_merge($impurePoints, $valueResult->getImpurePoints());
$isAlwaysTerminating = $isAlwaysTerminating || $valueResult->isAlwaysTerminating();
$scope = $valueResult->getScope();
if (!$arrayItem->byRef) {
continue;
}

$scope = $nodeScopeResolver->lookForUnsetAllowedUndefinedExpressions($scope, $arrayItem->value);
$scope = $scope->assignExpression($arrayItem->value, new MixedType(), new MixedType());
}
$nodeScopeResolver->callNodeCallback($nodeCallback, new LiteralArrayNode($expr, $itemNodes), $scope, $storage);

Expand Down
87 changes: 87 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-6799.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php declare(strict_types = 1);

namespace Bug6799;

use function PHPStan\Testing\assertType;

class HelloWorld
{
/**
* @param string[] $where
* @param string $sqlTableName
* @param mixed[] $filter
* @param string $value
*/
protected function listingAddWhereFilterAtableDefault(array &$where, string $sqlTableName, array $filter, string $value): void
{
if ($value != "" && !empty($filter) && !empty($filter['sql']) && is_string($filter['sql'])) {
$where[] = "`" . $sqlTableName . "`.`" . (string)$filter['sql'] . "` = '" . $value . "'";
}
}

/**
* @param string[] $filterValues
* @param string[] $where
* @param string[] $tables
* @param mixed[] $filters
*/
protected function listingAddWhereFilterAtable(array $filterValues, array &$where, array &$tables, array $filters): void
{
if (!empty($filterValues) && !empty($filters)) {
$whereFilter = array();
foreach ($filterValues as $type => $value) {
call_user_func_array(array($this, 'listingAddWhereFilterAtableDefault'), array(&$whereFilter, 'xxxx', $filters[$type], $value));
}
assertType('mixed', $whereFilter);
if (count($whereFilter) > 0) {
$where[] = "(" . implode(" AND ", $whereFilter) . ")";
}
}
}
}

/**
* @param mixed $foo
*/
function foo($foo): void {}

function testByRefInArray(): void
{
$a = [];
assertType('array{}', $a);

$b = [&$a];
assertType('mixed', $a); // Could stay array{}

foo($b);
assertType('mixed', $a);
}

function testByRefInArrayWithKey(): void
{
$a = 'hello';
assertType("'hello'", $a);

$b = ['key' => &$a];
assertType('mixed', $a); // Could stay 'hello'

$b['key'] = 42;
assertType('mixed', $a); // Could be 42
}

function testMultipleByRefInArray(): void
{
$a = 1;
$c = 'test';

$b = [&$a, 'normal', &$c];
assertType('mixed', $a); // Could stay 1
assertType('mixed', $c); // Could stay 'test'

$b[0] = 2;
$b[1] = 'foo';
$b[2] = 'bar';

assertType('mixed', $a); // Could be 2
assertType('mixed', $c); // Could be 'bar'
}
47 changes: 47 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-6799b.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types = 1);

namespace Bug6799b;

use function PHPStan\Testing\assertType;

class HelloWorld
{


/**
* listingAddWhereFilterAtableRoleCategory
*
* @param string[] $where
* @param string $sqlTableName
* @param mixed[] $filter
* @param string $value
*
* @return void
*/
protected function listingAddWhereFilterAtableDefault(array &$where, string $sqlTableName, array $filter, string $value): void
{
if ($value != "" && !empty($filter) && !empty($filter['sql']) && is_string($filter['sql'])) {
$where[] = "`" . $sqlTableName . "`.`" . (string)$filter['sql'] . "` = '" . $value . "'";
}
}

/**
* listingAddWhereFilterAtableFilter
*
* @param string[] $filterValues
* @param string[] $where
* @param string[] $tables
* @param mixed[] $filters
* @return void
*/
protected function listingAddWhereFilterAtable(array $filterValues, array &$where, array &$tables, array $filters): void
{
if (!empty($filterValues) && !empty($filters)) {
$whereFilter = array();
foreach ($filterValues as $type => $value) {
$this->listingAddWhereFilterAtableDefault($whereFilter, 'xxxxx', $filters[$type], $value);
}
assertType('array<string>', $whereFilter);
}
}
}
17 changes: 17 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-6799c.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);

namespace Bug6799C;

use function PHPStan\Testing\assertType;

// https://3v4l.org/g5UjS

$a = [&$x];
assertType('mixed', $x);

function doFoo(array &$arr) {
$arr[0] = 'string';
}

doFoo($a);
assertType('mixed', $x);
Original file line number Diff line number Diff line change
Expand Up @@ -298,4 +298,9 @@ public function testBug12163(): void
]);
}

public function testBug6799(): void
{
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-6799.php'], []);
}

}
10 changes: 10 additions & 0 deletions tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1430,4 +1430,14 @@ public function testBug14117(): void
]);
}

public function testBug6799c(): void
{
$this->cliArgumentsVariablesRegistered = true;
$this->polluteScopeWithLoopInitialAssignments = false;
$this->checkMaybeUndefinedVariables = true;
$this->polluteScopeWithAlwaysIterableForeach = true;

$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-6799c.php'], []);
}

}
Loading