Skip to content

Commit

Permalink
FilterVarDynamicReturnTypeExtension - get rid of runtime constants
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 16, 2020
1 parent 58da413 commit f5a3df6
Showing 1 changed file with 40 additions and 34 deletions.
74 changes: 40 additions & 34 deletions src/Type/Php/FilterVarDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Constant\ConstantArrayType;
Expand All @@ -25,55 +26,60 @@
class FilterVarDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{

private ReflectionProvider $reflectionProvider;

private ConstantStringType $flagsString;

/** @var array<int, Type> */
private array $filterTypeMap;

public function __construct()
public function __construct(ReflectionProvider $reflectionProvider)
{
if (!defined('FILTER_SANITIZE_EMAIL')) {
return;
}
$this->reflectionProvider = $reflectionProvider;

$booleanType = new BooleanType();
$floatType = new FloatType();
$intType = new IntegerType();
$stringType = new StringType();

$this->filterTypeMap = [
FILTER_UNSAFE_RAW => $stringType,
FILTER_SANITIZE_EMAIL => $stringType,
FILTER_SANITIZE_ENCODED => $stringType,
FILTER_SANITIZE_NUMBER_FLOAT => $stringType,
FILTER_SANITIZE_NUMBER_INT => $stringType,
FILTER_SANITIZE_SPECIAL_CHARS => $stringType,
FILTER_SANITIZE_STRING => $stringType,
FILTER_SANITIZE_URL => $stringType,
FILTER_VALIDATE_BOOLEAN => $booleanType,
FILTER_VALIDATE_EMAIL => $stringType,
FILTER_VALIDATE_FLOAT => $floatType,
FILTER_VALIDATE_INT => $intType,
FILTER_VALIDATE_IP => $stringType,
FILTER_VALIDATE_MAC => $stringType,
FILTER_VALIDATE_REGEXP => $stringType,
FILTER_VALIDATE_URL => $stringType,
$this->getConstant('FILTER_UNSAFE_RAW') => $stringType,
$this->getConstant('FILTER_SANITIZE_EMAIL') => $stringType,
$this->getConstant('FILTER_SANITIZE_ENCODED') => $stringType,
$this->getConstant('FILTER_SANITIZE_NUMBER_FLOAT') => $stringType,
$this->getConstant('FILTER_SANITIZE_NUMBER_INT') => $stringType,
$this->getConstant('FILTER_SANITIZE_SPECIAL_CHARS') => $stringType,
$this->getConstant('FILTER_SANITIZE_STRING') => $stringType,
$this->getConstant('FILTER_SANITIZE_URL') => $stringType,
$this->getConstant('FILTER_VALIDATE_BOOLEAN') => $booleanType,
$this->getConstant('FILTER_VALIDATE_EMAIL') => $stringType,
$this->getConstant('FILTER_VALIDATE_FLOAT') => $floatType,
$this->getConstant('FILTER_VALIDATE_INT') => $intType,
$this->getConstant('FILTER_VALIDATE_IP') => $stringType,
$this->getConstant('FILTER_VALIDATE_MAC') => $stringType,
$this->getConstant('FILTER_VALIDATE_REGEXP') => $stringType,
$this->getConstant('FILTER_VALIDATE_URL') => $stringType,
$this->getConstant('FILTER_SANITIZE_MAGIC_QUOTES') => $stringType,
$this->getConstant('FILTER_SANITIZE_ADD_SLASHES') => $stringType,
];

if (defined('FILTER_SANITIZE_MAGIC_QUOTES')) {
$this->filterTypeMap[FILTER_SANITIZE_MAGIC_QUOTES] = $stringType;
}
$this->flagsString = new ConstantStringType('flags');
}

if (defined('FILTER_SANITIZE_ADD_SLASHES')) {
$this->filterTypeMap[FILTER_SANITIZE_ADD_SLASHES] = $stringType;
private function getConstant(string $constantName): int
{
$constant = $this->reflectionProvider->getConstant(new Node\Name($constantName), null);
$valueType = $constant->getValueType();
if (!$valueType instanceof ConstantIntegerType) {
throw new \PHPStan\ShouldNotHappenException(sprintf('Constant %s does not have integer type.', $constantName));
}

$this->flagsString = new ConstantStringType('flags');
return $valueType->getValue();
}

public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return defined('FILTER_SANITIZE_EMAIL') && strtolower($functionReflection->getName()) === 'filter_var';
return strtolower($functionReflection->getName()) === 'filter_var';
}

public function getTypeFromFunctionCall(
Expand All @@ -86,7 +92,7 @@ public function getTypeFromFunctionCall(

$filterArg = $functionCall->args[1] ?? null;
if ($filterArg === null) {
$filterValue = FILTER_DEFAULT;
$filterValue = $this->getConstant('FILTER_DEFAULT');
} else {
$filterType = $scope->getType($filterArg->value);
if (!$filterType instanceof ConstantIntegerType) {
Expand All @@ -109,7 +115,7 @@ public function getTypeFromFunctionCall(
}
}

if ($this->hasFlag(FILTER_FORCE_ARRAY, $flagsArg, $scope)) {
if ($this->hasFlag($this->getConstant('FILTER_FORCE_ARRAY'), $flagsArg, $scope)) {
return new ArrayType(new MixedType(), $type);
}

Expand All @@ -119,13 +125,13 @@ public function getTypeFromFunctionCall(

private function determineExactType(Type $in, int $filterValue): ?Type
{
if (($filterValue === FILTER_VALIDATE_BOOLEAN && $in instanceof BooleanType)
|| ($filterValue === FILTER_VALIDATE_INT && $in instanceof IntegerType)
|| ($filterValue === FILTER_VALIDATE_FLOAT && $in instanceof FloatType)) {
if (($filterValue === $this->getConstant('FILTER_VALIDATE_BOOLEAN') && $in instanceof BooleanType)
|| ($filterValue === $this->getConstant('FILTER_VALIDATE_INT') && $in instanceof IntegerType)
|| ($filterValue === $this->getConstant('FILTER_VALIDATE_FLOAT') && $in instanceof FloatType)) {
return $in;
}

if ($filterValue === FILTER_VALIDATE_FLOAT && $in instanceof IntegerType) {
if ($filterValue === $this->getConstant('FILTER_VALIDATE_FLOAT') && $in instanceof IntegerType) {
return $in->toFloat();
}

Expand All @@ -144,7 +150,7 @@ private function getOtherType(?Node\Arg $flagsArg, Scope $scope): Type
return $defaultType;
}

if ($this->hasFlag(FILTER_NULL_ON_FAILURE, $flagsArg, $scope)) {
if ($this->hasFlag($this->getConstant('FILTER_NULL_ON_FAILURE'), $flagsArg, $scope)) {
return new NullType();
}

Expand Down

0 comments on commit f5a3df6

Please sign in to comment.