Skip to content

Releases: carthage-software/mago

Mago 1.0.0-rc.5

25 Nov 02:11
09b1f6e

Choose a tag to compare

Bug Fixes

Syntax

  • Fixed parsing of integers prefixed with 0 to correctly interpret them as octal numbers

Analyzer

  • Fixed type narrowing for in_array and Psl\Iter\contains (#658)
  • Fixed assertion scrapping for in_array and Psl\Iter\contains
  • Fixed int/float disjointness handling in conditional type narrowing (#656)

Codex

  • Fixed child native return types to take precedence over parent docblock types (#657)

Improvements

PHP 8.5 Support

  • Semicolon as a switch case separator is now deprecated for PHP >= 8.5

CLI

  • Removed deprecated --no-color flag (use --colors never instead)

Internal

  • Refactored codex populator into modular subcomponents

Full Changelog: 1.0.0-rc.4...1.0.0-rc.5

Mago 1.0.0-rc.4

24 Nov 00:50

Choose a tag to compare

Features

Partial Function Application (RFC)

Mago now fully supports PHP 8.6's Partial Function Application RFC, enabling first-class callable creation using placeholder syntax. You can use positional (?), named (name: ?), and variadic (...) placeholders to create closures from existing functions and methods, with full type inference and validation to ensure type safety across your partial applications.

PHP Version Support

  • Added support for PHP 8.5, which was released just days ago
  • Switched minimum supported PHP version from 7.4 to 8.0

Looking Ahead to PHP 8.6

This release demonstrates Mago's unique capability to stay ahead of the PHP ecosystem. While PHP 8.6 won't be released for another year and the Partial Function Application RFC hasn't even been merged into PHP yet, Mago already provides full support for this feature. By controlling our entire stack, from parser to analyzer, we can implement and validate upcoming PHP features much faster than traditional tools, giving developers early access to write future-proof code with confidence.

Linter

  • New no-redundant-yield-from rule to identify unnecessary yield from expressions that can be simplified

Bug Fixes

Analyzer

  • Fixed incorrect parameter type mapping when using named placeholders that reorder parameters in partial function application
  • Corrected false positives when real methods are documented with @method annotations in child classes for return type refinement
  • Fixed variable invalidation logic to properly invalidate descendant variables when passed by reference
  • Resolved trait constant access issues when using self, static, and $this keywords
  • Suppressed redundant unused statement issues in test contexts
  • Disabled reporting of #[Override] attribute issues before PHP 8.3
  • Corrected impossible condition detection when comparing numeric and array-key types
  • Eliminated invalid and redundant auto-fix suggestions
  • Fixed array_merge() result computation when using empty arrays as arguments

Codex

  • Improved docblock inheritance logic to respect explicit return types in child classes instead of inheriting parent types
  • Enhanced overall docblock inheritance handling with better resolution rules

Database

  • Corrected path normalization for Windows systems during test execution
  • Improved pattern specificity resolution for path and include conflicts
  • Fixed file logical name usage when updating files during watch mode

Linter

  • Prevented false positives in no-redundant-math rule for float division operations
  • Disabled sensitive-parameter rule for PHP versions below 8.2
  • Fixed incorrect static closure suggestions when nested closures reference $this

Formatter

  • Preserved parentheses around constant access expressions in instanceof binary operations

Docblock

  • Added support for multiple spaces after colons in callable type annotations

Collector

  • Disabled reporting of unfulfilled expectations during incremental analysis to reduce noise

Installation

  • Fixed installation script incorrectly exiting successfully when the download server is unavailable

Improvements

Syntax

  • Tagged Expression, Statement, and Node enums as #[non_exhaustive] for future compatibility

Full Changelog: 1.0.0-rc.3...1.0.0-rc.4

Mago 1.0.0-rc.3

16 Nov 00:04
ed0c848

Choose a tag to compare

Mago 1.0.0-rc.3

🤩 Features

💻 CLI

  • --retain-code option: Filter reported issues by specific codes (e.g., --retain-code semantics --retain-code invalid-argument). Unlike --only in lint, this runs all checks but filters the
    output

✨ Formatter

  • Method sorting: Add option to sort class methods based on name, visibility, and other modifiers

🐛 Bug Fixes

🎯 Type System

  • Variance-aware inheritdoc: Respect contravariance for parameters (allow child to widen types) and covariance for return types (allow child to narrow types) when inheriting docblock types (closes
    #629)
  • Better type narrowing: Narrow mixed to array types after isset() checks on nested array access
  • Nullsafe operator: Propagate nullsafe operator short-circuit through entire expression chains
  • Promoted properties: Parse inline @var docblocks on promoted constructor properties
  • Trait self types: Support self parameter types in closures defined in trait methods
  • Array operations: Preserve non-empty flag in array addition and array_merge() type inference
  • Type expansion: Correct type expansion logic for specialized type parameters

🔧 Linter

  • str-contains rule: Add support for negation pattern (strpos === false!str_contains). Only provide fixes for strict equality (=== and !==) to prevent unsafe replacements. Report warnings for loose equality (==, !=, <>) due to strpos returning 0 at position 0 (closes #494)

📚 Prelude

  • mbstring functions : Correct return types for encoding functions (closes #628)

🎨 Improvements

📁 Database

  • Path handling: Prioritize includes over paths, and add support for path and include patterns (closes #327)

Full Changelog: 1.0.0-rc.2...1.0.0-rc.3

Mago 1.0.0-rc.2

14 Nov 05:45
4b30882

Choose a tag to compare

Bug Fixes

  • Type alias imports: Fixed false positive "class not found" errors when importing type aliases from classes that haven't been populated yet #624
  • Self-imported type aliases: Fixed stack overflow when a class imports type aliases from itself - now skips self-imports with a helpful message #624
  • Empty containers: Fixed false positive error when passing new ArrayIterator([]) or similar empty containers to typed iterable parameters #626

Full Changelog: 1.0.0-rc.1...1.0.0-rc.2

Mago 1.0.0-rc.1

12 Nov 08:00
d6a6097

Choose a tag to compare

Mago 1.0.0-rc.1 🎉

We're excited to announce Mago 1.0.0-rc.1, a major milestone toward the 1.0.0 release! This release candidate includes significant performance improvements, comprehensive type system enhancements, and numerous bug fixes based on community feedback.

📊 Release Highlights

  • 🚀 Watch Mode with Incremental Analysis - Real-time analysis with intelligent re-analysis of only changed files
  • 📝 Full Type Alias Support - Complete implementation of @type, @psalm-type, and @phpstan-type
  • ✅ Comprehensive Inheritance Validation - 20+ new issue codes for class hierarchy validation
  • 📤 SARIF Format Support - Better integration with security and analysis tools
  • ⚙️ Configuration Enhancements - JSON schema support, YAML/JSON configuration files
  • 💰 67 GitHub issues closed in this release
  • 🤝 12 merged pull requests from community contributors

🎉 New Features

🧬 Type System Enhancements

Full Type Alias Support

Mago now has complete support for type aliases in docblocks:

/**
 * @type UserId = positive-int
 * @psalm-type UserData = array{id: UserId, name: string}
 */
class User {
    /** @param UserData $data */
    public function __construct(private array $data) {}
}

/**
 * @import-type UserData from User
 */
class UserService {
    /** @param !User::UserData $data */  // Reference without importing
    public function process(array $data): void {}
}

Features:

  • Define type aliases with @type, @psalm-type, and @phpstan-type tags
  • Import type aliases with @import-type (with as keyword support for renaming)
  • Reference without importing using !ClassName::AliasName syntax
  • Support for self and static references
  • Type aliases can reference other aliases, creating dependency chains

Index Access Type Support

Added support for T[K] (index access) type syntax for more precise array typing.

Literal-Int Type Support

Added support for literal-int type to distinguish between literal integer values (e.g., 42) and general integer types.

@inheritDoc Support

Full support for @inheritDoc tag in docblocks, including inline {@inheritDoc} syntax.

🔍 Static Analysis Improvements

Comprehensive Inheritance Validation

Mago now validates class inheritance, interface implementation, and trait composition following PHP's type system rules and the Liskov Substitution Principle.

Validates:

  • Property type compatibility (covariance/contravariance)
  • Method signature compatibility
  • Constant type and value compatibility
  • Visibility rules (cannot make members more restrictive)
  • Access modifiers (static, readonly, final)
  • Parameter counts and defaults

New Issue Codes (20+):

  • IncompatibleConstantAccess, IncompatibleConstantOverride, IncompatibleConstantType
  • IncompatibleParameterCount, IncompatibleParameterName, IncompatibleParameterType
  • IncompatiblePropertyAccess, IncompatiblePropertyDefault, IncompatiblePropertyOverride, IncompatiblePropertyType
  • IncompatibleReturnType, IncompatibleVisibility
  • MissingOverrideAttribute
  • And more...

Trait Constant Override Validation

Validates trait constant override rules - prevents classes from overriding trait constants with different values.

Auto-Fix Support for Redundancy Issues

The analyzer can now automatically fix various redundancy issues with mago analyze --fix --fmt.

Conditional Return Type for json_encode()

Special handler recognizes the JSON_THROW_ON_ERROR flag and returns string instead of string|false.

🎯 Special Function Handlers

array_merge and Psl\Dict\merge

Special type inference for array_merge and PSL's Psl\Dict\merge with proper key/value tracking.

array_filter with Callback Assertion

Better type narrowing when using array_filter with callback assertions like is_int(...).

compact() Handler

Preserves type information when using compact() - knows the exact keys and their types.

🧹 Linter Rules

New Rules

  1. no-self-assignment - Detects assignments where a variable is assigned to itself ($x = $x;)
  2. prefer-early-continue - Encourages early returns to reduce nesting
  3. prefer-static-closure - Performance optimization for closures that don't use $this

Previously Disabled Rules Now Enabled

The following rules are now enabled by default:

  • no-redundant-use - Detects unused imports
  • no-variable-variable - Warns about variable variables
  • no-redundant-readonly - Detects redundant readonly modifiers
  • sensitive-parameter - Validates sensitive parameter handling

📊 Reporting & Output

SARIF Format Support

Added support for SARIF (Static Analysis Results Interchange Format) for integration with GitHub Code Scanning, security dashboards, and CI/CD pipelines.

mago analyze --reporting-format sarif > results.sarif

Baseline Improvements

Major improvements to baseline management:

  • Cross-platform path normalization - Works correctly on Windows and Unix
  • Detailed statistics - Dead/removed issues, filtered counts, severity levels
  • Stricter baseline checking - New fail_on_out_of_sync_baseline parameter
  • Better API - Moved to mago-reporting crate for reusability

Enhanced Annotations for CI/CD

Annotation messages now included in GitHub, GitLab, and Checkstyle formats with column information and detailed context.

⚙️ Configuration

JSON Schema Support

Generate JSON schema for configuration validation with IDE autocomplete:

mago config --schema > mago-schema.json

YAML and JSON Configuration Support

Configuration files can now be written in YAML or JSON in addition to TOML.

Search Order: workspace directory → XDG_CONFIG_HOME → home directory
Format Priority: TOML > YAML > JSON

🖥️ CLI Enhancements

Shell Completions

Generate shell completions for Bash, Zsh, Fish, and PowerShell:

mago generate-completions bash > /etc/bash_completion.d/mago

Install Specific Versions

Installation script now supports version selection:

curl -sSL https://mago.sh/install | bash -s -- --version=1.0.0-rc.1

⚡ Performance

Watch Mode

New --watch flag for continuous analysis during development:

mago analyze --watch

Watches your filesystem and automatically re-analyzes only the files that changed and their dependencies. Powered by intelligent incremental analysis internally - dramatically faster than re-analyzing the entire codebase!

Type Pre-allocation Optimization

Re-use pre-allocated types for better memory usage and performance.

🔧 Breaking Changes

Configuration File Behavior Change

⚠️ BREAKING CHANGE: Configuration files are no longer merged from multiple locations.

What Changed:

  • Only ONE configuration file is loaded (first found in search order)
  • No merging between global and workspace configs

Migration: Consolidate your configuration into a single file if you previously relied on merging.

Previously Disabled Rules Now Enabled

Several linter rules are now enabled by default. You may see new warnings/errors for:

  • Unused imports
  • Variable variables
  • Redundant readonly modifiers
  • Sensitive parameter handling

Comprehensive Inheritance Validation

The new strict inheritance validation may report issues in class hierarchies that were previously undetected.

🐛 Bug Fixes

🧬 Type System & Analysis

  • Array key access in union types - Corrected error reporting
  • False positives - Fixed multiple issues to improve accuracy
  • Null coalesce type narrowing - Fixed when right-hand side is never type
  • isset() false positives - Fixed several issues with union array types and type checks after isset()
  • Nullability preservation - Preserve nullability when merging docblock types with real types

🔤 Syntax & Parser

  • Reserved keywords in string interpolation - Now accepts reserved keywords in array access ("$array[class]")
  • Static in constant expressions - Correctly disallows static in constant expressions (matches PHP behavior)

🎨 Formatter

  • String interpolation alignment - Preserve alignment for braced expressions
  • Echo tags recognition - No longer flagged as useless code
  • Empty single-line comments - Allow empty comments in blocks

🧹 Linter

  • --only flag - Fixed flag being ignored when linting
  • Only filter warning - Warn when filter doesn't match any rule
  • Unfulfilled expectations - Don't report on non-active rules

💥 Other Fixes

  • Extreme values - Prevent panics on extreme numeric values
  • Try statement return detection - Fixed detection when calling functions returning never

🏗️ Internal Changes

Orchestrator Crate

Major architectural refactoring introducing the orchestrator crate - separates CLI from core analysis engine.

Rust 1.91 Update

Updated to Rust 1.91 for latest language features and performance improvements.

SPL Stubs

Improved SPL (Standard PHP Library) stubs with better type definitions.

⚠️ Migration Notes

Upgrading from Previous Versions

  1. 📁 Configuration Files - Consolidate multiple configs into a single file (no more merging)
  2. 🧹 Linter Rules - Review new warnings from previously disabled rules
  3. 🔍 Inheritance Validation - Review and fix newly reported incompatibilities
  4. 🎨 JSON Schema - Generate schema for IDE autocomplete: mago config --schema > mago-schema.json
  5. ⌨️ Shell Completions - Install completions for better CLI experience

🙏 Acknowledgments

Thank you to all contri...

Read more

Mago 1.0.0-beta.34

19 Oct 07:04
1a29b3a

Choose a tag to compare

Mago 1.0.0-beta.34

This is a focused patch release that addresses a key correctness issue in the analyzer's handling of generic types, particularly when working with ::class strings.

🐞 Analyzer Fix

The analyzer's type comparator has been improved to be smarter about generic types.

Previously, comparing a "raw" generic class (e.g., BoxImpl) against a version with explicit type parameters (e.g., BoxImpl<mixed>) would fail. This commonly caused false positive errors when passing a literal class string like BoxImpl::class to a function expecting class-string<BoxImpl<mixed>>.

The analyzer now correctly understands that when type parameters are missing from a generic class, they should be treated as their default template constraints (which is usually mixed). This makes the comparison logic more robust and aligns it with developer expectations.


Full Changelog: 1.0.0-beta.33...1.0.0-beta.34

Mago 1.0.0-beta.33

19 Oct 06:10
74b081b

Choose a tag to compare

Mago 1.0.0-beta.33

This release introduces a powerful and highly-requested set of new analysis features for detecting missing type hints. We've also made the analyzer significantly smarter at handling superglobals and boolean comparisons, landed a raft of critical bug fixes that improve type-narrowing, and shipped a major performance optimization for large projects.

✨ New: Missing Type Hint Detection

The analyzer can now detect and report missing type hints for constants, properties, parameters, and return types, helping you improve the strictness and reliability of your codebase. This new analyzer-based feature is more powerful and accurate than the old linter rules it replaces.

  • Comprehensive Checking: The analyzer will report missing-constant-type, missing-property-type, missing-parameter-type, and missing-return-type issues.
  • Highly Configurable: You have fine-grained control via new settings in your mago.toml:
    • check-missing-type-hints: The main switch to enable/disable this feature.
    • check-closure-missing-type-hints: Toggles checks specifically for closures.
    • check-arrow-function-missing-type-hints: Toggles checks specifically for arrow functions.
  • Intelligent & Low-Noise: The analyzer is smart enough to avoid common false positives. It automatically ignores:
    • Methods/properties that override a parent that also lacks a type hint.
    • Parameters prefixed with $_ (a common convention for unused variables).
    • Constructors (__construct) and destructors (__destruct) for return type checks.

⚡️ Major Performance Gains for Large Projects

A significant performance bottleneck in the type reconciliation logic for sealed classes has been eliminated. The impact of this optimization is most dramatic on very large projects that make extensive use of sealed type hierarchies with many instanceof checks.

When analyzing a large codebase like the Symfony framework, this release is over 6x faster than the previous version.

Symfony Benchmarks:

Benchmark 1: mago-32 analyze --reporting-format count
  Time (mean ± σ):      8.894 s ±  0.326 s    [User: 11.398 s, System: 4.137 s]
  Range (min … max):    8.509 s …  9.508 s    10 runs
 
  Warning: Ignoring non-zero exit code.
 
Benchmark 2: mago-33 analyze --reporting-format count
  Time (mean ± σ):      1.455 s ±  0.071 s    [User: 7.138 s, System: 1.680 s]
  Range (min … max):    1.351 s …  1.536 s    10 runs
 
  Warning: Ignoring non-zero exit code.
 
Summary
  mago-33 analyze --reporting-format count ran
    6.11 ± 0.37 times faster than mago-32 analyze --reporting-format count

For smaller projects or codebases that do not heavily rely on this specific pattern (like PSL or WordPress), the performance will remain similar to the already fast previous versions. This targeted optimization ensures that Mago scales efficiently, even for the most complex architectural patterns, without introducing regressions elsewhere.

Psl Benchmarks:

Benchmark 1: mago-32 --config config/mago.toml analyze
  Time (mean ± σ):     233.6 ms ±  13.4 ms    [User: 612.0 ms, System: 400.4 ms]
  Range (min … max):   224.4 ms … 271.2 ms    10 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 2: mago-33 --config config/mago.toml analyze
  Time (mean ± σ):     224.4 ms ±   8.4 ms    [User: 631.4 ms, System: 402.6 ms]
  Range (min … max):   214.0 ms … 237.2 ms    10 runs
 
Summary
  mago-33 --config config/mago.toml analyze ran
    1.04 ± 0.07 times faster than mago-32 --config config/mago.toml analyze

WordPress Benchmarks:

Benchmark 1: mago-32 analyze --reporting-format count
  Time (mean ± σ):      3.853 s ±  0.147 s    [User: 15.298 s, System: 0.752 s]
  Range (min … max):    3.467 s …  3.946 s    10 runs
 
  Warning: Ignoring non-zero exit code.
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 2: mago-33 analyze --reporting-format count
  Time (mean ± σ):      3.832 s ±  0.200 s    [User: 16.264 s, System: 0.758 s]
  Range (min … max):    3.488 s …  3.991 s    10 runs
 
  Warning: Ignoring non-zero exit code.
 
Summary
  mago-33 analyze --reporting-format count ran
    1.01 ± 0.07 times faster than mago-32 analyze --reporting-format count

🚀 Major Analyzer Improvements

Smarter Superglobal Handling

The analyzer is now much smarter about superglobals ($_GET, $_SERVER, etc.). They are now pre-registered in all scopes by default, which resolves a major pain point where type narrowing on superglobal array keys (e.g., in if (isset($_GET['user']))) would fail. You can control this with the new register-super-globals setting.

Upgraded: Type-Aware Boolean Comparison

The no-boolean-literal-comparison check has been moved from the linter to the analyzer. Because the analyzer is type-aware, it can now avoid false positives by understanding the context. For example, it will correctly allow a check like $foo === false if the type of $foo is a union that includes false (e.g., false|string).

🐞 Other Bug Fixes

  • Critical Analysis Fixes:
    • Resolved several bugs related to correctly resolving static return types, detecting callables in ::class strings, and handling chained null checks. (#511, #549, #577)
    • Corrected object type subtraction logic for non-sealed class hierarchies, improving type narrowing.
    • Fixed a bug where isset() was not correctly narrowing a nullable array key to non-null within a conditional block. (#573)

⚠️ Deprecations

The following linter rules are now deprecated in favor of the more powerful analyzer-based checks. They will be removed in a future release.

  • constant-type
  • parameter-type
  • property-type
  • return-type
  • no-boolean-literal-comparison

🤝 New Contributors

Closed Issues


Full Changelog: 1.0.0-beta.32...1.0.0-beta.33

Mago 1.0.0-beta.32

17 Oct 05:13
18c2f9a

Choose a tag to compare

Mago 1.0.0-beta.32

This is a massive stability and correctness release for the analyzer. We've significantly improved its ability to understand complex conditional logic and type hierarchies, leading to smarter type narrowing and the elimination of many false positives. As we get closer to the first release candidate, these foundational fixes are crucial for providing a reliable analysis experience.

A huge thank you to @baukevdw for their contribution to this release!

🚀 Smarter Conditional Type Analysis

The analyzer's ability to narrow types within if and match statements has been made much more powerful and correct.

  • Sealed Types (@psalm-inheritors) (#537): The analyzer now understands the @psalm-inheritors tag. When you check and negate an instanceof on a sealed interface or class, it will correctly infer the remaining possible types from the inheritor list. This is a huge improvement for codebases using algebraic data types.

  • Null Coalesce Assertions (??) (#531): The analyzer now correctly understands that a check like ($var ?? null) !== null guarantees that $var is both set and not null, eliminating false "possibly undefined" errors inside the if block.

  • Assignments in && Conditions (#538): Fixed a bug where a variable assigned on the right-hand side of an && in a condition was incorrectly reported as undefined.

  • Multiple isset() Assertions (#564): The analyzer now correctly processes all assertions in conditions with multiple isset() calls, like isset($a, $b) or isset($a) && isset($b).

🐞 Analyzer Soundness & Correctness

Several fundamental bugs in the type system and reconciler have been fixed to improve soundness.

  • !isset is null, not never (#570): A !isset() check now correctly reconciles the variable's type to null (or possibly_undefined), not never. This fixes a major bug that caused the analyzer to incorrectly mark valid code paths as unreachable.

  • Differentiated Null Array Access (#568): The analyzer now distinguishes between accessing an array on a variable that is definitely null (a NullArrayAccess error) versus one that is possibly null (a PossiblyNullArrayAccess warning). This provides more precise and appropriately severe feedback.

  • array vs Traversable (#563): A soundness issue was fixed where array was incorrectly treated as a subtype of Traversable. The type comparator now correctly distinguishes between them, fixing incorrect type narrowing for instanceof checks.

✨ New Configuration

  • New strict-list-index-checks Option (#565): A new configuration option, strict-list-index-checks (default: false), has been added. When enabled, it enforces that array list indices must be non-negative-int, providing stricter validation for users who want it.

🤝 New Contributors

Closed Issues


Full Changelog: 1.0.0-beta.31...1.0.0-beta.32

Mago 1.0.0-beta.31

16 Oct 05:44
acac89c

Choose a tag to compare

Mago 1.0.0-beta.31

This is a hotfix release that addresses a critical panic in the mago ast command, ensuring stability when processing files with multi-byte characters.

🐞 CLI Fix

A critical bug has been fixed that could cause the mago ast --tokens command to panic and crash when processing files containing multi-byte UTF-8 characters (e.g., emojis or CJK characters).

The crash occurred because the table-formatting logic was incorrectly truncating token values by byte length, which is unsafe for UTF-8 strings and can lead to a panic if a slice ends in the middle of a character.

The logic has been rewritten to be fully UTF-8 aware. It now correctly measures the visual width of characters and truncates strings on proper character boundaries. This not only resolves the panic but also ensures the token table aligns correctly for all character sets.

Closed Issues


Full Changelog: 1.0.0-beta.30...1.0.0-beta.31

Mago 1.0.0-beta.30

16 Oct 02:16
9338007

Choose a tag to compare

Mago 1.0.0-beta.30

This release introduces mago guard, a powerful new tool for enforcing architectural rules in your PHP projects. We've also added a new list-files utility command and landed several key correctness fixes for the analyzer and formatter as we get closer to our first release candidate.

A huge thank you to our community contributors for this release: @dotdash and @bendavies!

🛡️ New Feature: mago guard

We are thrilled to introduce mago guard, a high-performance tool for defining and enforcing architectural boundaries directly within your project. It combines the power of dependency validation (like deptrac) and structural rule-checking (like arkitect) into a single, cohesive command.

The goal of guard is to provide instant feedback on architectural violations, helping you maintain a clean and scalable codebase. Its concepts are designed to be intuitive, using a "Fortress" metaphor:

  • 🏰 The Fortress is your entire codebase.
  • 🧱 Perimeter Rules define your defensive layers. They control which namespaces can communicate, preventing illegal dependencies (e.g., ensuring your Domain layer never depends on your UI layer). A violation is a BoundaryBreach.
  • 🏛️ Structural Rules define the building codes inside the fortress. They ensure code within a namespace is built correctly (e.g., all Controllers are final, all Repositories implement an interface). A violation is a StructuralFlaw.

All rules are defined declaratively in your mago.toml file.

Read the full documentation for mago guard here.

✨ Other New Features

  • New list-files Command (#552): A new mago list-files command has been added. This utility prints a list of all files that would be processed by the lint, format, analyze, or guard commands, based on your configuration. It's an excellent tool for debugging your [source] paths and exclusion rules. (Thanks, @dotdash!)

🚀 Analyzer & Formatter Correctness

  • Analyzer: Fixed a critical soundness bug where accessing an explicitly optional array shape key (e.g., email in array{email?: string}) would not produce a nullable type. Accessing such a key now correctly results in a T|null type. (#557)
  • Analyzer: Fixed a bug where a negated isset() check on a possibly undefined array key was not correctly narrowing the type to null. (Thanks, @bendavies!)
  • Analyzer: The analyzer now correctly allows assigning a value to a string-keyed index on a list, which correctly converts the type to a keyed array. (#559)
  • Formatter: Fixed a bug where catch blocks with multiple exception types that exceeded the print width were not being correctly broken onto multiple lines. (#556)
  • Linter: The prefer-arrow-function rule will no longer suggest converting a closure to an arrow function if the closure imports variables by reference, which is unsafe. (#554)

Closed Issues


Full Changelog: 1.0.0-beta.28...1.0.0-beta.30