Skip to content

Commit 4413bd9

Browse files
committed
add support for FILTER_THROW_ON_FAILURE for filter_var
1 parent 95d3e90 commit 4413bd9

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

src/Type/Php/FilterFunctionReturnTypeHelper.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPStan\Type\Constant\ConstantIntegerType;
1717
use PHPStan\Type\Constant\ConstantStringType;
1818
use PHPStan\Type\ConstantScalarType;
19+
use PHPStan\Type\ErrorType;
1920
use PHPStan\Type\FloatType;
2021
use PHPStan\Type\IntegerRangeType;
2122
use PHPStan\Type\IntegerType;
@@ -129,6 +130,10 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T
129130
$inputIsArray = $inputType->isArray();
130131
$hasRequireArrayFlag = $this->hasFlag('FILTER_REQUIRE_ARRAY', $flagsType);
131132
if ($inputIsArray->no() && $hasRequireArrayFlag) {
133+
if ($this->hasFlag('FILTER_THROW_ON_FAILURE', $flagsType)) {
134+
return new ErrorType();
135+
}
136+
132137
return $defaultType;
133138
}
134139

@@ -174,6 +179,10 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T
174179
return new ArrayType($inputArrayKeyType ?? $mixedType, $type);
175180
}
176181

182+
if ($this->hasFlag('FILTER_THROW_ON_FAILURE', $flagsType)) {
183+
$type = TypeCombinator::remove($type, $defaultType);
184+
}
185+
177186
return $type;
178187
}
179188

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PhpParser\Node\Name;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\DependencyInjection\AutowiredService;
9+
use PHPStan\Reflection\FunctionReflection;
10+
use PHPStan\Reflection\ReflectionProvider;
11+
use PHPStan\Type\BitwiseFlagHelper;
12+
use PHPStan\Type\Constant\ConstantIntegerType;
13+
use PHPStan\Type\Constant\ConstantStringType;
14+
use PHPStan\Type\DynamicFunctionThrowTypeExtension;
15+
use PHPStan\Type\ObjectType;
16+
use PHPStan\Type\Type;
17+
18+
#[AutowiredService]
19+
final class FilterVarThrowTypeExtension implements DynamicFunctionThrowTypeExtension
20+
{
21+
22+
public function __construct(
23+
private ReflectionProvider $reflectionProvider,
24+
private BitwiseFlagHelper $bitwiseFlagAnalyser,
25+
)
26+
{
27+
}
28+
29+
public function isFunctionSupported(
30+
FunctionReflection $functionReflection,
31+
): bool
32+
{
33+
return $functionReflection->getName() === 'filter_var'
34+
&& $this->reflectionProvider->hasConstant(new Name\FullyQualified('FILTER_THROW_ON_FAILURE'), null);
35+
}
36+
37+
public function getThrowTypeFromFunctionCall(
38+
FunctionReflection $functionReflection,
39+
FuncCall $funcCall,
40+
Scope $scope,
41+
): ?Type
42+
{
43+
if (!isset($funcCall->getArgs()[3])) {
44+
return null;
45+
}
46+
47+
$flagsExpr = $funcCall->getArgs()[3]->value;
48+
$flagsType = $scope->getType($flagsExpr);
49+
50+
if ($flagsType->isConstantArray()->yes()) {
51+
$flagsType = $flagsType->getOffsetValueType(new ConstantStringType('flags'));
52+
}
53+
54+
$flag = $this->getConstant();
55+
56+
if ($flag !== null && $flagsType instanceof ConstantIntegerType && ($flagsType->getValue() & $flag) === $flag) {
57+
return new ObjectType('Filter\FilterFailedException');
58+
}
59+
60+
return null;
61+
}
62+
63+
private function getConstant(): ?int
64+
{
65+
$constant = $this->reflectionProvider->getConstant(new Name('FILTER_THROW_ON_FAILURE'), null);
66+
$valueType = $constant->getValueType();
67+
if (!$valueType instanceof ConstantIntegerType) {
68+
return null;
69+
}
70+
71+
return $valueType->getValue();
72+
}
73+
74+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php // lint >= 8.5
2+
3+
declare(strict_types=1);
4+
5+
namespace FilterVarPHP85;
6+
7+
use PHPStan\TrinaryLogic;
8+
use function PHPStan\Testing\assertType;
9+
use function PHPStan\Testing\assertVariableCertainty;
10+
11+
class FilterVarPHP85
12+
{
13+
14+
public function doFoo($mixed): void
15+
{
16+
try {
17+
filter_var($mixed, FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
18+
$foo = 1;
19+
} catch (\Filter\FilterFailedException $e) {
20+
assertVariableCertainty(TrinaryLogic::createNo(), $foo);
21+
}
22+
23+
assertType('int', filter_var($mixed, FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE));
24+
assertType('int', filter_var($mixed, FILTER_VALIDATE_INT, ['flags' => FILTER_THROW_ON_FAILURE]));
25+
}
26+
}

0 commit comments

Comments
 (0)