github phan/phan 6.0.0
Phan 6.0.0

8 hours ago

Phan NEWS

Jan 16 2026, Phan 6.0.0

Bug fixes:

  • Fixed crash when analyzing implicit float-to-int conversion in modulo operator (#5392)
  • Fixed PhanUnreferencedClosure being emitted too aggressively for closure function parameters (#5389)
  • Fixed false positives for readonly properties in traits when targeting PHP < 8.2 (#5390)
  • Fixed detection of property usages from trait methods (#5391)
  • Fixed match expression type narrowing when multiple conditional expressions are used (#5398)
  • Fixed PhanTypeMismatchArgumentSuperType false positives on closure parameters (#5402)
  • Fixed array shape narrowing with count() - only narrow closed array shapes with optional keys (#5406)
  • Improved distinction between list-like array shapes and general array shapes (#5407)
  • Fixed "Possibly invalid offset" only appearing for some accesses in a foreach loop (#5409)
  • Fixed IS_PROMOTED_PROPERTY flag detection for constructor property promotion (#5411, #5416)
  • Fixed non-exhaustive match detection with literal bool edge cases (#5388)

Improvements:

  • Performance optimizations for template type substitution and FQSEN caching (#5417, #5418)
    • Early-exit checks in Method::cloneWithTemplateParameterTypeMap to avoid unnecessary cloning
    • Optimized CallableParamPlugin for functions with no parameters
    • Two-level cache for FullyQualifiedClassElement::make using object IDs
  • Allow Symfony 8.0 compatibility (#5401)
  • Fixed composer-patches package location for PHP 8.5 compatibility (#5387)

Nov 20 2025, Phan 6.0.0-beta

Breaking changes:

  • Requires PHP 8.1+ to run and minimum target version is now PHP 8.1
  • Requires php-ast 1.1.3+ for PHP 8.4+ analysis (AST version 110/120 support)
  • The -i CLI option is now an alias of --incremental instead of --ignore-undeclared
  • Renamed the PhanPluginCanUsePHP71Void issue to PhanPluginCanUseVoidReturnType
  • Dropped the allow_method_param_type_widening config option (contravariance is now always allowed)
  • Dropped the backward_compatibility_checks config option, along with its respective --backward-compatibility-checks and -b CLI flags

New features (Analysis):

  • Large literal arrays are now summarized to representative samples during parsing, significantly reducing peak memory when analyzing massive datasets.
    • Default limits can be tuned via the new config options ast_trim_max_elements_per_level (default 256) and ast_trim_max_total_elements (default 512).
  • Union types are automatically clamped once they exceed max_union_type_set_size (default 1024), preventing runaway growth from deeply nested array merges while keeping type inference useful.
  • These trim/clamp thresholds are also exposed as CLI flags (--ast-trim-max-elements-per-level, --ast-trim-max-total-elements, --max-union-type-set-size) for easy experimentation without editing config files.
  • Detect implicit float-to-int conversion in modulo operator (deprecated in PHP 8.1)
    • New issue type: PhanTypeInvalidModuloOperand
    • Warns when float types are used with the modulo (%) operator
  • Improved analysis of intersection types with unknown classes (#4431)
    • When an intersection type includes both known and unknown classes, Phan now analyzes the known types
    • Method calls and property access are checked against known classes in the intersection
    • Enables type checking even when some dependencies are missing or stubs are incomplete
    • Also improves analysis of catch blocks with mixed known/unknown exception types
  • Full support for PHP 8.5 features:
    • #[NoDiscard] attribute support with detection of ignored return values (PhanNoDiscardReturnValueIgnored)
    • #[Override] attribute extended to properties (in addition to methods)
    • Pipe operator (|>) with full type inference through piped call chains
    • (void) cast support for suppressing NoDiscard warnings
    • Updated function signatures for PHP 8.5 standard library changes
  • Full support for PHP 8.4 property hooks
    • Parse and validate property hook syntax (get/set hooks)
    • Parameter type checking for set hook parameters
    • Validation of hooks with default values (PhanPropertyHookWithDefaultValue)
    • Validation of readonly properties with set hooks (PhanReadonlyPropertyHasSetHook)
    • Return type compatibility validation (PhanPropertyHookIncompatibleReturnType)
    • Parameter type compatibility validation (PhanPropertyHookIncompatibleParamType)
    • Final hook override detection (PhanPropertyHookFinalOverride)
  • Support for PHP 8.4 #[Deprecated] attribute
    • Recognize #[Deprecated] attribute on functions, methods, and class constants
    • Works alongside existing @deprecated PHPDoc comment support
    • Emits PhanDeprecatedFunction and PhanDeprecatedClassConstant warnings when deprecated elements are used
  • Support for new PHP 8.4 functions and methods
    • DateTime::createFromTimestamp() / DateTimeImmutable::createFromTimestamp()
    • DOMXPath::registerPHPFunctionNS()
    • All new PHP 8.4 functions: array_find, array_find_key, array_any, array_all, mb_trim, mb_ltrim, mb_rtrim, mb_ucfirst, mb_lcfirst, and more
  • Full support for "new without parentheses" syntax (e.g., new MyClass()->method())
    • Type inference works correctly across method chains
    • Property and array access supported
  • Multiline doc comments for @param, @var, and @return
    • Doc comment parsing now normalizes multi-line @param, @var, and @return
      annotations before analysis, allowing nested array/generic syntax to be laid out
      across several lines without being misinterpreted.
    • Extended multiline support to Phan-specific annotations: @phan-param, @phan-var, @phan-return, @phan-real-return, @phan-property*, @phan-assert, @phan-type, @phan-implements, @phan-extends, @phan-inherits, @phan-use (#4252)
    • PHPDocRedundantPlugin now flags redundant @var annotations on typed
      properties, matching its existing coverage for functions and methods.
  • PreferNamespaceUsePlugin now handles union types
  • Improved generics support (#5182)
    • Enhanced template type handling and generic type inference
    • Better resolution of generic types in complex scenarios
  • Full support for PHP 8.3 typed class constants (#5140, #5128)
    • Inheritance checking and validation
    • New issue types: PhanTypeMismatchDeclaredConstant, PhanTypeMismatchDeclaredConstantNever, PhanConstantTypeMismatchInheritance
    • Proper type narrowing for class constants in conditions (#5127)
  • Literal type exclusion in !in_array() checks (#5185)
    • Type narrowing when checking values against literal arrays (up to 50 elements)
    • Improves precision when checking against known sets of values
  • Reference assignment literal type erasure (#5197, #4354)
    • Variables involved in reference assignments ($var2 =& $var1) now have their literal types erased
    • Prevents incorrect literal type tracking when variables are aliased
    • Applies to both sides of reference assignment and persists across subsequent assignments
  • Enhanced type inference improvements:
    • array_filter() recognizes null-stripping callbacks and keeps element types non-null (#5100)
    • array_chunk() return type inference based on preserve_keys parameter (#5165)
    • constant() return type inference from Phan's constant table for non-dynamic constants (#5157)
    • stdClass property inference from array shape casts (#5151)
    • Better static return type resolution preserving intersection types and generics (#5126)
    • Improved foreach iterator type inference honoring explicit iterator generics (#5108)
    • Conditional type refinement after array field checks (#5179)
    • Static properties in conditional expressions (#5194)
    • Type conditions with intermediary variables (#5125)
    • Enum property access in constant expressions (#5158): support for self::CASE->value
  • New detection capabilities:
    • UncoveredEnumCasesInMatchPlugin (#5164): Detects when match expressions with enum conditions don't cover all enum cases
    • Duplicate static variable detection (#5176): New PhanDuplicateStaticVariable issue (fatal error in PHP 8.3+)
    • Multiple readonly property assignment (#5175): New PhanAccessReadOnlyPropertyMultipleTimes issue
    • Redundant boolean type combinations (#5174): Warns on redundant type combinations
    • Trait constant compatibility (#5119): New PhanIncompatibleCompositionConstant for conflicting trait constants
    • Interface traits and readonly classes (#5118): Warns when interfaces use traits or readonly classes use non-readonly trait properties
    • Improved unused variable detection (#5093): Catches more cases with compound assignment operators (+=, -=, etc.)
    • Redundant property comments (#5107): Warns on redundant @var docblocks on typed properties

New features (CLI):

  • Git-style config discovery (#5092):
    • Searches parent directories for .phan/config.php
    • Filters output when running from subdirectories
    • New --subdirectory-only flag for analyzing specific modules with better performance
  • -n/--no-config-file implicitly limits analysis to just the files provided
    on the command line (e.g. phan -n test1.php test2.php) avoiding analysis of
    the rest of the project when you only want quick ad-hoc checks.
  • Added support for incremental analysis, where only changed files and their dependents
    will be analyzed on subsequent runs. Significantly speeds up re-analysis.
    • Add the --incremental / -i option that enables incremental analysis.
    • Add the --force-full-analysis flag to force a full re-analysis of all files, ignoring the incremental analysis manifest.
    • Add the --no-incremental / -N option that disables incremental analysis (useful if it was enabled in config.php).

Performance improvements:

  • phan_helpers C extension integration (#5096):
    • Optional C extension for 2-3x faster AST hashing and type deduplication
    • Overall analysis speedup: 5-15% for large projects
    • Automatically detected and used when available
  • Internal PHP stub definitions are now cached between CodeBase instances, so repeated test runs and daemon/server reloads reuse the previously parsed classes/functions without reparsing dozens of stub files on every initialization.
  • Conditional visitor optimization (#5186):
    • Skip visitor creation for ~60-70% of if statements (those without else/elseif)
    • Reduces memory usage and improves analysis speed
  • Incremental analysis support (see CLI features above)
  • Subdirectory-only mode for faster focused analysis

New features (Tools):

  • Add tool/add_suppressions.php to assist in adding @suppress or @phan-suppress annotations to files
    • Useful for batch suppressing specific issue types across the codebase
    • See tool/SuppressionsToolImplementation.md for technical details

Bug fixes:

Type Inference & Analysis:

  • Fixed too strict type inference after !empty() condition on arrays (#5295)
  • Fixed union types containing both known objects and mixed being checked too strictly (#5292)
  • Fixed PhanAccessReadOnlyPropertyMultipleTimes failing to account for mutually exclusive branches (#5285)
  • Fixed declare blocks causing Phan to lose track of declared variables (#5284)
  • Fixed conditionals causing wrong type inference for static properties (#5283)
  • Fixed too strict PhanTemplateTypeConstraintViolation when type can't be resolved (#5282)
  • Fixed Phan using unrelated array elements when inferring types (#5281)
  • Fixed array shape type inference for is_object and !is_array checks with mixed types (#5277)
  • Fixed static property type narrowing with type-check functions like is_null() and !is_null() (#5277)
  • Fixed type accumulation for interface-typed properties ensuring method calls are validated against interface contract (#5300)
  • Fixed spurious PhanPossiblyUndeclaredVariable with function calls and following usage (#5269)
  • Fixed PhanTypeMismatchGeneratorYieldKey false positive when yield has no key (#5258)
  • Fixed PhanAccessOwnConstructor incorrectly warning on valid usage of self::__construct (#5257)
  • Fixed unnecessary array key aliasing in type narrowing when assignment happens in foreach iteration (#5255)
  • Fixed array unions containing mixed types being checked too strictly for array offsets (#5299)
  • Fixed condition-based type narrowing using outdated context when variable is modified in conditionals (#5301)
  • Fixed isset() and empty() warning on undefined properties (now only warns on direct access and unset) (#5273)
  • Fixed property type narrowing scope leaking into unrelated code paths (#5249)
  • Fixed ArrayAccess type checking not validating offsetGet signature (#5251)
  • Fixed DNF type false positive when using null defaults (#5252)
  • Fixed array_combine signature being too strict for null values (#5219)
  • Improved type inference for get_class() to return more specific class-string types (#5274)
  • Improved handling of getIterator() returning Iterator with explicit generics (#5108)
  • Traits can now declare @require-extends / @require-implements (and their Psalm aliases) to ensure consuming classes satisfy those requirements
  • Fixed PhanPossiblyInfiniteRecursionSameParams false positive for instance methods that guard recursion with property state (#5371)
  • Fixed PhanTypeMismatchDeclaredConstant false positives for enum cases whose values are strings/ints (#5378)
  • Improved parameter and property default type representation in error messages (#5271)

Tooling:

  • Baselines generated with --save-baseline now store suppressions per symbol (class/method/function) instead of line numbers and normalize anonymous class/closure names, so suppressions remain stable when code moves or hashes change (#5381/#5383)

Plugins:

  • Fixed UnusedSuppressionPlugin incorrectly flagging magic method suppressions in class doc comments as unused (#5304)
  • Added missing ANSI color mappings for terminal output (#5294)
  • Improved confusing PhanSuspiciousValueComparison message when all literal possibilities exhausted (#5276)
  • Fixed issue message for PhanCommentAbstractOnInheritance (#5265)

Other Fixes:

  • Fixed nested closure scope tracking bug (#5291)
  • Fixed multiline PHPDoc comment parsing issues (#5293)
  • Fixed private property static/non-static mismatch detection in traits (#5247)
  • Fixed internal stubs not being included in releases (#5244)
  • Fixed nullsafe property access: No more strict object checking false positives with ?-> (#5112)
  • Fixed trait method multi-level inheritance: Track trait methods through multiple inheritance levels (#5120)
  • Fixed DNS aliases: Corrected reversed DNS aliases (#5170)
  • Fixed private final constructor: Don't warn on private final constructors (exempted in PHP 8.0+) (#5169)
  • Fixed float-to-int in modulo: Detect implicit float-to-int conversion in % operator (#5189)
  • Fixed sibling type compatibility: Assignment visitor type checking now works correctly (#5168)
  • Fixed dynamic array offset constants: Skip premature constant resolution for define() (#5156)
  • Fixed unary operator type aggregation: Corrected numeric fallback ordering bug (#5146)
  • Fixed xml_parser_create signature: Corrected incorrect signature (#5139)
  • Fixed crash with intersection types: No more EmptyFQSENException with --analyze-twice (#5086)
  • Fixed class constant regression: Corrected typed constant false positives after #5128 (#5133)
  • Fixed intersection types with unknown classes: Proper method resolution when intersection contains both known and unknown classes (#5190)
  • Fixed template type compatibility: No more false positive PhanTypeMismatchDeclaredReturn with template types (#5181)
  • Fixed readonly property access: No more false positives with isset() on readonly properties in @phan-side-effect-free classes (#5180)
  • Fixed switch fall-through variable tracking: Variable definitions now flow correctly through fall-through cases (#5155)
  • Fixed try/catch variable scope: Variable definitions in try blocks with finally clauses now track correctly (#5152, #5131)
  • Fixed global variable type pollution in daemon mode: Incremental re-analysis no longer pollutes global variable types (#5183)
  • Fixed ternary in function arguments: Proper type narrowing for conditional expressions in arguments (#5166)
  • Fixed empty array vs non-empty union: Re-checks union arguments mixing empty and non-empty arrays (#5115)
  • Fixed array map callback analysis: No more false positive PhanParamTooFewUnpack with array_map (#5114)
  • Fixed static call on trait properties: No more false positives after instanceof checks (#5111)
  • Fixed never return type inheritance: Proper detection of inherited never methods via parent::/static:: (#5109)
  • Fixed nested array shape field refinement: Suppressed property mismatch warnings when refining nested fields (#5106)
  • Fixed enums as class constants: Allow enum types as class constant values (#5103)
  • Fixed SID constant handling: Special-case handling for dynamically defined SID constant (#5150)
  • Fixed reference assignment literal type erasure: Variables involved in reference assignments now correctly erase literal types to prevent false positives (#5197)

PHPDoc & Attributes:

  • Fixed @deprecated detection for callable parameters: Emit PhanDeprecatedFunction when deprecated functions are passed as callable string arguments (#4858)
  • Fixed @phan-suppress on class constants: Suppression annotations are no longer ignored (#5188)
  • Fixed @phan-mandatory-param inheritance: Inherit annotation from interface methods (#5116)
  • Fixed @phan-pure inheritance: Exclude __call and __callStatic from automatic inheritance (#5124)
  • Fixed internal method purity: ArrayObject->count() now inherits pure flag from Countable (#5122)
  • Fixed PHPDoc type inheritance: Proper inheritance of parameter types from interfaces (#5117)

Plugin Fixes:

  • Fixed MoreSpecificElementTypePlugin: No more false positives with array{}|non-empty-array<K,V> (#5187)
  • Fixed UnknownElementTypePlugin: No more false positives on inherited methods (#5177)
  • Fixed RedundantConditionVisitor: No more false positives with static property empty() checks (#5148)
  • Fixed CompactPlugin: Added check for possibly undefined variables (#5167)
  • Fixed PHPDocRedundantPlugin: Added auto-fixer for redundant property comments (#5178)

Loop & Control Flow:

  • Fixed possibly infinite loop detection: No more false positives when loop condition uses array count (#5153)
  • Fixed !isset() variable tracking: Possibly-undefined flag preservation now works correctly (#5172)
  • Fixed redundant condition after empty(): Proper static property handling (#5148)

AST Compatibility:

  • Fixed AST version 110/120 compatibility for PHP 8.4
    • Updated TolerantASTConverter to support AST version 120
    • Fixed AST structural changes: closure 'name' field removal, parameter 'hooks' field addition, property 'hooks' field addition
    • Fixed clone being incorrectly treated as a function call in AST version 110+ (it's now AST_CALL instead of AST_CLONE)

Don't miss a new phan release

NewReleases is sending notifications on new releases.