Skip to content


Choose a tag to compare
@ondrejmirtes ondrejmirtes released this 24 Jun 18:15

This release has been in the works for the past 5 months - it contains advancements on several fronts at once and I can't wait till you get your hands on it! It has the most new commits (over 500) of any PHPStan release ever, see this chart for comparison.

PHPStan 0.10 has been made possible by PHPStan helps us to keep code quality on a high level and focus on the business that matters most for us. Keep going!

Also, my work on PHPStan is enabled by generous patrons over at Patreon. If PHPStan makes your life easier, consider a contribution, it'll be greatly appreciated!

Besides many new features and improvements to the core (which you can read about below), there were many improvements made over at related packages. See the release notes for phpstan-strict-rules and check out brand new package for detecting usage of @deprecated elements - phpstan-deprecation-rules. Also, you should check out great 3rd party package from @pepakriz pepakriz/phpstan-exception-rules that enforces putting your exceptions into @throws phpDoc tag and checks that you actually catch thrown exceptions from called functions!

Major new features

  • Constant value types (#828), thanks @JanTvrdik!
    • Big improvement to PHPStan's type system
    • Enables recognizing not only types, but also values of expressions in cases where they're known
    • For example:
      • Format string for sprintf() can first be put into a variable and then used in a function call; the placeholders/arguments count mismatch will still be recognized
      • Specific types and values of accessed array offsets are now recognized (#409)
      • PHPStan can recognize impossible comparisons of specific values, like count($array) === 3 when there are only two items in the array
      • Results of arithmetic operations of literal values are also precisely calculated
      • And many more improvements that make the analysis of your code much smarter!
  • Typehints for built-in PHP functions (f486280, milestone link)
    • Until 0.9 (including), PHPStan relied on built-in reflection which doesn't contain any typehint data at all, and is also full of inconsistencies
    • PHPStan will finally tell you that you're passing wrong parameter types to PHP functions like date
    • It will also make use of documented return typehints
    • The analysis can now be quite brutal - some PHP functions can return false in case of some rare error - this will make your analysis fail if you're on level 6 or 7. If you're overwhelmed by the number of reported errors, lower your level to 5 temporarily and fix the reported errors later.
  • Type-specifying extensions (#825), thanks @lookyman!
    • PHPStan can now recognize types after custom method calls like assertInstanceOf, assertInt etc.
    • 1st party extensions for most common assertion libraries are available:
    • Works with already available detections of always-true and impossible type comparisons
    • Write your own type-specifying extensions for situations like has+nullable get methods or supports() calls - see #201, #534, #634
  • Improved callables support
    • New check: Is $c() a callable? - level 2 (c5e6f49) - #733
    • Detect valid and invalid callable strings, arrays and classes with __invoke
    • Check passed parameters count and types into all callables, including anonymous functions
  • New check: Nonexistent and inaccessible array offsets - level 3 (d86efca)
  • New check: Detect always truthy/falsey conditions - level 4 (689e956)
    • Detects cases like:
    • if ($object) { }
  • New check: invalid arithmetic and binary operations - level 2 (2917336)
    • Detects $string + $string, $int . $int etc.
    • Detects non-numeric types on +$expr and -$expr
    • Detects invalid types for ++ and -- operators
    • Also detects possible division by zero
  • New check: invalid casts - level 2 (4a72885)
    • Detects (string) $stdClass or (int) $array.
  • New check: validate types inside @throws phpDoc tag - level 2 (3d6904b) - #1001, thanks @Majkl578!
  • New check: validate types in throw keyword (44b4680) - #1041, thanks @pepakriz!
  • Concept of a benevolent union type in vein of gradual typing (8ee7c6e)
    • For example array key type with mixed key will either be int or string - (int|string) (benevolent union type is marked with additional parentheses around it) will not be reported when passed to int and string even on the highest level because the code might be actually correct. But it will be reported when passed to a completely different type, like object.
  • Changes to levels:
    • Accepting types - moved partially correct union types checks from level 5 to level 6 (d514655)
    • If method/property/constant surely does not exist on a union, report that on level 2 (previously reported on level 6) (9e38fd1)
    • Reporting of accessing properties and methods on classes with magic methods (__get, __set, __call, __callStatic) moved to level 1 (455fb7b)


  • Support for array access on objects with ArrayAccess interface (#620), thanks @pepakriz!
  • Auto-discover phpstan.neon{,.dist} config files (#509), thanks @Majkl578!
  • Allow paths and level be specified in config (#508), thanks @Majkl578!
  • array_keys, array_values, array_merge, array_fill, array_fill_keys, array_map, array_pop, array_shift dynamic return type extensions (559b9d0, 40e1782, 9e97918, a063a42, 4779c7e, 5a6f3a2)
  • TypeSpecifier: support loose comparison with null (#833), thanks @JanTvrdik!
  • Allow early terminating static calls (#822), thanks @ndench!
  • Better support for accepting objects with __toString() as strings (043081b)
  • Result of === or !== might be constant boolean (477b55d)
  • Result of instanceof might be constant boolean (00014ce)
  • Result of is_* functions might be a constant boolean type (f8c6ef0)
  • StrictComparisonOfDifferentTypesRule - check always true comparisons (off by default) (407146b) - enabled in phpstan-strict-rules
    * Improve error message for command not found (#851), thanks @fain182!
  • sorting of file-specific Error entries by file name and line number (#838, #841) - thanks @Ocramius!
  • Implemented JSON formatter (#783), thanks @soullivaneuh!
  • self, static and parent - consistent case-insensitive handling (27f084e)
  • Use Composer/XdebugHandler to avoid performance penalty from Xdebug (#874), thanks @AJenbo!
  • TypeSpecifier - support instanceof parent (af17140)
  • reset() dynamic return type extension (3485d8c)
  • Detect exception class name with wrong case in catch block (597bbed)
  • Checking wrong function name case moved to strict-rules (4de98a1)
  • Improved type detection in (object) cast (6231eb1)
  • Fixed phpDocs interpretation when analyzing traits (9aaddbc) (#174)
  • Added support for is_scalar type-specifying function (4abeceb)
  • Fixed resolving UnaryMinus type (c845439)
  • Checking for invalid part of encapsed string ("Foo $objectWithoutToString bar") - level 2 (5010703)
  • InstantiationRule - warn about incorrect case name when the class has no constructor (9a21518)
  • Improved float, int and bool cast evaluation (6e2fc06)
  • min() and max() dynamic return type extensions (58fe09f)
  • is_subclass_of type-specifying extension (4e1b2d8) - #889
  • Type-losing, although consistent behaviour of parameters passed by reference (711413f)
  • pathinfo() dynamic return type extension (4d8c625)
  • Constant values for magic constants (b492ea8)
  • Report and check return types of magic methods (71e120a)
  • No need to update scope for the last node in sequence (8c3880f)
  • Array with mixed key type have int|string iterable key type (0acbd53) - #956
  • Improved support for anonymous classes (de7fd7d, f416740, 60a396e, 0319ed1, b29af42)
  • Add API for @deprecated phpDoc tag (79ba741) - #690, thanks @iluuu1994!
  • Absolute paths in checkstyle error formatter - #550, thanks @mavimo!
  • Group errors by file in checkstyle error formatter - #553, thanks @mavimo!
  • Add API for@throws phpDoc tag - #994, thanks @pepakriz!
  • microtime() dynamic return type extension - #998, thanks @lookyman!
  • Run dynamic return extensions for all compound types (56f26a0) - #1002, thanks @CzechBoy!
  • strtotime() dynamic return type extension - #1018, thanks @lookyman!
  • Check keys appended to arrays - level 3 (85753a3)
  • AppendedArrayItemTypeRule - detect changed item type through assign ops (4b204ac)
  • Annotated arrays are no longer checked, only when in properties (ad01d80) - #1032
  • range() dynamic return type extension (40ece9a) - #1035, thanks @pepakriz!
  • Type::equals() for more efective scope merging (29b0d3a)
  • Add support for non-truthy values filtering with array_filter (6c4bbce) - $877, thanks @fmasa!
  • Registry: support multiple rules registered by a single class (1956c7f) - #1048, thanks @pepakriz!
  • Redirect warnings and other unrelated messages to stderr (8962369) - #1040, #709, thanks @mavimo!
  • Add exclude_analyse support for RobotLoader (0dfd9ab) - #1080, thanks @zviryatko!
  • do not compute elseif scope if there's no elseif nor else branch (dbb17a7)
  • substr_replace dynamic return type extension (2092a46) - thanks @CzechBoy!


  • Scope: fix resolving new parent(...) expression type (3c576e0), thanks @JanTvrdik!
  • Closure uses passed by reference influence their type in outer scope (254a454)
  • Fixed internal error when reading inheritDoc in a class with a native parent (4ab430b)
  • Fixed condition filtering of ternary operator (ea47684) - #910, #914, #917
  • Fixed loading autoload-file with relative path (5c32e0d) - #909
  • ErrorSuppress - do not report void value usage (e6d3557) - #926
  • Annotation methods - variadic parameter is always optional (8085989) - #936
  • MixedType returns valid properties, methods and constants (eb8947d) - #935
  • Prevent spreading ErrorType that causes excessive rule errors (8c9e127) - #925
  • Variable assigned in a for loop condition definitely exists in the for loop body (f9a5ac4) - #894
  • Fixed openssl_pkcs12_export - fifth parameter is optional (2ff582b) - #820, thanks @akondas!
  • Scope knows about global variables (d062a23) - #823
  • Fixed negated is_numeric() behaviour - cannot eliminate string (b2cbc04)
  • lookForAssigns for scope after while loop takes truthiness of while condition into account (05eee5a)
  • Fixed detecting variadic method through func_get_args() from a trait (01035f3) - #37
  • TypeCombinator - support removing array and Traversable from iterable (16ad9e5) - #992
  • DOMNodeList and SimpleXMLElement are offset accessible even if they don't implement ArrayAccess (8af60f4)
  • UnionType::hasMethod, hasProperty, hasConstant - filter all incompatible types (555a33c)
  • With strict_types, string parameter does not accept object with __toString() (c379a16)
  • Fixed anonymous class name in trait (7ce6d92)
  • NullType is offset accessible (dbaec2e) - #1051
  • Fixed handling analysis with early termination in loops (ec9c04d, c32cc9c)
  • Registry: fixed cache key (21f24fc) - #1074, thanks @pepakriz!
  • StaticVar will always be mixed (f6f601f) - #930
  • COMPILER_HALT_OFFSET undefined const false detection (ebce59a) - #353, thanks @avant1!
  • Allow removing scalars from unions (2d62b9b)

BC breaks

BC breaks for extension developers

  • Many changes on the Type interface thanks to cleanup, see #815, thanks @JanTvrdik!
  • Removed TrueBooleanType and FalseBooleanType in favour of ConstantBooleanType (#828), thanks @JanTvrdik!
  • You should make use of constant types - instead of checking for Scalar nodes, check for types from the PHPStan\Type\Constant namespace!
  • MethodReflection::getPrototype() can return ClassMemberReflection, not full MethodReflection (8dd51d7)
  • Removed ParameterReflection::isPassedByReference() in favour of passedByReference() with PassedByReference object as typehint (80d731d)
  • Type::describe() has VerbosityLevel parameter (7d10e4f)
  • Type::accepts() now has $strict parameter (c379a16)
  • Type::accepts() returns TrinaryLogic instead of boolean (7739f0e)
  • Removed TypeCombinator::isUnionTypesEnabled(), use RuleLevelHelper instead (bcef526)
  • MethodReflection and FunctionReflection do not longer implement ParametersAcceptor, instead they have getVariants() method. Use ParametersAcceptorSelector if you want to select the correct variant! (abb0eda)