Mago 1.17.0
Mago 1.17.0 continues the focus on analyzer accuracy with 20+ bug fixes and several new features. Highlights include detection of array access on non-array types (false, scalars), support for PHP 8.4's #[\Deprecated] attribute, literal float arithmetic tracking, a new @suspends-fiber annotation for async code, and invalidation of memoized $this properties on self-method calls. The linter gains new rules for fully-qualified global imports and ambiguous constant access, plus an auto-fixer for redundant parentheses.
✨ Features
Analyzer
- Detect array access on non-array types: New
false-array-access,possibly-false-array-access,possibly-invalid-array-access, andinvalid-array-accessissue codes report when array access is performed onfalse, scalars, or other non-array types, including assignment contexts (#1542) - Support
#[\Deprecated]PHP attribute: Functions, methods, classes, constants, and enums with the#[\Deprecated]attribute are now recognized as deprecated (#1541) @suspends-fiberannotation: A new docblock tag@suspends-fibermarks methods that suspend fibers. Calls to these methods invalidate all memoized$thisproperty narrowings, preventing false positives in async code.Revolt\EventLoop\Suspension::suspend()is recognized automatically (#1536)- Invalidate memoized properties on self-method calls: Non-pure
$this->method()calls now clear memoized$this->property types, fixing false positives where property narrowings survived across method calls that could modify them (#1536) - Extend min/max/abs providers to Psl\Math functions:
Psl\Math\max,Psl\Math\maxva,Psl\Math\min,Psl\Math\minva, andPsl\Math\absnow use the same precise integer range providers as the built-in functions - Check reference constraints on array modifications: Array append (
$arr[] = $val) and indexed assignment ($arr[$k] = $val) on by-reference parameters now validate against the parameter's type constraint (#1539)
Linter
no-fully-qualified-global-function,no-fully-qualified-global-constant,no-fully-qualified-global-classrules: New rules that flag fully-qualified references (#1494)ambiguous-constant-accessrule: Detects ambiguous constant access patterns (#1508)- Auto-fixer for redundant parentheses: The
redundant-parenthesesrule now has an auto-fixer (#1549)
🐛 Bug Fixes
Analyzer
- Fixed false positives from truthy/falsy-mixed absorbing literals: The type combiner no longer absorbs falsy literal strings like
string('')intotruthy-mixed, which caused false redundant-condition warnings (#1534) - Fixed false positive for
isseton string offsets:isset($s[0])on a genericstringno longer returnstrueunconditionally - empty strings have no characters, so the result is correctlybool(#1537) - Fixed
parse_urlcomponent return types:parse_url()with a component argument now correctly includesfalsein the return type, since malformed URLs returnfalseregardless of the component (#1546) - Fixed empty array variants lost in loops with break: Infinite loops with
breakno longer drop the pre-loop empty array variant, fixing false positives wherecount($arr)was always truthy after awhile(true)loop (#1535) - Fixed empty array variants not removed after isset check: When
if (!isset($arr[$key])) { throw; }exits, empty array variants are now removed from the base variable since isset proves the array is non-empty - Fixed switch fall-through after never-returning call: Cases after a
never-returning call (likeexit()) withoutbreakare no longer incorrectly marked as unreachable - the switch can still jump directly to those cases (#1531) - Fixed if-body clauses leaking to post-if context: Clauses from non-exiting if-bodies (e.g.,
if ($m === 0) { echo; }) no longer leak to subsequent code, preventing incorrect type narrowing (#1509) - Fixed by-reference argument types not propagated through
&&: Variables passed by reference in function calls on the left side of&&now correctly update their type for the right side evaluation (#1524) - Fixed dynamic key array assignment overwriting known items:
$arr[$dynamic] = valueno longer replaces the value types of existing known keys in the array (#1527) - Fixed
isset/emptychecks on union types with lists: The impossible-isset check now considersTArray::Listvariants in union types, and non-empty arrays with literal key types no longer produce false possibly-undefined warnings (#1512) - Fixed nested array keys not marked definite after loops: The post-loop key-definiteness fix now recurses into nested arrays (#1522)
- Fixed integer type unions in min/max/abs providers: Multi-integer unions like
int|int(0)are now properly handled by computing combined bounds (#1526) - Fixed literal float arithmetic: Operations between literal ints and floats (e.g.,
1000 * 0.5) now compute the actual result (float(500.0)) instead of returning unspecifiedfloat(#1540) - Fixed non-empty array with literal key type access: Accessing a non-empty generic array with its exact literal key type no longer produces possibly-undefined warnings (#1512)
Formatter
- Fixed
fits()handling of LineSuffix: Corrected line-suffix width calculation in the formatter (#1516)
Prelude
- Fixed
getimagesize/getimagesizefromstringparam-out: Added@param-out ?arrayannotation matching the nullable parameter type (#1523)
Codex
- Fixed truthy/falsy-mixed containment:
truthy-mixedno longer contains falsy values andfalsy-mixedno longer contains truthy values in type comparisons (#1534) - Recognized
#[\Deprecated]attribute: The codex scanner now sets the deprecated flag from PHP attributes in addition to@deprecateddocblock tags (#1541)
Composer
- Fixed race condition in binary download: Concurrent Composer installations no longer corrupt the binary (#1544)
- Formatted internal binary download code: Cleaned up the Composer wrapper's download logic (#1545)
🏗️ Internal
- Ignored analyzer test files from typos check
🙏 Thank You
Contributors
A huge thank you to everyone who contributed code to this release:
Issue Reporters
Thank you to everyone who reported issues that shaped this release:
- @UweOhse - #1512, #1522, #1523, #1524, #1526, #1527, #1528, #1531, #1534, #1535, #1536, #1537, #1539, #1540, #1541, #1542, #1546
- @regnerisch - #1518
- @laylatichy - #1525
Full Changelog: 1.16.0...1.17.0