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
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function getNodeType(): string
public function processNode(Node $node, Scope $scope): array
{
$method = $node->getMethodReflection();
$prototypeData = $this->methodPrototypeFinder->findPrototype($node->getClassReflection(), $method->getName());
$prototypeData = $this->methodPrototypeFinder->findPrototype($node->getClassReflection(), $method->getName(), $scope->getPhpVersion());
if ($prototypeData === null) {
return [];
}
Expand Down
8 changes: 4 additions & 4 deletions src/Analyser/ExprHandler/ArrayHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ private function getWalkedArrayType(NodeScopeResolver $nodeScopeResolver, array
}

if ($inner instanceof Array_) {
return $this->initializerExprTypeResolver->getArrayType($inner, $getType);
return $this->initializerExprTypeResolver->getArrayType($inner, $getType, $scope->getPhpVersion());
}

if ($inner instanceof Scalar\String_ || $inner instanceof Scalar\Int_ || $inner instanceof Scalar\Float_) {
Expand All @@ -309,7 +309,7 @@ private function getWalkedArrayType(NodeScopeResolver $nodeScopeResolver, array
return new MixedType();
};

return $this->initializerExprTypeResolver->getArrayType($walk['expr'], $getType);
return $this->initializerExprTypeResolver->getArrayType($walk['expr'], $getType, $scope->getPhpVersion());
}

/**
Expand Down Expand Up @@ -394,7 +394,7 @@ private function createResult(NodeScopeResolver $nodeScopeResolver, array $walk,
}

throw new ShouldNotHappenException();
});
}, $beforeScope->getPhpVersion());

if (
count($expr->items) === 2
Expand Down Expand Up @@ -594,7 +594,7 @@ private function collectAbsorbedItems(Array_ $expr, array $itemResults, Mutating
return $scope;
}

$arrayType = $this->initializerExprTypeResolver->getArrayType($expr, static fn (Expr $inner): Type => $itemResults[spl_object_id($inner)]->getType());
$arrayType = $this->initializerExprTypeResolver->getArrayType($expr, static fn (Expr $inner): Type => $itemResults[spl_object_id($inner)]->getType(), $scope->getPhpVersion());

return $scope->addTemplateArgumentConstraints(ClosureSignatureInference::collectAbsorbedInto($itemTypes, $arrayType));
}
Expand Down
6 changes: 2 additions & 4 deletions src/Analyser/ExprHandler/AssignHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
use PHPStan\Node\Variable\VariableWrite;
use PHPStan\Node\VariableAssignNode;
use PHPStan\Node\VirtualNode;
use PHPStan\Php\PhpVersion;
use PHPStan\Rules\Properties\PropertyReflectionFinder;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
Expand Down Expand Up @@ -142,7 +141,6 @@ final class AssignHandler implements ExprHandler
public function __construct(
private TemplateArgumentObserver $templateArgumentObserver,
private VarAnnotationProcessor $varAnnotationProcessor,
private PhpVersion $phpVersion,
private ExprPrinter $exprPrinter,
private MatchHandler $matchHandler,
private TernaryHandler $ternaryHandler,
Expand Down Expand Up @@ -1584,7 +1582,7 @@ public function applyWrite(
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
$scope = $result->getScope();

if ($var->name instanceof Expr && $this->phpVersion->supportsPropertyHooks()) {
if ($var->name instanceof Expr && !$scope->getPhpVersion()->supportsPropertyHooks()->no()) {
$throwPoints[] = InternalThrowPoint::createImplicit($scope, $var);
}

Expand Down Expand Up @@ -1647,7 +1645,7 @@ public function applyWrite(
) {
$throwPoints[] = InternalThrowPoint::createExplicit($scope, new ObjectType(TypeError::class), $assignedExpr, false);
}
if ($this->phpVersion->supportsPropertyHooks()) {
if (!$scope->getPhpVersion()->supportsPropertyHooks()->no()) {
$throwPoints = array_merge($throwPoints, $this->propertyHookThrowPointsResolver->getThrowPointsFromPropertyHook($scope, $var, $nativeProperty, 'set'));
}
if ($enterExpressionAssign) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\ErrorType;
Expand All @@ -25,7 +24,6 @@ final class ImplicitToStringCallHelper
{

public function __construct(
private PhpVersion $phpVersion,
private MethodThrowPointHelper $methodThrowPointHelper,
private MethodCallReturnTypeHelper $methodCallReturnTypeHelper,
private ExpressionResultFactory $expressionResultFactory,
Expand Down Expand Up @@ -73,7 +71,7 @@ public function processImplicitToStringCall(Expr $expr, MutatingScope $scope, Ex
);
}

if ($this->phpVersion->throwsOnStringCast()) {
if (!$scope->getPhpVersion()->throwsOnStringCast()->no()) {
// the __toString() call's return type resolves directly (the receiver
// type is already in hand); the fabricated node is only the payload
// dynamic extensions receive - nothing walks it
Expand Down
6 changes: 2 additions & 4 deletions src/Analyser/ExprHandler/PropertyFetchHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
use PHPStan\Analyser\VariableFlowBuilder;
use PHPStan\Dependency\Dependencies;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Rules\Properties\FoundPropertyReflection;
use PHPStan\Rules\Properties\PropertyReflectionFinder;
use PHPStan\ShouldNotHappenException;
Expand All @@ -45,7 +44,6 @@ final class PropertyFetchHandler implements ExprHandler
{

public function __construct(
private PhpVersion $phpVersion,
private PropertyReflectionFinder $propertyReflectionFinder,
private ExpressionResultFactory $expressionResultFactory,
private PropertyHookThrowPointsResolver $propertyHookThrowPointsResolver,
Expand Down Expand Up @@ -87,7 +85,7 @@ public function composeResult(NodeScopeResolver $nodeScopeResolver, PropertyFetc
$scope = $varResult->getScope();
$mayShortCircuit = false;
if ($expr->name instanceof Identifier) {
if ($this->phpVersion->supportsPropertyHooks()) {
if (!$scopeBeforeVar->getPhpVersion()->supportsPropertyHooks()->no()) {
$propertyName = $expr->name->toString();
$propertyHolderType = $varResult->getType();
$propertyReflection = $scopeBeforeVar->getInstancePropertyReflection($propertyHolderType, $propertyName);
Expand Down Expand Up @@ -115,7 +113,7 @@ public function composeResult(NodeScopeResolver $nodeScopeResolver, PropertyFetc
// short-circuited world
$scope = $scope->mergeWith($varResult->getScope());
}
if ($this->phpVersion->supportsPropertyHooks()) {
if (!$scope->getPhpVersion()->supportsPropertyHooks()->no()) {
$throwPoints[] = InternalThrowPoint::createImplicit($scope, $expr);
}
}
Expand Down
30 changes: 12 additions & 18 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,6 @@ public function rememberConstructorScope(): self
/** @internal called by ScopeOps */
public function isReadonlyPropertyFetch(PropertyFetch $expr, bool $allowOnlyOnThis): bool
{
if (!$this->services->phpVersion->supportsReadOnlyProperties()) {
return false;
}

while ($expr instanceof PropertyFetch) {
if ($expr->var instanceof Variable) {
if (
Expand Down Expand Up @@ -436,7 +432,7 @@ public function isReadonlyPropertyFetch(PropertyFetch $expr, bool $allowOnlyOnTh
$expr = $expr->var;
}

return true;
return !$this->getPhpVersion()->supportsReadOnlyProperties()->no();
}

/** @api */
Expand Down Expand Up @@ -2576,20 +2572,18 @@ public function enterAnonymousFunctionWithoutReflection(
$expressionTypes['$this'] = ExpressionTypeHolder::createYes($node, $this->getType($node));
$nativeTypes['$this'] = ExpressionTypeHolder::createYes($node, $this->getNativeType($node));

if ($this->services->phpVersion->supportsReadOnlyProperties()) {
foreach ($nonStaticExpressions as $exprString => $typeHolder) {
$expr = $typeHolder->getExpr();

if (!$expr instanceof PropertyFetch) {
continue;
}
foreach ($nonStaticExpressions as $exprString => $typeHolder) {
$expr = $typeHolder->getExpr();

if (!$this->isReadonlyPropertyFetch($expr, true)) {
continue;
}
if (!$expr instanceof PropertyFetch) {
continue;
}

$expressionTypes[$exprString] = $typeHolder;
if (!$this->isReadonlyPropertyFetch($expr, true)) {
continue;
}

$expressionTypes[$exprString] = $typeHolder;
}
}

Expand Down Expand Up @@ -3734,7 +3728,7 @@ public function assignInitializedProperty(Type $fetchedOnType, string $propertyN
if (
$function instanceof MethodReflection
&& strtolower($function->getName()) === '__clone'
&& $scope->services->phpVersion->supportsReadonlyPropertyReinitializationOnClone()
&& $scope->getPhpVersion()->supportsReadonlyPropertyReinitializationOnClone()->yes()
) {
$scope = $scope->assignExpression(new CloneReinitializationExpr($propertyName), new MixedType(), new MixedType());
}
Expand Down Expand Up @@ -5598,7 +5592,7 @@ public function canWriteProperty(ExtendedPropertyReflection $propertyReflection)
return $this->canAccessClassMember($propertyReflection);
}

if (!$this->services->phpVersion->supportsAsymmetricVisibility()) {
if ($this->getPhpVersion()->supportsAsymmetricVisibility()->no()) {
return $this->canAccessClassMember($propertyReflection);
}

Expand Down
80 changes: 80 additions & 0 deletions src/Php/PhpVersions.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ public function supportsFinalProperties(): TrinaryLogic
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
}

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

public function supportsAsymmetricVisibilityForStaticProperties(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
Expand Down Expand Up @@ -184,11 +189,21 @@ public function supportsFinalConstants(): TrinaryLogic
return IntegerRangeType::fromInterval(80100, null)->isSuperTypeOf($this->phpVersions)->result;
}

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

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

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

public function supportsFirstClassCallables(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80100, null)->isSuperTypeOf($this->phpVersions)->result;
Expand Down Expand Up @@ -224,6 +239,71 @@ public function deprecatesNonStandardCasts(): TrinaryLogic
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

public function supportsDeprecatedTraits(): TrinaryLogic
{
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
Expand Down
12 changes: 9 additions & 3 deletions src/Reflection/InitializerExprTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -531,9 +531,16 @@ public function resolveConcatType(Type $left, Type $right): Type

/**
* @param callable(Expr): Type $getTypeCallback
* @param PhpVersions|null $phpVersions the analysed scope's versions, the configured version when null
*/
public function getArrayType(Expr\Array_ $expr, callable $getTypeCallback): Type
public function getArrayType(Expr\Array_ $expr, callable $getTypeCallback, ?PhpVersions $phpVersions = null): Type
{
// unpacking an array with string keys is an Error below PHP 8.1, so the
// string keys are kept whenever any analysed version supports it
$keepStringKeys = $phpVersions !== null
? !$phpVersions->supportsArrayUnpackingWithStringKeys()->no()
: $this->phpVersion->supportsArrayUnpackingWithStringKeys();

if (count($expr->items) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
return $this->oversizedArrayBuilder->build($expr, $getTypeCallback);
}
Expand All @@ -546,7 +553,6 @@ public function getArrayType(Expr\Array_ $expr, callable $getTypeCallback): Type
if ($arrayItem->unpack) {
$constantArrays = $valueType->getConstantArrays();
if (count($constantArrays) > 0) {
$keepStringKeys = $this->phpVersion->supportsArrayUnpackingWithStringKeys();
$totalArrays = count($constantArrays);

// Unpacking merges string keys by name, while integer keys are always
Expand Down Expand Up @@ -615,7 +621,7 @@ public function getArrayType(Expr\Array_ $expr, callable $getTypeCallback): Type
} else {
$arrayBuilder->degradeToGeneralArray();

if ($this->phpVersion->supportsArrayUnpackingWithStringKeys() && !$valueType->getIterableKeyType()->isString()->no()) {
if ($keepStringKeys && !$valueType->getIterableKeyType()->isString()->no()) {
$isList = false;
$offsetType = $valueType->getIterableKeyType();

Expand Down
8 changes: 4 additions & 4 deletions src/Rules/Arrays/AllowedArrayKeysTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace PHPStan\Rules\Arrays;

use PHPStan\Php\PhpVersion;
use PHPStan\Php\PhpVersions;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
Expand All @@ -24,7 +24,7 @@
final class AllowedArrayKeysTypes
{

public static function getType(?PhpVersion $phpVersion = null, bool $reportNonIntStringArrayKey = false): Type
public static function getType(?PhpVersions $phpVersions = null, bool $reportNonIntStringArrayKey = false): Type
{
$types = [
new IntegerType(),
Expand All @@ -34,10 +34,10 @@ public static function getType(?PhpVersion $phpVersion = null, bool $reportNonIn
if (!$reportNonIntStringArrayKey) {
$types[] = new BooleanType();

if ($phpVersion === null || !$phpVersion->deprecatesImplicitlyFloatConversionToInt()) {
if ($phpVersions === null || $phpVersions->deprecatesImplicitlyFloatConversionToInt()->no()) {
$types[] = new FloatType();
}
if ($phpVersion === null || !$phpVersion->deprecatesNullArrayOffset()) {
if ($phpVersions === null || $phpVersions->deprecatesNullArrayOffset()->no()) {
$types[] = new NullType();
}
}
Expand Down
Loading
Loading