Mago 1.16.0
Mago 1.16.0 is a significant release focused on analyzer accuracy. This release fixes over 20 false positives across loop analysis, type narrowing, integer range tracking, switch fall-through handling, and comparison assertions. It also adds new features including duplicate enum value detection, argument validation hooks for setcookie, session_set_save_handler, and session_set_cookie_params, and improved return type inference for min, max, abs, and array_reverse.
✨ Features
Analyzer
- Detect duplicate enum values: The analyzer now reports an error when a backed enum has multiple cases with the same value (#1475, #1478)
- Argument validation for
setcookie/setrawcookie: When the 3rd argument is an array (options form), additional positional arguments are now flagged as errors (#1467, #1492) - Argument validation for
session_set_save_handler: Validates both the object form (1-2 args) and callable form (6-9 args), reporting errors for mismatched argument counts (#1468) - Argument validation for
session_set_cookie_params: When the 1st argument is an array, extra arguments are now flagged as errors (#1468) - Improved
min/max/absreturn types: Multi-argumentminandmaxcalls now return precise integer range types, and a newabsprovider returns non-negative ranges (#1477, #1480)
🐛 Bug Fixes
Analyzer
- Fixed false positives in loop analysis: Resolved multiple issues where the analyzer incorrectly flagged conditions inside loops as impossible, including accumulative operations like
$total += $delta(#1491, #1493, #1499) - Fixed condition side effects lost in if-statements: Post-increment expressions like
$n++inside if-conditions had their side effects discarded when the if-body always exited, causing false "impossible condition" reports (#1504) - Fixed non-comparison loop conditions: Assignment-based loop conditions like
while ($row = func())were incorrectly treated as always-true, causing variables modified inside the loop to lose their pre-loop values (#1505) - Fixed switch fall-through type tracking: Variables modified in fall-through switch cases now correctly preserve types from both direct-entry and fall-through paths (#1490)
- Fixed switch cases after default treated as unreachable: Cases placed after the
defaultclause are no longer incorrectly flagged (#1484, #1485) - Fixed integer range narrowing in comparisons: Range bounds from non-literal types (e.g.,
non-negative-int) on the secondary variable in less-than comparisons no longer produce incorrect type narrowing when negated (#1503) - Fixed
count() === Nnot narrowing empty arrays: Empty arrays without generic parameters are now correctly removed when reconciling exact count assertions (#1506) - Fixed loose equality narrowing to
never: Loose equality (==) with strings no longer incorrectly narrows numeric types tonever(#1488) - Fixed closure parameter null stripping: Inferred null types on closure parameters are no longer incorrectly removed when no explicit type hint is present (#1489)
- Fixed
instanceof self/instanceof static: These expressions are now properly resolved for type computation (#1464) - Fixed increment/decrement on
neverproducingmixed: Operations onnevertypes now correctly propagateneverinstead of falling back tomixed(#1502) - Fixed
numericin string concatenation: Thenumerictype is now accepted in string concatenation operations (#1500) - Fixed integer range precision in loops: Bitwise and shift operations inside loops no longer unnecessarily widen integer ranges (#1499)
- Fixed
issetreturn type:isset()now returnstrueinstead ofboolwhen all checked values are definitely set, and array keys are marked as definite after loops that always enter (#1486, #1493) - Fixed loop integer widening: Variables with integer ranges that grow uniformly across loop iterations are now properly widened to prevent false impossible-condition reports (#1493)
Formatter
- Fixed stdin formatting memory issue: The source database is no longer populated when formatting from stdin (#1483)
- Fixed conditional expression comment placement: Comments in conditional (ternary) expressions are now placed correctly (#1465)
Prelude
- Fixed
array_reversereturn type: Non-list arrays (e.g.,array<string, int>) now correctly preserve their key type instead of being narrowed tolist<V>(#1466) - Fixed
non-positive-inttype mapping: Was incorrectly mapped topositive-intdue to a copy-paste error (#1479) - Removed false
@deprecatedonmb_scrub: The function is not deprecated in PHP (#1476, #1481) - Fixed
getrusagefunction signature: Corrected the return type to match PHP documentation (#1501) - Fixed
iconvmode parameter: Corrected the mode parameter type toint<0, 3>(#1497) - Fixed
openssl_pkey_get_detailsreturn type: Made all keys optional since algorithm-specific keys are only present for their key type - Consolidated
session_set_save_handlerstub: Merged duplicate declarations into a single signature with union types (#1468) - Consolidated
session_set_cookie_paramsstub: Merged duplicate declarations into a single signature (#1468)
Codex
- Improved integer bitwise operations: Bitwise AND with a non-negative mask now correctly produces a bounded non-negative result (#1499)
📖 Documentation
- Added Run On Save as alternative formatter: Documented the Run On Save VS Code extension as an alternative to format-on-save (#1469)
- Removed outdated Docker limitations: The limitations section was removed from the Docker recipe page as the issues have been resolved (#1438)
🏗️ Internal
- Updated dependencies
- Fixed clippy warnings
- Updated sponsors list
🙏 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 - #1484, #1485, #1486, #1488, #1490, #1491, #1493, #1497, #1498, #1499, #1500, #1501, #1502, #1503, #1504, #1505, #1506, #1507
- @zip-fa - #1489
- @KorvinSzanto - #1477
- @giorgiopogliani - #1474, #1475
- @karoun - #1470
- @mytskine - #1466, #1467, #1468
- @dotdash - #1464
Full Changelog: 1.15.3...1.16.0