Skip to content

Commit a22f582

Browse files
uekannuekann
authored andcommitted
Add simple implementation and test cases
1 parent cd5a6f6 commit a22f582

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Expr\FuncCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Analyser\SpecifiedTypes;
9+
use PHPStan\Analyser\TypeSpecifier;
10+
use PHPStan\Analyser\TypeSpecifierAwareExtension;
11+
use PHPStan\Analyser\TypeSpecifierContext;
12+
use PHPStan\DependencyInjection\AutowiredService;
13+
use PHPStan\Reflection\FunctionReflection;
14+
use PHPStan\Type\Constant\ConstantBooleanType;
15+
use PHPStan\ShouldNotHappenException;
16+
use PHPStan\Type\ArrayType;
17+
use PHPStan\Type\FunctionTypeSpecifyingExtension;
18+
use PHPStan\Type\MixedType;
19+
use function strtolower;
20+
21+
#[AutowiredService]
22+
final class ArrayAllFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
23+
{
24+
25+
private TypeSpecifier $typeSpecifier;
26+
27+
public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context): bool
28+
{
29+
return strtolower($functionReflection->getName()) === 'array_all'
30+
&& !$context->null();
31+
}
32+
33+
public function specifyTypes(FunctionReflection $functionReflection, FuncCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
34+
{
35+
$args = $node->getArgs();
36+
if (!$context->true() || count($args) < 2) {
37+
return new SpecifiedTypes();
38+
}
39+
40+
$array = $args[0]->value;
41+
$callable = $args[1]->value;
42+
if ($callable instanceof Expr\ArrowFunction && $callable->expr instanceof Expr\FuncCall) {
43+
$specifiedTypesInCallable = $this->typeSpecifier->specifyTypesInCondition($scope, $callable->expr, $context)->getSureTypes();
44+
$callableParm = $callable->params[0];
45+
if (!$callableParm instanceof Expr\Variable || !key_exists("$" . $callableParm->name, $specifiedTypesInCallable)) {
46+
return new SpecifiedTypes();
47+
}
48+
$ItemType = $specifiedTypesInCallable["$" . $callableParm->name][1];
49+
return $this->typeSpecifier->create(
50+
$array,
51+
new ArrayType(new MixedType(), $ItemType),
52+
$context,
53+
$scope
54+
);
55+
}
56+
57+
return new SpecifiedTypes();
58+
}
59+
60+
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
61+
{
62+
$this->typeSpecifier = $typeSpecifier;
63+
}
64+
65+
}

tests/PHPStan/Analyser/TypeSpecifierTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use PHPStan\Type\ArrayType;
2727
use PHPStan\Type\ClassStringType;
2828
use PHPStan\Type\Constant\ConstantBooleanType;
29+
use PHPStan\Type\Constant\ConstantIntegerType;
2930
use PHPStan\Type\FloatType;
3031
use PHPStan\Type\Generic\GenericClassStringType;
3132
use PHPStan\Type\IntegerType;
@@ -1320,6 +1321,46 @@ public static function dataCondition(): iterable
13201321
],
13211322
[],
13221323
],
1324+
[
1325+
new FuncCall(
1326+
new Name("array_all"),
1327+
[
1328+
new Arg(new Variable("array")),
1329+
new Arg(
1330+
new Expr\ArrowFunction(
1331+
[
1332+
'expr' => new FuncCall(new Name("is_int"), [new Arg(new Variable("value"))]),
1333+
'params' => [new Variable("value")],
1334+
],
1335+
[]
1336+
)
1337+
)
1338+
]
1339+
),
1340+
[
1341+
'$array' => 'array<int>'
1342+
],
1343+
[]
1344+
],
1345+
[
1346+
new FuncCall(
1347+
new Name("array_all"),
1348+
[
1349+
new Arg(new Variable("array")),
1350+
new Arg(
1351+
new Expr\ArrowFunction(
1352+
[
1353+
'expr' => new FuncCall(new Name("is_int"), [new Arg(new Expr\ConstFetch(new Name("1")))]),
1354+
'params' => [new Variable("value")],
1355+
],
1356+
[]
1357+
)
1358+
)
1359+
]
1360+
),
1361+
[],
1362+
[]
1363+
],
13231364
];
13241365
}
13251366

0 commit comments

Comments
 (0)