carthage-software/mago
Mago is an extremely fast PHP linter, formatter, and static analyzer written in Rust. It brings Rust-inspired speed and reliability to PHP projects with a modern toolchain and great developer experience, plus multiple install options (script, Homebrew, Composer).
This document details the rules available in the Correctness category.
| Rule | Code |
|---|---|
| Assert Description | assert-description |
| Identity Comparison | identity-comparison |
| Ineffective Format Ignore Next | ineffective-format-ignore-next |
| Ineffective Format Ignore Region | ineffective-format-ignore-region |
| Invalid Open Tag | invalid-open-tag |
| No Assign In Argument | no-assign-in-argument |
| No Assign In Condition | no-assign-in-condition |
| No Empty Catch Clause | no-empty-catch-clause |
| No Only | no-only |
| Strict Assertions | strict-assertions |
| Strict Behavior | strict-behavior |
| Strict Types | strict-types |
| Switch Continue to Break | switch-continue-to-break |
| Use Specific Assertions | use-specific-assertions |
assert-descriptionDetects assert functions that do not have a description.
Assert functions should have a description to make it easier to understand the purpose of the assertion.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
assert($user->isActivated(), 'User MUST be activated at this point.');
<?php
assert($user->isActivated());
identity-comparisonDetects equality and inequality comparisons that should use identity comparison operators.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
if ($a === $b) {
echo '$a is same as $b';
}
<?php
if ($a == $b) {
echo '$a is same as $b';
}
ineffective-format-ignore-nextDetects [@mago-format-ignore-next](https://github.com/mago-format-ignore-next) markers that will have no effect.
The formatter's ignore-next marker works at the statement level. When a marker is placed inside an expression (like function call arguments, array elements, or other non-statement contexts), it will not affect the formatter's output.
To effectively ignore the next statement, place the marker immediately before a complete statement at the top level of a block or file.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
// This works - marker is before a statement
// [@mago-format-ignore-next](https://github.com/mago-format-ignore-next)
const GRID = [
[1, 2, 3], [1, 2, ], [0, 0],
];
foo();
<?php
// This doesn't work - marker is inside an array literal
$arr = [ // [@mago-format-ignore-next](https://github.com/mago-format-ignore-next)
1,
2,
];
ineffective-format-ignore-regionDetects [@mago-format-ignore-start](https://github.com/mago-format-ignore-start) markers that will have no effect.
The formatter's ignore regions work at the statement level. When an ignore marker is placed inside an expression (like function call arguments, array elements, or other non-statement contexts), it will not affect the formatter's output.
To effectively ignore a region, place the ignore markers between complete statements at the top level of a block or file.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
// This works - markers are between statements
// [@mago-format-ignore-start](https://github.com/mago-format-ignore-start)
$x = 1; $y = 2; // preserved as-is
// [@mago-format-ignore-end](https://github.com/mago-format-ignore-end)
foo();
<?php
// This doesn't work - markers are inside a function call
foo( // [@mago-format-ignore-start](https://github.com/mago-format-ignore-start)
$x,
$y
// [@mago-format-ignore-end](https://github.com/mago-format-ignore-end)
);
invalid-open-tagDetects misspelled PHP opening tags like <php? instead of <?php.
A misspelled opening tag will cause the PHP interpreter to treat the following code as plain text, leading to the code being output directly to the browser instead of being executed. This can cause unexpected behavior and potential security vulnerabilities.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"note" |
<?php
echo 'Hello, world!';
<php?
echo 'Hello, world!';
no-assign-in-argumentDetects assignments in function call arguments which can lead to unexpected behavior and make the code harder to read and understand.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
false |
level |
string |
"warning" |
<?php
$x = 5;
foo($x);
<?php
foo($x = 5);
no-assign-in-conditionDetects assignments in conditions which can lead to unexpected behavior and make the code harder to read and understand.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
$x = 1;
if ($x == 1) {
// ...
}
<?php
if ($x = 1) {
// ...
}
no-empty-catch-clauseWarns when a catch clause is empty.
An empty catch clause suppresses exceptions without handling or logging them,
potentially hiding errors that should be addressed. This practice, known as
"exception swallowing," can make debugging significantly more difficult.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
try {
// some code that might throw an exception
} catch(Exception $e) {
// Handle the error, log it, or re-throw it.
error_log($e->getMessage());
}
<?php
try {
// some code
} catch(Exception $e) {
// This block is empty and swallows the exception.
}
no-onlyDetects usage of ->only() in Pest tests which should not be committed.
The ->only() modifier causes only that specific test to run, which can lead to
incomplete test coverage if accidentally committed to the repository.
Pest| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
test('example test', function () {
expect(true)->toBeTrue();
});
it('does something', function () {
expect(1)->toBe(1);
});
<?php
test('example test', function () {
expect(true)->toBeTrue();
})->only();
it('does something', function () {
expect(1)->toBe(1);
})->only();
strict-assertionsDetects non-strict assertions in test methods.
Assertions should use strict comparison methods, such as assertSame or assertNotSame
instead of assertEquals or assertNotEquals.
PHPUnit| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SomeTest extends TestCase
{
public function testSomething(): void
{
$this->assertSame(42, 42);
}
}
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SomeTest extends TestCase
{
public function testSomething(): void
{
$this->assertEquals(42, 42);
}
}
strict-behaviorDetects functions relying on loose comparison unless the $strict parameter is specified.
The use of loose comparison for these functions may lead to hard-to-debug, unexpected behaviors.
7.0.0| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
allow-loose-behavior |
boolean |
false |
<?php
in_array(1, ['foo', 'bar', 'baz'], strict: true);
<?php
in_array(1, ['foo', 'bar', 'baz']);
strict-typesDetects missing declare(strict_types=1); statement at the beginning of the file.
7.0.0| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
allow-disabling |
boolean |
false |
<?php
declare(strict_types=1);
echo "Hello, World!";
<?php
echo "Hello, World!";
switch-continue-to-breakDetects the use of continue inside a switch statement, which should
be break instead.
In PHP, continue inside a switch behaves the same as break, but
using continue is misleading because it suggests continuing a loop.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
false |
level |
string |
"warning" |
<?php
switch ($value) {
case 1:
echo 'one';
break;
}
<?php
switch ($value) {
case 1:
echo 'one';
continue;
}
use-specific-assertionsSuggests using specific PHPUnit assertions instead of generic equality assertions
when comparing with null, true, or false.
Using specific assertions like assertNull, assertTrue, and assertFalse
provides clearer error messages and makes test intent more explicit.
PHPUnit| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SomeTest extends TestCase
{
public function testSomething(): void
{
$this->assertNull($value);
$this->assertTrue($flag);
$this->assertFalse($condition);
}
}
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SomeTest extends TestCase
{
public function testSomething(): void
{
$this->assertEquals(null, $value);
$this->assertSame(true, $flag);
$this->assertEquals(false, $condition);
}
}
How can I help you explore Laravel packages today?