Skip to content

Commit

Permalink
Support arrays with union value-types in implode()
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm authored Jan 7, 2025
1 parent 8b27943 commit 65be2b2
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 9 deletions.
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1362,11 +1362,6 @@ parameters:
count: 1
path: src/Type/Php/FunctionExistsFunctionTypeSpecifyingExtension.php

-
message: "#^Doing instanceof PHPStan\\\\Type\\\\ConstantScalarType is error\\-prone and deprecated\\. Use Type\\:\\:isConstantScalarValue\\(\\) or Type\\:\\:getConstantScalarTypes\\(\\) or Type\\:\\:getConstantScalarValues\\(\\) instead\\.$#"
count: 1
path: src/Type/Php/ImplodeFunctionReturnTypeExtension.php

-
message: """
#^Call to deprecated method getConstantScalars\\(\\) of class PHPStan\\\\Type\\\\TypeUtils\\:
Expand Down
23 changes: 19 additions & 4 deletions src/Type/Php/ImplodeFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@

use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Internal\CombinationsHelper;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Type\Accessory\AccessoryLiteralStringType;
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ConstantScalarType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\StringType;
Expand Down Expand Up @@ -114,14 +115,28 @@ private function inferConstantType(ConstantArrayType $arrayType, ConstantStringT
$valueTypes = $array->getValueTypes();

$arrayValues = [];
$combinationsCount = 1;
foreach ($valueTypes as $valueType) {
if (!$valueType instanceof ConstantScalarType) {
$constScalars = $valueType->getConstantScalarValues();
if (count($constScalars) === 0) {
return null;
}
$arrayValues[] = $valueType->getValue();
$arrayValues[] = $constScalars;
$combinationsCount *= count($constScalars);
}

$strings[] = new ConstantStringType(implode($separatorType->getValue(), $arrayValues));
if ($combinationsCount > InitializerExprTypeResolver::CALCULATE_SCALARS_LIMIT) {
return null;
}

$combinations = CombinationsHelper::combinations($arrayValues);
foreach ($combinations as $combination) {
$strings[] = new ConstantStringType(implode($separatorType->getValue(), $combination));
}
}

if (count($strings) > InitializerExprTypeResolver::CALCULATE_SCALARS_LIMIT) {
return null;
}

return TypeCombinator::union(...$strings);
Expand Down
18 changes: 18 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-11854.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types = 1);

namespace Bug11854;

use function PHPStan\Testing\assertType;

class HelloWorld
{
public function sayHello(): void
{
$arr = [];
$arr[] = rand(0,1) ? 'A' : 'B';
$arr[] = rand(0,1) ? 'C' : '';

assertType("array{'A'|'B', ''|'C'}", $arr);
assertType("'A '|'A C'|'B '|'B C'", implode(' ', $arr));
}
}
30 changes: 30 additions & 0 deletions tests/PHPStan/Analyser/nsrt/implode.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,34 @@ public function constants() {
assertType("'x,345'", join(',', [self::X, '345']));
assertType("'1,345'", join(',', [self::ONE, '345']));
}

/** @param array{0: 1|2, 1: 'a'|'b'} $constArr */
public function constArrays($constArr) {
assertType("'1a'|'1b'|'2a'|'2b'", implode('', $constArr));
}

/** @param array{0: 1|2|3, 1: 'a'|'b'|'c'} $constArr */
public function constArrays2($constArr) {
assertType("'1a'|'1b'|'1c'|'2a'|'2b'|'2c'|'3a'|'3b'|'3c'", implode('', $constArr));
}

/** @param array{0: 1, 1: 'a'|'b', 2: 'x'|'y'} $constArr */
public function constArrays3($constArr) {
assertType("'1ax'|'1ay'|'1bx'|'1by'", implode('', $constArr));
}

/** @param array{0: 1, 1: 'a'|'b', 2?: 'x'|'y'} $constArr */
public function constArrays4($constArr) {
assertType("'1a'|'1ax'|'1ay'|'1b'|'1bx'|'1by'", implode('', $constArr));
}

/** @param array{10: 1|2|3, xy: 'a'|'b'|'c'} $constArr */
public function constArrays5($constArr) {
assertType("'1a'|'1b'|'1c'|'2a'|'2b'|'2c'|'3a'|'3b'|'3c'", implode('', $constArr));
}

/** @param array{0: 1, 1: 'a'|'b', 3?: 'c'|'d', 4?: 'e'|'f', 5?: 'g'|'h', 6?: 'x'|'y'} $constArr */
public function constArrays6($constArr) {
assertType("string", implode('', $constArr));
}
}

0 comments on commit 65be2b2

Please sign in to comment.