Skip to content

Commit

Permalink
Fix filter_var() with float input and FILTER_VALIDATE_INT
Browse files Browse the repository at this point in the history
  • Loading branch information
herndlm authored and ondrejmirtes committed Jan 30, 2023
1 parent 224751c commit 5bd68ef
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 5 deletions.
12 changes: 8 additions & 4 deletions src/Type/Php/FilterFunctionReturnTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ConstantScalarType;
Expand All @@ -28,6 +29,7 @@
use function octdec;
use function preg_match;
use function sprintf;
use const PHP_FLOAT_EPSILON;

final class FilterFunctionReturnTypeHelper
{
Expand Down Expand Up @@ -196,14 +198,16 @@ private function determineExactType(Type $in, int $filterValue, Type $defaultTyp
}

if ($filterValue === $this->getConstant('FILTER_VALIDATE_INT')) {
if ($in->isFloat()->yes()) {
return $in->toInteger();
}

if ($in->isInteger()->yes()) {
return $in;
}

if ($in instanceof ConstantFloatType) {
return $in->getValue() - (int) $in->getValue() <= PHP_FLOAT_EPSILON
? $in->toInteger()
: $defaultType;
}

if ($in instanceof ConstantStringType) {
$value = $in->getValue();
$allowOctal = $this->hasFlag($this->getConstant('FILTER_FLAG_ALLOW_OCTAL'), $flagsType);
Expand Down
10 changes: 9 additions & 1 deletion tests/PHPStan/Analyser/data/filter-var.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,51 +47,59 @@ public function scalars(bool $bool, float $float, int $int, string $string, int
assertType('false', filter_var(false, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
assertType('bool|null', filter_var($float, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
assertType('bool|null', filter_var(17.0, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); // could be null
assertType('bool|null', filter_var(17.1, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); // could be null
assertType('bool|null', filter_var($int, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
assertType('bool|null', filter_var($intRange, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
assertType('bool|null', filter_var(17, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); // could be null
assertType('bool|null', filter_var($string, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
assertType('bool|null', filter_var($nonEmptyString, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
assertType('bool|null', filter_var('17', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); // could be null
assertType('bool|null', filter_var('17.1', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); // could be null
assertType('bool|null', filter_var(null, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); // could be null

assertType('float|false', filter_var($bool, FILTER_VALIDATE_FLOAT));
assertType('float|false', filter_var(true, FILTER_VALIDATE_FLOAT)); // could be 1
assertType('float|false', filter_var(false, FILTER_VALIDATE_FLOAT)); // could be false
assertType('float', filter_var($float, FILTER_VALIDATE_FLOAT));
assertType('17.0', filter_var(17.0, FILTER_VALIDATE_FLOAT));
assertType('17.1', filter_var(17.1, FILTER_VALIDATE_FLOAT));
assertType('float', filter_var($int, FILTER_VALIDATE_FLOAT));
assertType('float', filter_var($intRange, FILTER_VALIDATE_FLOAT));
assertType('17.0', filter_var(17, FILTER_VALIDATE_FLOAT));
assertType('float|false', filter_var($string, FILTER_VALIDATE_FLOAT));
assertType('float|false', filter_var($nonEmptyString, FILTER_VALIDATE_FLOAT));
assertType('float|false', filter_var('17', FILTER_VALIDATE_FLOAT)); // could be 17.0
assertType('float|false', filter_var('17.1', FILTER_VALIDATE_FLOAT)); // could be 17.1
assertType('float|false', filter_var(null, FILTER_VALIDATE_FLOAT)); // could be false

assertType('int|false', filter_var($bool, FILTER_VALIDATE_INT));
assertType('int|false', filter_var(true, FILTER_VALIDATE_INT)); // could be 1
assertType('int|false', filter_var(false, FILTER_VALIDATE_INT)); // could be false
assertType('int', filter_var($float, FILTER_VALIDATE_INT));
assertType('int|false', filter_var($float, FILTER_VALIDATE_INT));
assertType('17', filter_var(17.0, FILTER_VALIDATE_INT));
assertType('false', filter_var(17.1, FILTER_VALIDATE_INT));
assertType('int', filter_var($int, FILTER_VALIDATE_INT));
assertType('int<0, 9>', filter_var($intRange, FILTER_VALIDATE_INT));
assertType('17', filter_var(17, FILTER_VALIDATE_INT));
assertType('int|false', filter_var($string, FILTER_VALIDATE_INT));
assertType('int|false', filter_var($nonEmptyString, FILTER_VALIDATE_INT));
assertType('17', filter_var('17', FILTER_VALIDATE_INT));
assertType('false', filter_var('17.1', FILTER_VALIDATE_INT));
assertType('int|false', filter_var(null, FILTER_VALIDATE_INT)); // could be false

assertType("''|'1'", filter_var($bool));
assertType("'1'", filter_var(true));
assertType("''", filter_var(false));
assertType('numeric-string', filter_var($float));
assertType("'17'", filter_var(17.0));
assertType("'17.1'", filter_var(17.1));
assertType('numeric-string', filter_var($int));
assertType('numeric-string', filter_var($intRange));
assertType("'17'", filter_var(17));
assertType('string', filter_var($string));
assertType('non-empty-string', filter_var($nonEmptyString));
assertType("'17'", filter_var('17'));
assertType("'17.1'", filter_var('17.1'));
assertType("''", filter_var(null));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,10 @@ public function testBug5317(): void
]);
}

public function testBug8797(): void
{
$this->treatPhpDocTypesAsCertain = true;
$this->analyse([__DIR__ . '/data/bug-8797.php'], []);
}

}
13 changes: 13 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/bug-8797.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace Bug8797;

final class TestClass
{
public function testMethod(float $float): void {
$boolean = filter_var($float, FILTER_VALIDATE_INT) !== false;
if (!$boolean) {
return;
}
}
}

0 comments on commit 5bd68ef

Please sign in to comment.