Skip to content

Commit 65be2b2

Browse files
authoredJan 7, 2025
Support arrays with union value-types in implode()
1 parent 8b27943 commit 65be2b2

File tree

4 files changed

+67
-9
lines changed

4 files changed

+67
-9
lines changed
 

Diff for: ‎phpstan-baseline.neon

-5
Original file line numberDiff line numberDiff line change
@@ -1362,11 +1362,6 @@ parameters:
13621362
count: 1
13631363
path: src/Type/Php/FunctionExistsFunctionTypeSpecifyingExtension.php
13641364

1365-
-
1366-
message: "#^Doing instanceof PHPStan\\\\Type\\\\ConstantScalarType is error\\-prone and deprecated\\. Use Type\\:\\:isConstantScalarValue\\(\\) or Type\\:\\:getConstantScalarTypes\\(\\) or Type\\:\\:getConstantScalarValues\\(\\) instead\\.$#"
1367-
count: 1
1368-
path: src/Type/Php/ImplodeFunctionReturnTypeExtension.php
1369-
13701365
-
13711366
message: """
13721367
#^Call to deprecated method getConstantScalars\\(\\) of class PHPStan\\\\Type\\\\TypeUtils\\:

Diff for: ‎src/Type/Php/ImplodeFunctionReturnTypeExtension.php

+19-4
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44

55
use PhpParser\Node\Expr\FuncCall;
66
use PHPStan\Analyser\Scope;
7+
use PHPStan\Internal\CombinationsHelper;
78
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Reflection\InitializerExprTypeResolver;
810
use PHPStan\Type\Accessory\AccessoryLiteralStringType;
911
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
1012
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
1113
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
1214
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
1315
use PHPStan\Type\Constant\ConstantArrayType;
1416
use PHPStan\Type\Constant\ConstantStringType;
15-
use PHPStan\Type\ConstantScalarType;
1617
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1718
use PHPStan\Type\IntersectionType;
1819
use PHPStan\Type\StringType;
@@ -114,14 +115,28 @@ private function inferConstantType(ConstantArrayType $arrayType, ConstantStringT
114115
$valueTypes = $array->getValueTypes();
115116

116117
$arrayValues = [];
118+
$combinationsCount = 1;
117119
foreach ($valueTypes as $valueType) {
118-
if (!$valueType instanceof ConstantScalarType) {
120+
$constScalars = $valueType->getConstantScalarValues();
121+
if (count($constScalars) === 0) {
119122
return null;
120123
}
121-
$arrayValues[] = $valueType->getValue();
124+
$arrayValues[] = $constScalars;
125+
$combinationsCount *= count($constScalars);
122126
}
123127

124-
$strings[] = new ConstantStringType(implode($separatorType->getValue(), $arrayValues));
128+
if ($combinationsCount > InitializerExprTypeResolver::CALCULATE_SCALARS_LIMIT) {
129+
return null;
130+
}
131+
132+
$combinations = CombinationsHelper::combinations($arrayValues);
133+
foreach ($combinations as $combination) {
134+
$strings[] = new ConstantStringType(implode($separatorType->getValue(), $combination));
135+
}
136+
}
137+
138+
if (count($strings) > InitializerExprTypeResolver::CALCULATE_SCALARS_LIMIT) {
139+
return null;
125140
}
126141

127142
return TypeCombinator::union(...$strings);

Diff for: ‎tests/PHPStan/Analyser/nsrt/bug-11854.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11854;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
public function sayHello(): void
10+
{
11+
$arr = [];
12+
$arr[] = rand(0,1) ? 'A' : 'B';
13+
$arr[] = rand(0,1) ? 'C' : '';
14+
15+
assertType("array{'A'|'B', ''|'C'}", $arr);
16+
assertType("'A '|'A C'|'B '|'B C'", implode(' ', $arr));
17+
}
18+
}

Diff for: ‎tests/PHPStan/Analyser/nsrt/implode.php

+30
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,34 @@ public function constants() {
2121
assertType("'x,345'", join(',', [self::X, '345']));
2222
assertType("'1,345'", join(',', [self::ONE, '345']));
2323
}
24+
25+
/** @param array{0: 1|2, 1: 'a'|'b'} $constArr */
26+
public function constArrays($constArr) {
27+
assertType("'1a'|'1b'|'2a'|'2b'", implode('', $constArr));
28+
}
29+
30+
/** @param array{0: 1|2|3, 1: 'a'|'b'|'c'} $constArr */
31+
public function constArrays2($constArr) {
32+
assertType("'1a'|'1b'|'1c'|'2a'|'2b'|'2c'|'3a'|'3b'|'3c'", implode('', $constArr));
33+
}
34+
35+
/** @param array{0: 1, 1: 'a'|'b', 2: 'x'|'y'} $constArr */
36+
public function constArrays3($constArr) {
37+
assertType("'1ax'|'1ay'|'1bx'|'1by'", implode('', $constArr));
38+
}
39+
40+
/** @param array{0: 1, 1: 'a'|'b', 2?: 'x'|'y'} $constArr */
41+
public function constArrays4($constArr) {
42+
assertType("'1a'|'1ax'|'1ay'|'1b'|'1bx'|'1by'", implode('', $constArr));
43+
}
44+
45+
/** @param array{10: 1|2|3, xy: 'a'|'b'|'c'} $constArr */
46+
public function constArrays5($constArr) {
47+
assertType("'1a'|'1b'|'1c'|'2a'|'2b'|'2c'|'3a'|'3b'|'3c'", implode('', $constArr));
48+
}
49+
50+
/** @param array{0: 1, 1: 'a'|'b', 3?: 'c'|'d', 4?: 'e'|'f', 5?: 'g'|'h', 6?: 'x'|'y'} $constArr */
51+
public function constArrays6($constArr) {
52+
assertType("string", implode('', $constArr));
53+
}
2454
}

0 commit comments

Comments
 (0)
Please sign in to comment.