Skip to content
Open
74 changes: 68 additions & 6 deletions src/Type/Php/FilterFunctionReturnTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectShapeType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
Expand Down Expand Up @@ -191,7 +192,11 @@
$type = TypeCombinator::intersect($type, $accessory);
}

if ($exactType === null || $hasOptions->maybe() || (!$inputType->equals($type) && $inputType->isSuperTypeOf($type)->yes())) {
if (
$exactType === null
|| $hasOptions->maybe()
|| ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && $inputType->isSuperTypeOf($type)->yes()))

Check warning on line 198 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $exactType === null || $hasOptions->maybe() - || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && $inputType->isSuperTypeOf($type)->yes())) + || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && !$inputType->isSuperTypeOf($type)->no())) ) { if (!$defaultType->isSuperTypeOf($type)->yes()) { $type = TypeCombinator::union($type, $defaultType);

Check warning on line 198 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ if ( $exactType === null || $hasOptions->maybe() - || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && $inputType->isSuperTypeOf($type)->yes())) + || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && $type->isSuperTypeOf($inputType)->yes())) ) { if (!$defaultType->isSuperTypeOf($type)->yes()) { $type = TypeCombinator::union($type, $defaultType);

Check warning on line 198 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $exactType === null || $hasOptions->maybe() - || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && $inputType->isSuperTypeOf($type)->yes())) + || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && !$inputType->isSuperTypeOf($type)->no())) ) { if (!$defaultType->isSuperTypeOf($type)->yes()) { $type = TypeCombinator::union($type, $defaultType);

Check warning on line 198 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ if ( $exactType === null || $hasOptions->maybe() - || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && $inputType->isSuperTypeOf($type)->yes())) + || ($this->isValidationFilter($filterValue) && (!$inputType->equals($type) && $type->isSuperTypeOf($inputType)->yes())) ) { if (!$defaultType->isSuperTypeOf($type)->yes()) { $type = TypeCombinator::union($type, $defaultType);
) {
if (!$defaultType->isSuperTypeOf($type)->yes()) {
$type = TypeCombinator::union($type, $defaultType);
}
Expand Down Expand Up @@ -389,18 +394,70 @@
}

if ($filterValue === $this->getConstant('FILTER_DEFAULT')) {
if ($this->canStringBeSanitized($filterValue, $flagsType)->no() && $in->isString()->yes()) {
return $in;
$scalarOrNull = new UnionType([
new StringType(),
new FloatType(),
new BooleanType(),
new IntegerType(),
new NullType(),
]);

$scalarOrNullType = null;
$useDefaultType = false;

if ($scalarOrNull->isSuperTypeOf($in)->yes()) {

Check warning on line 408 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $scalarOrNullType = null; $useDefaultType = false; - if ($scalarOrNull->isSuperTypeOf($in)->yes()) { + if (!$scalarOrNull->isSuperTypeOf($in)->no()) { $scalarOrNullType = $in; }

Check warning on line 408 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ $scalarOrNullType = null; $useDefaultType = false; - if ($scalarOrNull->isSuperTypeOf($in)->yes()) { + if ($in->isSuperTypeOf($scalarOrNull)->yes()) { $scalarOrNullType = $in; }

Check warning on line 408 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $scalarOrNullType = null; $useDefaultType = false; - if ($scalarOrNull->isSuperTypeOf($in)->yes()) { + if (!$scalarOrNull->isSuperTypeOf($in)->no()) { $scalarOrNullType = $in; }

Check warning on line 408 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ $scalarOrNullType = null; $useDefaultType = false; - if ($scalarOrNull->isSuperTypeOf($in)->yes()) { + if ($in->isSuperTypeOf($scalarOrNull)->yes()) { $scalarOrNullType = $in; }
$scalarOrNullType = $in;
}

if ($in->isBoolean()->yes() || $in->isFloat()->yes() || $in->isInteger()->yes() || $in->isNull()->yes()) {
return $in->toString();
if ($scalarOrNull->isSuperTypeOf($in)->maybe()) {

Check warning on line 412 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ $scalarOrNullType = $in; } - if ($scalarOrNull->isSuperTypeOf($in)->maybe()) { + if ($in->isSuperTypeOf($scalarOrNull)->maybe()) { // $in is (array or object) or (scalar or null). $scalarOrNullType = TypeCombinator::remove( TypeCombinator::remove($in, new ObjectShapeType([], [])),

Check warning on line 412 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ $scalarOrNullType = $in; } - if ($scalarOrNull->isSuperTypeOf($in)->maybe()) { + if ($in->isSuperTypeOf($scalarOrNull)->maybe()) { // $in is (array or object) or (scalar or null). $scalarOrNullType = TypeCombinator::remove( TypeCombinator::remove($in, new ObjectShapeType([], [])),
// $in is (array or object) or (scalar or null).
$scalarOrNullType = TypeCombinator::remove(
TypeCombinator::remove($in, new ObjectShapeType([], [])),
new ArrayType(new MixedType(), new MixedType()),
);
// Combine future results with defaultType as $in might be an array or an object.
$useDefaultType = true;
}

if (
$scalarOrNullType !== null
&& ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes())

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && ($in->isSuperTypeOf(new MixedType())->yes() || !$scalarOrNull->isSuperTypeOf($scalarOrNullType)->no()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNullType->isSuperTypeOf($scalarOrNull)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && (!$in->isSuperTypeOf(new MixedType())->no() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && ((new MixedType())->isSuperTypeOf($in)->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && ($in->isSuperTypeOf(new MixedType())->yes() || !$scalarOrNull->isSuperTypeOf($scalarOrNullType)->no()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNullType->isSuperTypeOf($scalarOrNull)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && (!$in->isSuperTypeOf(new MixedType())->no() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {

Check warning on line 424 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ if ( $scalarOrNullType !== null - && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) + && ((new MixedType())->isSuperTypeOf($in)->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); if ($canBeSanitized->no()) {
) {
$canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType);
if ($canBeSanitized->no()) {

Check warning on line 427 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); - if ($canBeSanitized->no()) { + if (!$canBeSanitized->yes()) { $stringType = $scalarOrNullType->toString(); } elseif ($scalarOrNullType->isString()->no()) { $stringType = $scalarOrNullType->toString();

Check warning on line 427 in src/Type/Php/FilterFunctionReturnTypeHelper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ && ($in->isSuperTypeOf(new MixedType())->yes() || $scalarOrNull->isSuperTypeOf($scalarOrNullType)->yes()) ) { $canBeSanitized = $this->canStringBeSanitized($filterValue, $flagsType); - if ($canBeSanitized->no()) { + if (!$canBeSanitized->yes()) { $stringType = $scalarOrNullType->toString(); } elseif ($scalarOrNullType->isString()->no()) { $stringType = $scalarOrNullType->toString();
$stringType = $scalarOrNullType->toString();
} elseif ($scalarOrNullType->isString()->no()) {
$stringType = $scalarOrNullType->toString();
} else {
$stringType = TypeCombinator::union(
TypeCombinator::remove($scalarOrNullType, new StringType()),
new StringType(),
);
}

$returnType = $this->handleEmptyStringNullFlag($stringType, $flagsType);
return $useDefaultType ? TypeCombinator::union($defaultType, $returnType) : $returnType;
}
}

return null;
}

private function handleEmptyStringNullFlag(Type $in, ?Type $flagsType): Type
{
$hasFlag = $this->hasFlag('FILTER_FLAG_EMPTY_STRING_NULL', $flagsType);
if ($hasFlag->no()) {
return $in;
}

$hasEmptyString = !$in->isSuperTypeOf(new ConstantStringType(''))->no();
if ($hasFlag->maybe()) {
return $hasEmptyString ? TypeCombinator::addNull($in) : $in;
}

return $hasEmptyString ? TypeCombinator::remove(TypeCombinator::addNull($in), new ConstantStringType('')) : $in;
}

/** @param array<string, ?Type> $typeOptions */
private function applyRangeOptions(Type $type, array $typeOptions, Type $defaultType): Type
{
Expand Down Expand Up @@ -532,7 +589,7 @@
private function canStringBeSanitized(int $filterValue, ?Type $flagsType): TrinaryLogic
{
// If it is a validation filter, the string will not be changed
if (($filterValue & self::VALIDATION_FILTER_BITMASK) !== 0) {
if ($this->isValidationFilter($filterValue)) {
return TrinaryLogic::createNo();
}

Expand All @@ -547,4 +604,9 @@
return TrinaryLogic::createYes();
}

private function isValidationFilter(int $filterValue): bool
{
return ($filterValue & self::VALIDATION_FILTER_BITMASK) !== 0;
}

}
Loading
Loading