Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/Php/PhpVersions.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,84 @@ public function supportsDeprecatedTraits(): TrinaryLogic
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function arrayFunctionsReturnNullWithNonArray(): TrinaryLogic
{
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
}

public function hasDateTimeExceptions(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80300, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function hasFilterThrowOnFailureConstant(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function hasPDOSubclasses(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function hasStricterRoundFunctions(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function highlightStringDoesNotReturnFalse(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function isEmptyStringValidAliasForNoneInMbSubstituteCharacter(): TrinaryLogic
{
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
}

public function isNullValidArgInMbSubstituteCharacter(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function isNumericStringValidArgInMbSubstituteCharacter(): TrinaryLogic
{
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
}

public function strSplitReturnsEmptyArray(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80200, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function substrReturnFalseInsteadOfEmptyString(): TrinaryLogic
{
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
}

public function supportsAllUnicodeScalarCodePointsInMbSubstituteCharacter(): TrinaryLogic
{
return IntegerRangeType::fromInterval(70200, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function supportsHhPrintfSpecifier(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function supportsPassNoneEncodings(): TrinaryLogic
{
return IntegerRangeType::fromInterval(null, 70299)->isSuperTypeOf($this->phpVersions)->result;
}

public function throwsOnInvalidMbStringEncoding(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
}

public function throwsValueErrorForInvalidRoundingMode(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
}

}
17 changes: 10 additions & 7 deletions src/Type/Php/ArrayChunkFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
Expand All @@ -19,10 +18,6 @@
final class ArrayChunkFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{

public function __construct(private PhpVersion $phpVersion)
{
}

public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return $functionReflection->getName() === 'array_chunk';
Expand All @@ -37,13 +32,21 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,

$arrayType = $scope->getType($args[0]->value);
if ($arrayType->isArray()->no()) {
return $this->phpVersion->arrayFunctionsReturnNullWithNonArray() ? new NullType() : new NeverType();
if ($scope->getPhpVersion()->arrayFunctionsReturnNullWithNonArray()->no()) {
return new NeverType();
}

return new NullType();
}

$lengthType = $scope->getType($args[1]->value);
$negativeOrZero = IntegerRangeType::fromInterval(null, 0);
if ($negativeOrZero->isSuperTypeOf($lengthType)->yes()) {
return $this->phpVersion->throwsValueErrorForInternalFunctions() ? new NeverType() : new NullType();
if ($scope->getPhpVersion()->throwsValueErrorForInternalFunctions()->yes()) {
return new NeverType();
}

return new NullType();
}

$preserveKeysType = isset($args[2]) ? $scope->getType($args[2]->value) : new ConstantBooleanType(false);
Expand Down
7 changes: 1 addition & 6 deletions src/Type/Php/ArrayChunkFunctionThrowTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\DynamicFunctionThrowTypeExtension;
use PHPStan\Type\IntegerRangeType;
Expand All @@ -20,18 +19,14 @@
final class ArrayChunkFunctionThrowTypeExtension implements DynamicFunctionThrowTypeExtension
{

public function __construct(private PhpVersion $phpVersion)
{
}

public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return $functionReflection->getName() === 'array_chunk';
}

public function getThrowTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $funcCall, Scope $scope): ?Type
{
if (!$this->phpVersion->throwsValueErrorForInternalFunctions()) {
if ($scope->getPhpVersion()->throwsValueErrorForInternalFunctions()->no()) {
return new VoidType();
}

Expand Down
26 changes: 12 additions & 14 deletions src/Type/Php/ArrayColumnHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Accessory\AccessoryArrayListType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
Expand All @@ -21,12 +20,6 @@
final class ArrayColumnHelper
{

public function __construct(
private PhpVersion $phpVersion,
)
{
}

/**
* @return array{Type, TrinaryLogic}
*/
Expand Down Expand Up @@ -74,7 +67,7 @@ public function handleAnyArray(Type $arrayType, Type $columnType, Type $indexTyp
}

$returnKeyType = $this->getReturnIndexType($arrayType, $indexType, $scope);
$returnType = new ArrayType($this->castToArrayKeyType($returnKeyType), $returnValueType);
$returnType = new ArrayType($this->castToArrayKeyType($returnKeyType, $scope), $returnValueType);

if ($iterableAtLeastOnce->yes()) {
$returnType = TypeCombinator::intersect($returnType, new NonEmptyArrayType());
Expand Down Expand Up @@ -113,7 +106,7 @@ public function handleConstantArray(ConstantArrayType $arrayType, Type $columnTy
}

if ($keyType !== null) {
$keyType = $this->castToArrayKeyType($keyType);
$keyType = $this->castToArrayKeyType($keyType, $scope);
}
$builder->setOffsetValueType($keyType, $valueType, $arrayType->isOptionalKey($i));
}
Expand All @@ -131,9 +124,9 @@ public function handleConstantArray(ConstantArrayType $arrayType, Type $columnTy
if ($unsealedKeyFromIndex instanceof NeverType) {
$unsealedKey = $unsealedTypes[0];
} elseif ($unsealedKeyCertainty->yes()) {
$unsealedKey = $this->castToArrayKeyType($unsealedKeyFromIndex);
$unsealedKey = $this->castToArrayKeyType($unsealedKeyFromIndex, $scope);
} else {
$unsealedKey = $this->castToArrayKeyType(TypeCombinator::union($unsealedKeyFromIndex, new IntegerType()));
$unsealedKey = $this->castToArrayKeyType(TypeCombinator::union($unsealedKeyFromIndex, new IntegerType()), $scope);
}
} else {
// `null` indexType keeps integer-keyed list semantics —
Expand Down Expand Up @@ -221,18 +214,23 @@ private function getOffsetOrProperty(Type $type, Type $offsetOrProperty, Scope $
return [TypeCombinator::union(...$returnTypes), $certainty];
}

private function castToArrayKeyType(Type $type): Type
private function castToArrayKeyType(Type $type, Scope $scope): Type
{
$throwsTypeError = $scope->getPhpVersion()->throwsTypeErrorForInternalFunctions();
$isArray = $type->isArray();
if ($isArray->yes()) {
return $this->phpVersion->throwsTypeErrorForInternalFunctions() ? new NeverType() : new IntegerType();
if ($throwsTypeError->yes()) {
return new NeverType();
}

return new IntegerType();
}
if ($isArray->no()) {
return $type->toArrayKey();
}
$withoutArrayType = TypeCombinator::remove($type, new ArrayType(new MixedType(), new MixedType()));
$keyType = $withoutArrayType->toArrayKey();
if ($this->phpVersion->throwsTypeErrorForInternalFunctions()) {
if ($throwsTypeError->yes()) {
return $keyType;
}
return TypeCombinator::union($keyType, new IntegerType());
Expand Down
7 changes: 3 additions & 4 deletions src/Type/Php/ArrayCombineFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
Expand All @@ -20,7 +19,6 @@ final class ArrayCombineFunctionReturnTypeExtension implements DynamicFunctionRe

public function __construct(
private ArrayCombineHelper $arrayCombineHelper,
private PhpVersion $phpVersion,
)
{
}
Expand All @@ -45,15 +43,16 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return $returnType;
}

$throwsValueError = $scope->getPhpVersion()->throwsValueErrorForInternalFunctions();
if ($hasValueError->yes()) {
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
if ($throwsValueError->yes()) {
return new NeverType();
}

return new ConstantBooleanType(false);
}

if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
if ($throwsValueError->yes()) {
return $returnType;
}

Expand Down
11 changes: 4 additions & 7 deletions src/Type/Php/ArrayFillFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Accessory\AccessoryArrayListType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
Expand All @@ -28,10 +27,6 @@ final class ArrayFillFunctionReturnTypeExtension implements DynamicFunctionRetur

private const MAX_SIZE_USE_CONSTANT_ARRAY = 100;

public function __construct(private PhpVersion $phpVersion)
{
}

public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return $functionReflection->getName() === 'array_fill';
Expand All @@ -46,12 +41,14 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,

$numberType = $scope->getType($args[1]->value);
$isValidNumberType = IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($numberType);
$throwsValueError = $scope->getPhpVersion()->throwsValueErrorForInternalFunctions();

// check against negative-int, which is not allowed
if ($isValidNumberType->no()) {
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
if ($throwsValueError->yes()) {
return new NeverType();
}

return new ConstantBooleanType(false);
}

Expand Down Expand Up @@ -96,7 +93,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
$resultType = TypeCombinator::intersect($resultType, new NonEmptyArrayType());
}

if (!$isValidNumberType->yes() && !$this->phpVersion->throwsValueErrorForInternalFunctions()) {
if (!$isValidNumberType->yes() && !$throwsValueError->yes()) {
$resultType = TypeCombinator::union($resultType, new ConstantBooleanType(false));
}

Expand Down
11 changes: 5 additions & 6 deletions src/Type/Php/ArrayFillKeysFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
Expand All @@ -19,10 +18,6 @@
final class ArrayFillKeysFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{

public function __construct(private PhpVersion $phpVersion)
{
}

public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return $functionReflection->getName() === 'array_fill_keys';
Expand All @@ -37,7 +32,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,

$keysType = $scope->getType($args[0]->value);
if ($keysType->isArray()->no()) {
return $this->phpVersion->arrayFunctionsReturnNullWithNonArray() ? new NullType() : new NeverType();
if ($scope->getPhpVersion()->arrayFunctionsReturnNullWithNonArray()->no()) {
return new NeverType();
}

return new NullType();
}

$filled = $keysType->fillKeysArray($scope->getType($args[1]->value));
Expand Down
4 changes: 1 addition & 3 deletions src/Type/Php/ArrayFilterFunctionReturnTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
Expand Down Expand Up @@ -49,7 +48,6 @@ final class ArrayFilterFunctionReturnTypeHelper

public function __construct(
private ReflectionProvider $reflectionProvider,
private PhpVersion $phpVersion,
)
{
}
Expand All @@ -70,7 +68,7 @@ public function getType(Scope $scope, ?Expr $arrayArg, ?Expr $callbackArg, ?Expr
}

if ($arrayArgType instanceof MixedType) {
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
if ($scope->getPhpVersion()->throwsValueErrorForInternalFunctions()->yes()) {
return new ArrayType(new MixedType(), new MixedType());
}

Expand Down
Loading
Loading