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.
- Enhancements in handling parameters passed by reference
- Validate inline PHPDoc
@var
tag type - List type enforced
- Always
true
conditions always reported: previously reported only with phpstan-strict-rules, this is now always reported.
It'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
It'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
These options have been removed because PHPStan now always behaves as if these were set to true
:
checkAlwaysTrueCheckTypeFunctionCall
checkAlwaysTrueInstanceof
checkAlwaysTrueStrictComparison
checkAlwaysTrueLooseComparison
It has been replaced with excludePaths
.
If 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.
- phpstan-doctrine
- Removed config parameter
searchOtherMethodsForQueryBuilderBeginning
(extension now behaves as when this was set totrue
) - Removed config parameter
queryBuilderFastAlgorithm
(extension now behaves as when this was set tofalse
)
- Removed config parameter
- phpstan-symfony
- Removed legacy options with
_
in the name container_xml_path
-> usecontainerXmlPath
constant_hassers
-> useconstantHassers
console_application_loader
-> useconsoleApplicationLoader
- Removed legacy options with
- Removed unused config parameter
cache.nodesByFileCountMax
- Removed unused config parameter
memoryLimitFile
- Removed unused feature toggle
disableRuntimeReflectionProvider
- Removed unused config parameter
staticReflectionClassNamePatterns
- Remove
fixerTmpDir
config parameter, usepro.tmpDir
instead - Remove
tempResultCachePath
config parameter, useresultCachePath
instead additionalConfigFiles
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 your composer.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(),
];
Learn more: Why Is instanceof *Type Wrong and Getting Deprecated?
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:
- InClassMethodNode::getMethodReflection()
- InFunctionNode::getFunctionReflection()
- FunctionReturnStatementsNode::getFunctionReflection()
- MethodReturnStatementsNode::getMethodReflection()
- Scope::getFunction()
Before:
$function = $node->getFunctionReflection();
$returnType = ParametersAcceptorSelector::selectSingle($function->getVariants())->getReturnType();
After:
$returnType = $node->getFunctionReflection()->getReturnType();
PHPStan\Analyser\TypeSpecifier::create()
now accepts (all parameters are required):
Expr $expr
Type $type
TypeSpecifierContext $context
Scope $scope
If 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 $sureTypes
array $sureNotTypes
If 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).
Type::getArrays()
now returns list<ArrayType|ConstantArrayType>
.
Using $type instanceof ArrayType
is being deprecated anyway so the impact of this change should be minimal.
This method now 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).
Learn more: https://phpstan.org/blog/preprocessing-ast-for-custom-rules
As a replacement you can implement PHPStan\Type\ExpressionTypeResolverExtension
interface instead and register it as a service.
Use 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())
.
- Classes that were previously
@final
were madefinal
- Parameter
$callableParameters
ofMutatingScope::enterAnonymousFunction()
andenterArrowFunction()
made required - Parameter
StatementContext $context
ofNodeScopeResolver::processStmtNodes()
made required - ClassPropertiesNode - remove
$extensions
parameter fromgetUninitializedProperties()
Type::getSmallerType()
,Type::getSmallerOrEqualType()
,Type::getGreaterType()
,Type::getGreaterOrEqualType()
,Type::isSmallerThan()
,Type::isSmallerThanOrEqual()
now requirePhpVersion
as argument.CompoundType::isGreaterThan()
,CompoundType::isGreaterThanOrEqual()
now requirePhpVersion
as argument.- Removed
ReflectionProvider::supportsAnonymousClasses()
(all reflection providers support anonymous classes) - Remove
ArrayType::generalizeKeys()
- Remove
ArrayType::count()
, useType::getArraySize()
instead - Remove
ArrayType::castToArrayKeyType()
,Type::toArrayKey()
instead - Remove
UnionType::pickTypes()
, usepickFromTypes()
instead - Remove
RegexArrayShapeMatcher::matchType()
, usematchExpr()
instead - Remove unused
PHPStanTestCase::$useStaticReflectionProvider
- Remove
PHPStanTestCase::getReflectors()
, usegetReflector()
instead - Remove
ClassReflection::getFileNameWithPhpDocs()
, usegetFileName()
instead - Remove
AnalysisResult::getInternalErrors()
, usegetInternalErrorObjects()
instead - Remove
ConstantReflection::getValue()
, usegetValueExpr()
instead. To getType
fromExpr
, useScope::getType()
orInitializerExprTypeResolver::getType()
- Remove
PropertyTag::getType()
, usegetReadableType()
/getWritableType()
instead - Remove
GenericTypeVariableResolver
, useType::getTemplateType()
instead - Rename
Type::isClassStringType()
toType::isClassString()
- Remove
Scope::isSpecified()
, usehasExpressionType()
instead - Remove
ConstantArrayType::isEmpty()
, useisIterableAtLeastOnce()->no()
instead - Remove
ConstantArrayType::getNextAutoIndex()
- Removed methods from
ConstantArrayType
-getFirst*Type
andgetLast*Type
- Use
getFirstIterable*Type
andgetLastIterable*Type
instead
- Use
- Remove
ConstantArrayType::generalizeToArray()
- Remove
ConstantArrayType::findTypeAndMethodName()
, usefindTypeAndMethodNames()
instead - Remove
ConstantArrayType::removeLast()
, useType::popArray()
instead - Remove
ConstantArrayType::removeFirst()
, useType::shiftArray()
instead - Remove
ConstantArrayType::reverse()
, useType::reverseArray()
instead - Remove
ConstantArrayType::chunk()
, useType::chunkArray()
instead - Remove
ConstantArrayType::slice()
, useType::sliceArray()
instead - Made
TypeUtils
thinner by removing methods:- Remove
TypeUtils::getArrays()
andgetAnyArrays()
, useType::getArrays()
instead - Remove
TypeUtils::getConstantArrays()
andgetOldConstantArrays()
, useType::getConstantArrays()
instead - Remove
TypeUtils::getConstantStrings()
, useType::getConstantStrings()
instead - Remove
TypeUtils::getConstantTypes()
andgetAnyConstantTypes()
, useType::isConstantValue()
orType::generalize()
- Remove
TypeUtils::generalizeType()
, useType::generalize()
instead - Remove
TypeUtils::getDirectClassNames()
, useType::getObjectClassNames()
instead - Remove
TypeUtils::getConstantScalars()
, useType::isConstantScalarValue()
orType::getConstantScalarTypes()
instead - Remove
TypeUtils::getEnumCaseObjects()
, useType::getEnumCases()
instead - Remove
TypeUtils::containsCallable()
, useType::isCallable()
instead
- Remove
- Removed
Scope::doNotTreatPhpDocTypesAsCertain()
, usegetNativeType()
instead - Parameter
$isList
inConstantArrayType
constructor can only beTrinaryLogic
, no longerbool
- Parameter
$nextAutoIndexes
inConstantArrayType
constructor can only benon-empty-list<int>
, no longerint
- Remove
ConstantType
interface, useType::isConstantValue()
instead acceptsNamedArguments()
inFunctionReflection
,ExtendedMethodReflection
andCallableParametersAcceptor
interfaces returnsTrinaryLogic
instead ofbool
- Remove
FunctionReflection::isFinal()
Type::getProperty()
now returnsExtendedPropertyReflection
- Remove
__set_state()
on objects that should not be serialized in cache - Parameter
$selfClass
ofTypehintHelper::decideTypeFromReflection()
no longer acceptsstring
LevelsTestCase::dataTopics()
data provider made staticPHPStan\Node\Printer\Printer
no longer autowired asPhpParser\PrettyPrinter\Standard
, usePHPStan\Node\Printer\Printer
in the typehint- Remove
Type::acceptsWithReason()
,Type:accepts()
return type changed fromTrinaryLogic
toAcceptsResult
- Remove
CompoundType::isAcceptedWithReasonBy()
,CompoundType::isAcceptedBy()
return type changed fromTrinaryLogic
toAcceptsResult
RemoveType::isSuperTypeOfWithReason()
,Type:isSuperTypeOf()
return type changed fromTrinaryLogic
toIsSuperTypeOfResult
- Remove
CompoundType::isSubTypeOfWithReasonBy()
,CompoundType::isSubTypeOf()
return type changed fromTrinaryLogic
toIsSuperTypeOfResult
- Remove
TemplateType::isValidVarianceWithReason()
, changedTemplateType::isValidVariance()
return type toIsSuperTypeOfResult
RuleLevelHelper::accepts()
return type changed frombool
toRuleLevelHelperAcceptsResult
- Changes around
ClassConstantReflection
- Class
ClassConstantReflection
removed from BC promise, renamed toRealClassConstantReflection
- Interface
ConstantReflection
renamed toClassConstantReflection
- Added more methods around PHPDoc types and native types to the (new)
ClassConstantReflection
- Interface
GlobalConstantReflection
renamed toConstantReflection
- Class
- Renamed interfaces and classes from
*WithPhpDocs
toExtended*
ParametersAcceptorWithPhpDocs
->ExtendedParametersAcceptor
ParameterReflectionWithPhpDocs
->ExtendedParameterReflection
FunctionVariantWithPhpDocs
->ExtendedFunctionVariant
ClassPropertyNode::getNativeType()
return type changed from AST node toType|null
- Class
PHPStan\Node\ClassMethod
(accessible fromClassMethodsNode
) is no longer an AST node- Call
PHPStan\Node\ClassMethod::getNode()
to access the original AST node
- Call