Configuration reference

Settings live under [guard] in mago.toml. The configuration has two parts: [guard.perimeter] for dependency rules and [[guard.structural.rules]] for structural conventions.

Top-level options

OptionTypeDefaultDescription
modestring"default"Which checks to run. One of "default", "structural", "perimeter".
excludesstring[][]Paths or glob patterns to exclude from analysis. Additive to [source].excludes.
baselinestringunsetPath to a baseline file. Equivalent to passing --baseline on every run.
baseline-variantstring"loose"Format for newly generated baselines. "loose" (count-based) or "strict" (exact line matching). See baseline.
minimum-fail-levelstring"error"Minimum severity that causes a non-zero exit. One of "note", "help", "warning", "error". Overridden by --minimum-fail-level.

mode controls which half of the guard runs:

  • "default" runs both halves.
  • "structural" runs only structural checks.
  • "perimeter" runs only perimeter checks.
[guard]
mode = "structural"

The --structural and --perimeter flags override the configured mode. See the command reference.

excludes here is added to whatever you set in [source].excludes; it never narrows the global list.

[source]
excludes = ["cache/**"]

[guard]
excludes = ["src/ThirdParty/**"]

Perimeter guard

The perimeter section defines dependency rules between parts of the project.

[guard.perimeter]
layering = [
    "CarthageSoftware\\Domain",
    "CarthageSoftware\\Application",
    "CarthageSoftware\\UI",
    "CarthageSoftware\\Infrastructure",
]

[guard.perimeter.layers]
core = ["@native", "Psl\\**"]
psr = ["Psr\\**"]
framework = ["Symfony\\**", "Doctrine\\**"]

[[guard.perimeter.rules]]
namespace = "CarthageSoftware\\Domain"
permit = ["@layer:core"]

[[guard.perimeter.rules]]
namespace = "CarthageSoftware\\Application"
permit = ["@layer:core", "@layer:psr"]

[[guard.perimeter.rules]]
namespace = "CarthageSoftware\\Infrastructure"
permit = ["@layer:core", "@layer:psr", "@layer:framework"]

[[guard.perimeter.rules]]
namespace = "CarthageSoftware\\Tests"
permit = ["@all"]

layering

An ordered list of namespaces, from the most independent core down to the outermost layer. Each layer can only depend on layers defined before it. A dependency that points to a layer further out triggers a violation.

Layer aliases

[guard.perimeter.layers] defines reusable groups of namespaces and paths, referenced from rules with @layer:<name>.

Rules

Each [[guard.perimeter.rules]] table defines one rule:

  • namespace: the namespace this rule applies to. Either a namespace ending in \ or the special keyword @global for the global namespace.
  • permit: the dependencies that are allowed. Either a list of strings or a list of detailed objects.

permit values

permit accepts paths. A path can be a keyword, a namespace, a symbol, or a glob pattern.

PathDescription
@globalSymbols defined in the global namespace.
@allAny symbol anywhere in the project, including vendor packages. Useful for tests.
@self / @thisAny symbol within the same root namespace as the rule's namespace.
@native / @phpPHP's built-in functions, classes, and constants.
@layer:<name>All namespaces and paths in the named alias from [guard.perimeter.layers].
App\Shared\\**Glob pattern. * matches a single namespace segment, ** matches zero or more.
App\ServiceExact fully qualified symbol name.
App\Service\\Exact namespace. Allows symbols directly within it.

You can narrow a permission by symbol kind using an object form:

[[guard.perimeter.rules]]
namespace = "DoctrineMigrations\\"
permit = [{ path = "@all", kinds = ["class-like"] }]
  • path: any of the path forms above.
  • kinds: which kinds of symbols are permitted. Values are class-like (covers classes, interfaces, traits, enums), function, constant, and attribute.

Structural guard

[[guard.structural.rules]] defines structural conventions. Each entry combines selectors that pick which symbols to inspect with constraints that the selected symbols must satisfy.

[[guard.structural.rules]]
on = "CarthageSoftware\\UI\\**\\Controller\\**"
target = "class"
must-be-named = "*Controller"
must-be-final = true
must-be-readonly = true
reason = "Controllers must be final and follow naming conventions."

[[guard.structural.rules]]
on = "CarthageSoftware\\Domain\\**\\Repository\\**"
target = "interface"
must-be-named = "*RepositoryInterface"
reason = "Domain repository interfaces must follow a standard naming convention."

[[guard.structural.rules]]
on = "CarthageSoftware\\Infrastructure\\**\\Repository\\**"
target = "class"
must-be-final = true
must-extend = "CarthageSoftware\\Infrastructure\\Shared\\Repository\\AbstractRepository"
reason = "Infrastructure repositories must extend our abstract class."

[[guard.structural.rules]]
on = "CarthageSoftware\\Domain\\**\\Enum\\**"
must-be = ["enum"]
reason = "This namespace is designated for enums only."

Selectors

KeyDescription
onRequired. Glob pattern matching the fully qualified name of the symbols this rule applies to.
not-onOptional glob pattern excluding symbols that would otherwise match on.
targetOptional filter restricting the rule to one symbol kind. One of class, interface, trait, enum, function, constant.

Constraints

KeyDescription
must-beRestrict the selected namespace to contain only the listed symbol kinds. Values: class, interface, trait, enum, function, constant.
must-be-namedGlob pattern the symbol name must match (e.g. *Controller).
must-be-finalBoolean. true requires final; false forbids it.
must-be-abstractBoolean. true requires abstract; false forbids it.
must-be-readonlyBoolean. true requires readonly; false forbids it.
must-implementOne or more interfaces the class must implement.
must-extendA class the symbol must extend.
must-use-traitOne or more traits the symbol must use.
must-use-attributeOne or more attributes the symbol must carry.
reasonHuman-readable explanation shown in error messages.

Inheritance constraint shapes

must-implement, must-extend, must-use-trait, and must-use-attribute accept a single string, an array of strings (AND), or an array of arrays of strings (OR of ANDs). The literal "@nothing" forbids any value.

must-extend = "App\\BaseClass"

must-implement = ["App\\InterfaceA", "App\\InterfaceB"]

must-extend = [
    ["App\\AbstractA", "App\\AbstractB"],
    ["App\\AbstractC"],
]

must-implement = "@nothing"

↳ Edit this page →