Skip to content

Commit 84a2641

Browse files
Improve implode signature
1 parent 1f150cc commit 84a2641

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

src/Parser/ImplodeArgVisitor.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Parser;
4+
5+
use Override;
6+
use PhpParser\Node;
7+
use PhpParser\NodeVisitorAbstract;
8+
use PHPStan\DependencyInjection\AutowiredService;
9+
10+
#[AutowiredService]
11+
final class ImplodeArgVisitor extends NodeVisitorAbstract
12+
{
13+
14+
public const ATTRIBUTE_NAME = 'isImplodeArg';
15+
16+
#[Override]
17+
public function enterNode(Node $node): ?Node
18+
{
19+
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name) {
20+
$functionName = $node->name->toLowerString();
21+
if ($functionName === 'implode' || $functionName === 'join') {
22+
$args = $node->getRawArgs();
23+
if (isset($args[0])) {
24+
$args[0]->setAttribute(self::ATTRIBUTE_NAME, true);
25+
}
26+
}
27+
}
28+
return null;
29+
}
30+
31+
}

src/Reflection/ParametersAcceptorSelector.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPStan\Parser\ClosureBindArgVisitor;
1616
use PHPStan\Parser\ClosureBindToVarVisitor;
1717
use PHPStan\Parser\CurlSetOptArgVisitor;
18+
use PHPStan\Parser\ImplodeArgVisitor;
1819
use PHPStan\Reflection\Callables\CallableParametersAcceptor;
1920
use PHPStan\Reflection\Native\NativeParameterReflection;
2021
use PHPStan\Reflection\Php\DummyParameter;
@@ -203,6 +204,53 @@ public static function selectFromArgs(
203204
];
204205
}
205206

207+
if (isset($args[0]) && (bool) $args[0]->getAttribute(ImplodeArgVisitor::ATTRIBUTE_NAME)) {
208+
$acceptor = $parametersAcceptors[0];
209+
$parameters = $acceptor->getParameters();
210+
if (isset($args[1])) {
211+
$parameters = [
212+
new NativeParameterReflection(
213+
$parameters[0]->getName(),
214+
$parameters[0]->isOptional(),
215+
new StringType(),
216+
$parameters[0]->passedByReference(),
217+
$parameters[0]->isVariadic(),
218+
$parameters[0]->getDefaultValue(),
219+
),
220+
new NativeParameterReflection(
221+
$parameters[1]->getName(),
222+
$parameters[1]->isOptional(),
223+
new ArrayType(new MixedType(), new MixedType()),
224+
$parameters[1]->passedByReference(),
225+
$parameters[1]->isVariadic(),
226+
$parameters[1]->getDefaultValue(),
227+
),
228+
];
229+
} else {
230+
$parameters = [
231+
new NativeParameterReflection(
232+
$parameters[0]->getName(),
233+
$parameters[0]->isOptional(),
234+
new ArrayType(new MixedType(), new MixedType()),
235+
$parameters[0]->passedByReference(),
236+
$parameters[0]->isVariadic(),
237+
$parameters[0]->getDefaultValue(),
238+
),
239+
];
240+
}
241+
242+
$parametersAcceptors = [
243+
new FunctionVariant(
244+
$acceptor->getTemplateTypeMap(),
245+
$acceptor->getResolvedTemplateTypeMap(),
246+
array_values($parameters),
247+
$acceptor->isVariadic(),
248+
$acceptor->getReturnType(),
249+
$acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
250+
),
251+
];
252+
}
253+
206254
if (isset($args[0]) && (bool) $args[0]->getAttribute(ArrayWalkArgVisitor::ATTRIBUTE_NAME)) {
207255
$arrayWalkParameters = [
208256
new DummyParameter('item', $scope->getIterableValueType($scope->getType($args[0]->value)), false, PassedByReference::createReadsArgument(), false, null),

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,46 @@ public function testBug13065(): void
21922192
$this->analyse([__DIR__ . '/data/bug-13065.php'], $errors);
21932193
}
21942194

2195+
public function testBug5760(): void
2196+
{
2197+
$this->checkExplicitMixed = true;
2198+
$this->checkImplicitMixed = true;
2199+
$this->analyse([__DIR__ . '/data/bug-5760.php'], [
2200+
[
2201+
'Parameter #2 $pieces of function join expects array, list<int>|null given.',
2202+
10,
2203+
],
2204+
[
2205+
'Parameter #1 $glue of function join expects array, list<int>|null given.',
2206+
11,
2207+
],
2208+
[
2209+
'Parameter #2 $array of function implode expects array, list<int>|null given.',
2210+
13,
2211+
],
2212+
[
2213+
'Parameter #1 $separator of function implode expects array, list<int>|null given.',
2214+
14,
2215+
],
2216+
[
2217+
'Parameter #2 $pieces of function join expects array, array<string>|string given.',
2218+
22,
2219+
],
2220+
[
2221+
'Parameter #1 $glue of function join expects array, array<string>|string given.',
2222+
23,
2223+
],
2224+
[
2225+
'Parameter #2 $array of function implode expects array, array<string>|string given.',
2226+
25,
2227+
],
2228+
[
2229+
'Parameter #1 $separator of function implode expects array, array<string>|string given.',
2230+
26,
2231+
],
2232+
]);
2233+
}
2234+
21952235
#[RequiresPhp('>= 8.0')]
21962236
public function testBug12317(): void
21972237
{
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug5760;
4+
5+
/**
6+
* @param list<int>|null $arrayOrNull
7+
*/
8+
function doImplode(?array $arrayOrNull): void
9+
{
10+
join(',', $arrayOrNull);
11+
join($arrayOrNull);
12+
13+
implode(',', $arrayOrNull);
14+
implode($arrayOrNull);
15+
}
16+
17+
/**
18+
* @param array<string>|string $union
19+
*/
20+
function more(array|string $union): void
21+
{
22+
join(',', $union);
23+
join($union);
24+
25+
implode(',', $union);
26+
implode($union);
27+
}
28+
29+
function success(): void
30+
{
31+
join(',', ['']);
32+
join(['']);
33+
34+
implode(',', ['']);
35+
implode(['']);
36+
}

0 commit comments

Comments
 (0)