UPGRADING.md
PHPStan now requires PHP 7.4 or newer to run.
The best way to get ready for upgrade to PHPStan 2.0 is to update to the latest PHPStan 1.12 release and enable Bleeding Edge. This will enable the new rules and behaviours that 2.0 turns on for all users.
Also make sure to install and enable phpstan/phpstan-deprecation-rules.
Once you get to a green build with no deprecations showed on latest PHPStan 1.12.x with Bleeding Edge enabled, you can update all your related PHPStan dependencies to 2.0 in composer.json:
"require-dev": {
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-doctrine": "^2.0",
"phpstan/phpstan-nette": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpstan/phpstan-symfony": "^2.0",
"phpstan/phpstan-webmozart-assert": "^2.0",
...
}
Don't forget to update 3rd party PHPStan extensions as well.
After changing your composer.json, run composer update 'phpstan/*' -W.
It's up to you whether you go through the new reported errors or if you just put them all to the baseline ;) Everyone who's on PHPStan 1.12 should be able to upgrade to PHPStan 2.0.
@var tag typetrue conditions always reported: previously reported only with phpstan-strict-rules, this is now always reported.checkMissingIterableValueTypeIt's strongly recommended to add the missing array typehints.
If you want to continue ignoring missing typehints from arrays, add missingType.iterableValue error identifier to your ignoreErrors:
parameters:
ignoreErrors:
-
identifier: missingType.iterableValue
checkGenericClassInNonGenericObjectTypeIt's strongly recommended to add the missing generic typehints.
If you want to continue ignoring missing typehints from generics, add missingType.generics error identifier to your ignoreErrors:
parameters:
ignoreErrors:
-
identifier: missingType.generics
checkAlwaysTrue* optionsThese options have been removed because PHPStan now always behaves as if these were set to true:
checkAlwaysTrueCheckTypeFunctionCallcheckAlwaysTrueInstanceofcheckAlwaysTrueStrictComparisoncheckAlwaysTrueLooseComparisonexcludes_analyseIt has been replaced with excludePaths.
excludePaths and ignoreErrors have to be a valid file path or a fnmatch patternIf you are excluding a file path that might not exist but you still want to have it in excludePaths, append (?):
parameters:
excludePaths:
- tests/*/data/*
- src/broken
- node_modules (?) # optional path, might not exist
If you have the same situation in ignoreErrors (ignoring an error in a path that might not exist), use reportUnmatchedIgnoredErrors: false.
parameters:
reportUnmatchedIgnoredErrors: false
Appending (?) in ignoreErrors is not supported.
searchOtherMethodsForQueryBuilderBeginning (extension now behaves as when this was set to true)queryBuilderFastAlgorithm (extension now behaves as when this was set to false)_ in the namecontainer_xml_path -> use containerXmlPathconstant_hassers -> use constantHassersconsole_application_loader -> use consoleApplicationLoadercache.nodesByFileCountMaxmemoryLimitFiledisableRuntimeReflectionProviderstaticReflectionClassNamePatternsfixerTmpDir config parameter, use pro.tmpDir insteadtempResultCachePath config parameter, use resultCachePath insteadadditionalConfigFiles config parameter must be a list[!NOTE] Please switch to PHPStan 2.0 in a new major version of your extension. It's not feasible to try to support both PHPStan 1.x and PHPStan 2.x with the same extension code.
You can definitely get closer to supporting PHPStan 2.0 without increasing major version by solving reported deprecations and other issues by analysing your extension code with PHPStan & phpstan-deprecation-rules & Bleeding Edge, but the final leap and solving backward incompatibilities should be done by requiring
"phpstan/phpstan": "^2.0"in yourcomposer.json, and releasing a new major version.
See UPGRADING guide for PHP-Parser.
The most notable change is how throw statement is represented. Previously, throw statements like throw $e; were represented using the Stmt\Throw_ class, while uses inside other expressions (such as $x ?? throw $e) used the Expr\Throw_ class.
Now, throw $e; is represented as a Stmt\Expression that contains an Expr\Throw_. The
Stmt\Throw_ class has been removed.
See UPGRADING guide for phpstan/phpdoc-parser.
Identifiers are also required in custom rules.
Learn more: Using RuleErrorBuilder to enrich reported errors in custom rules
Before:
return ['My error'];
After:
return [
RuleErrorBuilder::message('My error')
->identifier('my.error')
->build(),
];
instanceof *Type in favour of new methods on Type interfaceLearn more: Why Is instanceof *Type Wrong and Getting Deprecated?
ParametersAcceptorSelector::selectSingle()Use ParametersAcceptorSelector::selectFromArgs() instead. It should be used in most places where selectSingle() was previously used, like dynamic return type extensions.
Before:
$defaultReturnType = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
After:
$defaultReturnType = ParametersAcceptorSelector::selectFromArgs(
$scope,
$functionCall->getArgs(),
$functionReflection->getVariants()
)->getReturnType();
If you're analysing function or method body itself and you're using one of the following methods, ask for getParameters() and getReturnType() directly on the reflection object:
Before:
$function = $node->getFunctionReflection();
$returnType = ParametersAcceptorSelector::selectSingle($function->getVariants())->getReturnType();
After:
$returnType = $node->getFunctionReflection()->getReturnType();
TypeSpecifier::create() and SpecifiedTypes constructor parametersPHPStan\Analyser\TypeSpecifier::create() now accepts (all parameters are required):
Expr $exprType $typeTypeSpecifierContext $contextScope $scopeIf you want to change $overwrite or $rootExpr (previous parameters also used to be accepted by this method), call setAlwaysOverwriteTypes() and setRootExpr() on SpecifiedTypes (object returned by TypeSpecifier::create()). These methods return a new object (SpecifiedTypes is immutable).
SpecifiedTypes constructor now accepts:
array $sureTypesarray $sureNotTypesIf you want to change $overwrite or $rootExpr (previous parameters also used to be accepted by the constructor), call setAlwaysOverwriteTypes() and setRootExpr(). These methods return a new object (SpecifiedTypes is immutable).
ConstantArrayType no longer extends ArrayTypeType::getArrays() now returns list<ArrayType|ConstantArrayType>.
Using $type instanceof ArrayType is being deprecated anyway so the impact of this change should be minimal.
TypeSpecifier::specifyTypesInCondition()This method no longer accepts Expr $rootExpr. If you want to change it, call setRootExpr() on SpecifiedTypes (object returned by TypeSpecifier::specifyTypesInCondition()). setRootExpr() method returns a new object (SpecifiedTypes is immutable).
parent, previous, next are no longer availableLearn more: https://phpstan.org/blog/preprocessing-ast-for-custom-rules
scopeClassAs a replacement you can implement PHPStan\Type\ExpressionTypeResolverExtension interface instead and register it as a service.
PHPStan\Broker\BrokerUse PHPStan\Reflection\ReflectionProvider instead.
BrokerAwareExtension was also removed. Ask for ReflectionProvider in the extension constructor instead.
Instead of PHPStanTestCase::createBroker(), call PHPStanTestCase::createReflectionProvider().
Removed static methods from AccessoryArrayListType class:
isListTypeEnabled()setListTypeEnabled()intersectWith()Instead of AccessoryArrayListType::intersectWith($type), do TypeCombinator::intersect($type, new AccessoryArrayListType()).
@final were made final$callableParameters of MutatingScope::enterAnonymousFunction() and enterArrowFunction() made requiredStatementContext $context of NodeScopeResolver::processStmtNodes() made required$extensions parameter from getUninitializedProperties()Type::getSmallerType(), Type::getSmallerOrEqualType(), Type::getGreaterType(), Type::getGreaterOrEqualType(), Type::isSmallerThan(), Type::isSmallerThanOrEqual() now require PhpVersion as argument.CompoundType::isGreaterThan(), CompoundType::isGreaterThanOrEqual() now require PhpVersion as argument.ReflectionProvider::supportsAnonymousClasses() (all reflection providers support anonymous classes)ArrayType::generalizeKeys()ArrayType::count(), use Type::getArraySize() insteadArrayType::castToArrayKeyType(), Type::toArrayKey() insteadUnionType::pickTypes(), use pickFromTypes() insteadRegexArrayShapeMatcher::matchType(), use matchExpr() insteadPHPStanTestCase::$useStaticReflectionProviderPHPStanTestCase::getReflectors(), use getReflector() insteadClassReflection::getFileNameWithPhpDocs(), use getFileName() insteadAnalysisResult::getInternalErrors(), use getInternalErrorObjects() insteadConstantReflection::getValue(), use getValueExpr() instead. To get Type from Expr, use Scope::getType() or InitializerExprTypeResolver::getType()PropertyTag::getType(), use getReadableType() / getWritableType() insteadGenericTypeVariableResolver, use Type::getTemplateType() insteadType::isClassStringType() to Type::isClassString()Scope::isSpecified(), use hasExpressionType() insteadConstantArrayType::isEmpty(), use isIterableAtLeastOnce()->no() insteadConstantArrayType::getNextAutoIndex()ConstantArrayType - getFirst*Type and getLast*Type
getFirstIterable*Type and getLastIterable*Type insteadConstantArrayType::generalizeToArray()ConstantArrayType::findTypeAndMethodName(), use findTypeAndMethodNames() insteadConstantArrayType::removeLast(), use Type::popArray() insteadConstantArrayType::removeFirst(), use Type::shiftArray() insteadConstantArrayType::reverse(), use Type::reverseArray() insteadConstantArrayType::chunk(), use Type::chunkArray() insteadConstantArrayType::slice(), use Type::sliceArray() insteadTypeUtils thinner by removing methods:
TypeUtils::getArrays() and getAnyArrays(), use Type::getArrays() insteadTypeUtils::getConstantArrays() and getOldConstantArrays(), use Type::getConstantArrays() insteadTypeUtils::getConstantStrings(), use Type::getConstantStrings() insteadTypeUtils::getConstantTypes() and getAnyConstantTypes(), use Type::isConstantValue() or Type::generalize()TypeUtils::generalizeType(), use Type::generalize() insteadTypeUtils::getDirectClassNames(), use Type::getObjectClassNames() insteadTypeUtils::getConstantScalars(), use Type::isConstantScalarValue() or Type::getConstantScalarTypes() insteadTypeUtils::getEnumCaseObjects(), use Type::getEnumCases() insteadTypeUtils::containsCallable(), use Type::isCallable() insteadScope::doNotTreatPhpDocTypesAsCertain(), use getNativeType() instead$isList in ConstantArrayType constructor can only be TrinaryLogic, no longer bool$nextAutoIndexes in ConstantArrayType constructor can only be non-empty-list<int>, no longer intConstantType interface, use Type::isConstantValue() insteadacceptsNamedArguments() in FunctionReflection, ExtendedMethodReflection and CallableParametersAcceptor interfaces returns TrinaryLogic instead of boolFunctionReflection::isFinal()Type::getProperty() now returns ExtendedPropertyReflection__set_state() on objects that should not be serialized in cache$selfClass of TypehintHelper::decideTypeFromReflection() no longer accepts stringLevelsTestCase::dataTopics() data provider made staticPHPStan\Node\Printer\Printer no longer autowired as PhpParser\PrettyPrinter\Standard, use PHPStan\Node\Printer\Printer in the typehintType::acceptsWithReason(), Type:accepts() return type changed from TrinaryLogic to AcceptsResultCompoundType::isAcceptedWithReasonBy(), CompoundType::isAcceptedBy() return type changed from TrinaryLogic to AcceptsResult
Remove Type::isSuperTypeOfWithReason(), Type:isSuperTypeOf() return type changed from TrinaryLogic to IsSuperTypeOfResultCompoundType::isSubTypeOfWithReasonBy(), CompoundType::isSubTypeOf() return type changed from TrinaryLogic to IsSuperTypeOfResultTemplateType::isValidVarianceWithReason(), changed TemplateType::isValidVariance() return type to IsSuperTypeOfResultRuleLevelHelper::accepts() return type changed from bool to RuleLevelHelperAcceptsResultClassConstantReflection
ClassConstantReflection removed from BC promise, renamed to RealClassConstantReflectionConstantReflection renamed to ClassConstantReflectionClassConstantReflectionGlobalConstantReflection renamed to ConstantReflection*WithPhpDocs to Extended*
ParametersAcceptorWithPhpDocs -> ExtendedParametersAcceptorParameterReflectionWithPhpDocs -> ExtendedParameterReflectionFunctionVariantWithPhpDocs -> ExtendedFunctionVariantClassPropertyNode::getNativeType() return type changed from AST node to Type|nullPHPStan\Node\ClassMethod (accessible from ClassMethodsNode) is no longer an AST node
PHPStan\Node\ClassMethod::getNode() to access the original AST node