Cette traduction peut être obsolète.

Le linter de Mago propose 175 règles réparties sur 9 catégories. Cliquez sur une règle pour afficher sa description, ses prérequis, sa configuration par défaut et ses exemples.

Règles spécifiques aux intégrations

Certaines règles ne se déclenchent que lorsque Mago détecte une bibliothèque ou un framework particulier. Chaque règle renvoie à sa description complète dans la section ci-dessus.

CakePHP

Laravel

PHPUnit

Pest

Psl

Spiral

Symfony

Tempest

WordPress

Yii

Clarté

Règles qui rendent l'intention explicite et réduisent l'effort de lecture. Elles signalent des constructions techniquement valides mais qui obscurcissent ce que fait le code.

no-emptyerror

Detects the use of the empty() construct.

The empty() language construct can lead to ambiguous and potentially buggy code due to loose and counterintuitive definition of emptiness. It fails to clearly convey developer's intent or expectation, making it preferable to use explicit checks.


À éviter
<?php

if (!empty($myArray)) {
    // ...
}
À privilégier
<?php

if ($myArray === []) {
    // ...
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
explicit-octalwarning

Detects implicit octal numeral notation and suggests replacing it with explicit octal numeral notation.

Cette règle requiert PHP 8.1.0 ou supérieur.

À éviter
<?php

$a = 0123;
À privilégier
<?php

$a = 0o123;

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
instanceof-stringablewarning

Detects the legacy pattern is_object($x) && method_exists($x, '__toString') and suggests replacing it with $x instanceof Stringable for improved readability and performance.

Since PHP 8.0, all classes with __toString() automatically implement the Stringable interface.

Cette règle requiert PHP 8.0.0 ou supérieur.

À éviter
<?php

function stringify(mixed $value): string {
    if (is_object($value) && method_exists($value, '__toString')) {
        return (string) $value;
    }

    return '';
}
À privilégier
<?php

function stringify(mixed $value): string {
    if ($value instanceof Stringable) {
        return (string) $value;
    }

    return '';
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
literal-named-argumentwarning

Enforces that literal values used as arguments in function or method calls are passed as named arguments.

This improves readability by clarifying the purpose of the literal value at the call site. It is particularly helpful for boolean flags, numeric constants, and null values where the intent is often ambiguous without the parameter name.

Cette règle requiert PHP 8.0.0 ou supérieur.

À éviter
<?php

function set_option(string $key, bool $enable_feature) {}

set_option('feature_x', true); // ❌ intent unclear
À privilégier
<?php

function set_option(string $key, bool $enable_feature) {}

set_option(key: 'feature_x', enable_feature: true); // ✅ clear intent

OptionTypeDéfaut
check-first-argumentbooleanfalse
enabledbooleantrue
levelstring"warning"
thresholdnumber1
no-hash-emojiwarning

Discourages usage of the #️⃣ emoji in place of the ASCII #.

While PHP allows the use of emojis in comments, it is generally discouraged to use them in place of the normal ASCII # symbol. This is because it can confuse readers and may break external tools that expect the normal ASCII # symbol.


À éviter
<?php

#️⃣ This is a comment

#️⃣[MyAttribute] <- not a valid attribute
class Foo {}
À privilégier
<?php

# This is a comment

#[MyAttribute]
class Foo {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-issetwarning

Detects the use of the isset() construct.

The isset() language construct checks whether a variable is set and is not null. However, it can lead to ambiguous code because it conflates two distinct checks: variable existence and null comparison. Using explicit null checks or the null coalescing operator (??) is often clearer and more maintainable.


À éviter
<?php

if (isset($value)) {
    // ...
}
À privilégier
<?php

if ($value !== null) {
    // ...
}

$result = $value ?? 'default';

OptionTypeDéfaut
allow-array-checksbooleanfalse
enabledbooleantrue
levelstring"warning"
no-multi-assignmentswarning

Flags any instances of multiple assignments in a single statement. This can lead to confusion and unexpected behavior, and is generally considered poor practice.


À éviter
<?php

$a = $b = 0;
À privilégier
<?php

$b = 0;
$a = $b;

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-nested-ternarywarning

Nested ternary expressions are disallowed to improve code clarity and prevent potential bugs arising from confusion over operator associativity.

In PHP 8.0 and later, the ternary operator (? :) is non-associative. Before PHP 8.0, it was left-associative, which is now deprecated. Most other programming languages treat it as right-associative. This inconsistency across versions and languages can make nested ternaries hard to reason about, even when using parentheses.


À éviter
<?php

$allowed = $user->isAdmin() ? true : ($user->isEditor() ? true : false);
À privilégier
<?php

if ($user->isAdmin()) {
    $allowed = true;
} else {
    $allowed = $user->isEditor();
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-shorthand-ternarywarning

Detects the use of the shorthand ternary and elvis operators.

Both shorthand ternary operator ($a ? : $b) and elvis operator ($a ?: $b) relies on loose comparison.


À éviter
<?php
$value = $foo ?: $default;
$value = $foo ? : $default;
À privilégier
<?php

$value = $foo ?? $default;
$value = $foo ? $foo : $default;

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-variable-variablewarning

Discourages usage of PHP's variable variables feature.

Variable variables can make code harder to read and maintain, as they introduce a level of indirection that can confuse readers and complicate static analysis.


À éviter
<?php

$foo = 'bar';
$varName = 'foo';

echo $$varName; // Outputs 'bar'
À privilégier
<?php

$foo = 'bar';

echo $foo; // Outputs 'bar'

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
readable-literalwarning

Enforces using underscore separators in numeric literals for improved readability.

Cette règle requiert PHP 7.4.0 ou supérieur.

À éviter
<?php

$a = 1000000;
$b = 0xCAFEF00D;
$c = 0b01011111;
À privilégier
<?php

$a = 1_000_000;
$b = 0xCAFE_F00D;
$c = 0b0101_1111;

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
min-digitsnumber5
str-containswarning

Detects strpos($a, $b) !== false and strpos($a, $b) === false comparisons and suggests replacing them with str_contains($a, $b) or !str_contains($a, $b) for improved readability and intent clarity.

Cette règle requiert PHP 8.0.0 ou supérieur.

À éviter
<?php

$a = 'hello world';
$b = 'world';

if (strpos($a, $b) !== false) {
    echo 'Found';
}
À privilégier
<?php

$a = 'hello world';
$b = 'world';

if (str_contains($a, $b)) {
    echo 'Found';
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
str-starts-withwarning

Detects strpos($a, $b) === 0 comparisons and suggests replacing them with str_starts_with($a, $b) for improved readability and intent clarity.

Cette règle requiert PHP 8.0.0 ou supérieur.

À éviter
<?php

$a = 'hello world';
$b = 'hello';
if (strpos($a, $b) === 0) {
    echo 'Found';
}
À privilégier
<?php

$a = 'hello world';
$b = 'hello';
if (str_starts_with($a, $b)) {
    echo 'Found';
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
tagged-fixmewarning

Detects FIXME comments that are not tagged with a user or issue reference. Untagged FIXME comments are not actionable and can be easily missed by the team. Tagging the FIXME comment with a user or issue reference ensures that the issue is tracked and resolved.


À éviter
<?php

// FIXME: This is an invalid FIXME comment.
À privilégier
<?php

// FIXME(@azjezz) This is a valid FIXME comment.
// FIXME(azjezz) This is a valid FIXME comment.
// FIXME(#123) This is a valid FIXME comment.

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
tagged-todowarning

Detects TODO comments that are not tagged with a user or issue reference. Untagged TODOs can be difficult to track and may be forgotten. Tagging TODOs with a user or issue reference makes it easier to track progress and ensures that tasks are not forgotten.


À éviter
<?php

// TODO: This is an invalid TODO comment.
À privilégier
<?php

// TODO(@azjezz) This is a valid TODO comment.
// TODO(azjezz) This is a valid TODO comment.
// TODO(#123) This is a valid TODO comment.

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
use-dedicated-expectationwarning

Use dedicated matchers instead of function calls in Pest tests.

Instead of expect(is_array($x))->toBeTrue(), use expect($x)->toBeArray(). This provides clearer intent and better error messages.

Supported patterns:

  • Type checks: is_array, is_string, is_int, is_float, is_bool, is_numeric, is_callable, is_iterable, is_object, is_resource, is_scalar, is_null
  • String: str_starts_with, str_ends_with, ctype_alpha, ctype_alnum
  • Array: in_array, array_key_exists
  • File: is_file, is_dir, is_readable, is_writable, file_exists
  • Object: property_exists
Cette règle requiert que l'intégration Pest soit activée.

À éviter
<?php

test('function calls', function () {
    expect(is_array($value))->toBeTrue();
    expect(is_string($value))->toBeTrue();
    expect(str_starts_with($string, 'prefix'))->toBeTrue();
    expect(in_array($item, $array))->toBeTrue();
    expect(is_file($path))->toBeTrue();
    expect(property_exists($obj, 'name'))->toBeTrue();
});
À privilégier
<?php

test('dedicated matchers', function () {
    expect($value)->toBeArray();
    expect($value)->toBeString();
    expect($string)->toStartWith('prefix');
    expect($array)->toContain($item);
    expect($path)->toBeFile();
    expect($obj)->toHaveProperty('name');
});

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
use-simpler-expectationwarning

Simplify expect() expressions in Pest tests by using dedicated matchers.

This rule detects patterns where the expect() argument contains an expression that can be simplified:

  • expect(!$x)->toBeTrue() -> expect($x)->toBeFalse()
  • expect(!$x)->toBeFalse() -> expect($x)->toBeTrue()
  • expect($a > $b)->toBeTrue() -> expect($a)->toBeGreaterThan($b)
  • expect($a >= $b)->toBeTrue() -> expect($a)->toBeGreaterThanOrEqual($b)
  • expect($a < $b)->toBeTrue() -> expect($a)->toBeLessThan($b)
  • expect($a <= $b)->toBeTrue() -> expect($a)->toBeLessThanOrEqual($b)
  • expect($a === $b)->toBeTrue() -> expect($a)->toBe($b)
  • expect($a !== $b)->toBeTrue() -> expect($a)->not->toBe($b)
  • expect($x instanceof Y)->toBeTrue() -> expect($x)->toBeInstanceOf(Y::class)
  • expect($x >= min && $x <= max)->toBeTrue() -> expect($x)->toBeBetween(min, max)

Using dedicated matchers provides clearer intent and better error messages.

Cette règle requiert que l'intégration Pest soit activée.

À éviter
<?php

test('complex expectations', function () {
    expect(!$condition)->toBeTrue();
    expect($a > $b)->toBeTrue();
    expect($a === $b)->toBeTrue();
    expect($obj instanceof ClassName)->toBeTrue();
    expect($x >= 1 && $x <= 10)->toBeTrue();
});
À privilégier
<?php

test('simplified expectations', function () {
    expect($condition)->toBeFalse();
    expect($a)->toBeGreaterThan($b);
    expect($a)->toBe($b);
    expect($obj)->toBeInstanceOf(ClassName::class);
    expect($x)->toBeBetween(1, 10);
});

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
use-specific-expectationswarning

Use dedicated matchers instead of generic comparisons in Pest tests.

This rule suggests more specific matchers for common patterns:

  • toBe(true) / toEqual(true) -> toBeTrue()
  • toBe(false) / toEqual(false) -> toBeFalse()
  • toBe(null) / toEqual(null) -> toBeNull()
  • toBe([]) / toBe('') -> toBeEmpty()
  • not->toBeFalse() -> toBeTrue()
  • not->toBeTrue() -> toBeFalse()

Using dedicated matchers provides clearer intent and better error messages.

Cette règle requiert que l'intégration Pest soit activée.

À éviter
<?php

test('generic comparisons', function () {
    expect($value)->toBe(true);
    expect($value)->toBe(false);
    expect($value)->toBe(null);
    expect($array)->toBe([]);
    expect($value)->not->toBeFalse();
});
À privilégier
<?php

test('specific matchers', function () {
    expect($value)->toBeTrue();
    expect($value)->toBeFalse();
    expect($value)->toBeNull();
    expect($array)->toBeEmpty();
});

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
valid-docblocknote

Checks for syntax errors in docblock comments, such as malformed {@see} or {@link} annotations. It does not enforce the presence of docblocks or verify that declared types match the native declaration.


À éviter
<?php

/**
 * For more information, {@see https://example.com
 *
 * @param int $a
 *
 * @return int
 */
function foo($a) {
    return $a;
}
À privilégier
<?php

/**
 * For more information, {@see https://example.com}.
 *
 * @param int $a
 *
 * @return int
 */
function foo($a) {
    return $a;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
missing-docshelp

Detects declarations that are missing a docblock.

This rule can be configured to require documentation for functions, classes, interfaces, traits, enums, enum cases, constants, statics, methods, and properties.

Documentation is useful when it explains intent, behaviour, usage, invariants, or other details that are not obvious from the code alone.


À éviter
<?php

function foo() {}
À privilégier
<?php

/**
 * A helpful piece of documentation.
 */
function foo() {}

OptionTypeDéfaut
classesbooleanfalse
constantsbooleantrue
enabledbooleanfalse
enum-casesbooleantrue
enumsbooleanfalse
functionsbooleantrue
interfacesbooleanfalse
levelstring"help"
methodsbooleantrue
propertiesbooleantrue
staticsbooleantrue
traitsbooleanfalse
no-negated-ternaryhelp

Flags ternary expressions whose condition is a logical negation (!$foo ? a : b).

A negated condition adds a layer of indirection the reader has to undo to follow the branches. Removing the negation and swapping the then and else branches produces an equivalent expression that reads more directly.


À éviter
<?php

$x = !$foo ? 1 : 0;
À privilégier
<?php

$x = $foo ? 0 : 1;

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
no-short-bool-casthelp

Detects the use of double negation !!$expr as a shorthand for (bool) $expr.

The explicit (bool) cast is clearer about the intent to convert a value to a boolean.


À éviter
<?php

$active = !!$value;
À privilégier
<?php

$active = (bool) $value;

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"

Bonnes pratiques

Patterns idiomatiques et conventions largement adoptées. Ces règles orientent le code vers la manière dont le PHP moderne est écrit par celles et ceux qui en livrent beaucoup.

final-controllererror

Enforces that controller classes are declared as final.

In modern MVC frameworks, controllers should be treated as entry points that orchestrate the application's response to a request. They are not designed to be extension points.

Extending controllers can lead to deep inheritance chains, making the codebase rigid and difficult to maintain. It's a best practice to favor composition (injecting services for shared logic) over inheritance.

If a controller is intended as a base for others, it should be explicitly marked as abstract. All other concrete controllers should be final to prevent extension.

Cette règle requiert l'activation de l'un des ensembles d'intégrations suivants : Symfony ; ou Laravel ; ou Tempest ; ou Spiral ; ou CakePHP ; ou Yii.

À éviter
<?php

namespace App\Http\Controllers;

class UserController
{
    // ...
}
À privilégier
<?php

namespace App\Http\Controllers;

final class UserController
{
    // ...
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-inlineerror

Disallows inline content (text outside of PHP tags) in source files.

Most modern PHP applications are source-code only and do not use PHP as a templating language. Inline content before <?php, after ?>, or between PHP tags is typically unintentional and can cause issues such as unexpected output or "headers already sent" errors.

This rule is disabled by default and is intended for codebases that do not use PHP templates.


À éviter
Hello
<?php

echo "Hello, world!";

?>
Goodbye
À privilégier
<?php

namespace App;

echo "Hello, world!";

OptionTypeDéfaut
enabledbooleanfalse
levelstring"error"
psl-outputerror

This rule enforces the usage of Psl output functions over their PHP counterparts. Psl output functions are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

echo "Hello, world!";
À privilégier
<?php

Psl\IO\write_line("Hello, world!");

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
combine-consecutive-issetswarning

Suggests combining consecutive calls to isset() when they are joined by a logical AND.

For example, isset($a) && isset($b) can be turned into isset($a, $b), which is more concise and avoids repeated function calls. If one or both isset() calls are wrapped in parentheses, the rule will still warn, but it will not attempt an automated fix.


À éviter
<?php

if (isset($a) && isset($b)) {
    // ...
}
À privilégier
<?php

if (isset($a, $b)) {
    // ...
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
loop-does-not-iteratewarning

Detects loops (for, foreach, while, do-while) that unconditionally break or return before executing even a single iteration. Such loops are misleading or redundant since they give the impression of iteration but never actually do so.


À éviter
<?php

for ($i = 0; $i < 3; $i++) {
    break; // The loop never truly iterates, as this break is unconditional.
}
À privilégier
<?php

for ($i = 0; $i < 3; $i++) {
    echo $i;
    if ($some_condition) {
        break; // This break is conditional.
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
middleware-in-routeswarning

This rule warns against applying middlewares in controllers.

Middlewares should be applied in the routes file, not in the controller.

Cette règle requiert que l'intégration Laravel soit activée.

À éviter
<?php

namespace App\Http\Controllers;

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }
}
À privilégier
<?php

// routes/web.php
Route::get('/user', 'UserController@index')->middleware('auth');

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-array-accumulation-in-loopwarning

Detects O(n²) array accumulation patterns inside loops.

Calling array_merge(), array_merge_recursive(), array_unique(), or array_values() on an accumulator inside a loop copies the entire array on every iteration. Similarly, using spread syntax ([...$result, ...$item]) in a reassignment has the same cost.

Collect items first and transform once after the loop instead.


À éviter
<?php

$result = [];
foreach ($items as $item) {
    $result = array_merge($result, $item);
}
À privilégier
<?php

$chunks = [];
foreach ($items as $item) {
    $chunks[] = $item;
}
$result = array_merge(...$chunks);

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-direct-db-querywarning

This rule flags all direct method calls on the global $wpdb object. Direct database queries bypass the WordPress object cache, which can lead to poor performance. Using high-level functions like get_posts() is safer and more efficient.

Cette règle requiert que l'intégration WordPress soit activée.

À éviter
<?php

global $wpdb;
$posts = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_author = 1");
À privilégier
<?php

$posts = get_posts(['author' => $author_id]);

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-ini-setwarning

Enforces that ini_set is not used.

Runtime configuration changes via ini_set make application behavior unpredictable and environment-dependent. They can mask misconfigured servers, introduce subtle bugs, and lead to inconsistent behavior between development, testing, and production environments.

Modern applications should rely on well-defined configuration through php.ini or framework specific configuration. This ensures that configuration is explicit, consistent, and controlled across all environments.

If a setting truly needs to vary between contexts, it should be handled at the infrastructure or framework configuration level, never by calling ini_set within the application code.


À éviter
<?php

// This can override server settings in an unpredictable way.
ini_set( 'display_errors', 1 );
ini_set( 'memory_limit', '256M' );
À privilégier
<?php

// In framework config files (e.g., wp-config.php), use constants.
define( 'WP_DEBUG', true );

// Use framework-provided functions where available.
wp_raise_memory_limit( 'admin' );

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-literal-namespace-stringwarning

Flags hardcoded fully qualified class name strings. Use ::class notation instead for better IDE support, refactoring safety, and static analysis.


À éviter
<?php

$className = 'App\Models\User';
À privilégier
<?php

$className = \App\Models\User::class;

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-parameter-shadowingwarning

Detects when a function or method parameter is shadowed by a loop variable or catch variable, making the original parameter value inaccessible.


À éviter
<?php

function read(array $domains, array $locales): void
{
    $translations = getTranslations($domains, $locales);

    foreach ($translations as $namespace => $locales) {
        // $locales now refers to the loop value, original argument is lost
    }
}
À privilégier
<?php

function read(array $domains, array $locales): void
{
    $translations = getTranslations($domains, $locales);

    foreach ($translations as $namespace => $namespaceLocales) {
        // $locales is still accessible
    }
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-side-effects-with-declarationswarning

Enforces that a PHP file either declares symbols (classes, functions, constants, interfaces, traits, enums) or causes side-effects, but not both.

Side-effects include echo, print, top-level function calls, assignments, include/require statements, and any other executable code outside of a symbol declaration.

This follows the PSR-1 basic coding standard: files SHOULD either declare symbols or execute logic, but SHOULD NOT do both.


À éviter
<?php

echo 'Loading utility file...';

class StringHelper
{
    public static function slugify(string $input): string
    {
        return '';
    }
}
À privilégier
<?php

namespace App;

class UserManager
{
    public function find(int $id): ?User
    {
        return null;
    }
}

OptionTypeDéfaut
allow-class-aliasbooleantrue
allow-class-existsbooleantrue
allow-conditional-declarationsbooleantrue
enabledbooleanfalse
levelstring"warning"
no-sprintf-concatwarning

Disallows string concatenation with the result of an sprintf call.

Concatenating with sprintf is less efficient and can be less readable than incorporating the string directly into the format template. This pattern creates an unnecessary intermediate string and can make the final output harder to see at a glance.


À éviter
<?php

$name = 'World';
$greeting = 'Hello, ' . sprintf('%s!', $name);
À privilégier
<?php

$name = 'World';
$greeting = sprintf('Hello, %s!', $name);

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
prefer-anonymous-migrationwarning

Prefer using anonymous classes for Laravel migrations instead of named classes. Anonymous classes are more concise and reduce namespace pollution, making them the recommended approach for migrations.

Cette règle requiert que l'intégration Laravel soit activée.

À éviter
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class MyMigration extends Migration {
    public function up(): void {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('airline');
            $table->timestamps();
        });
    }

    public function down(): void {
        Schema::drop('flights');
    }
}

return new MyMigration();
À privilégier
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('airline');
            $table->timestamps();
        });
    }

    public function down(): void {
        Schema::drop('flights');
    }
};

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
prefer-explode-over-preg-splitwarning

Detects calls to preg_split() whose pattern has no regex meta-characters and no modifiers, which means the split could be done with explode() and no regex engine at all.

explode() is faster (no compilation step), easier to read, and expresses the intent more directly when the separator is a plain string.

The rule only fires when:

  • the pattern argument is a string literal,
  • the pattern has no flags after the closing delimiter,
  • the content between the delimiters contains no regex meta-characters,
  • and the flags argument (if present) is literal 0.

À éviter
<?php

$parts = preg_split('/, /', $csv);
À privilégier
<?php

$parts = explode(', ', $csv);

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
prefer-first-class-callablewarning

Promotes the use of first-class callable syntax (...) for creating closures.

This rule identifies closures and arrow functions that do nothing but forward their arguments to another function or method. In such cases, the more concise and modern first-class callable syntax, introduced in PHP 8.1, can be used instead. This improves readability by reducing boilerplate code.

By default, this rule only checks method and static method calls. Optionally, function calls can also be checked by enabling check-functions, but this may produce false positives with internal PHP functions that enforce strict argument counts.

Cette règle requiert PHP 8.1.0 ou supérieur.

À éviter
<?php

$names = ['Alice', 'Bob', 'Charlie'];
$uppercased_names = array_map(fn($name) => $formatter->format($name), $names);
À privilégier
<?php

$names = ['Alice', 'Bob', 'Charlie'];
$uppercased_names = array_map($formatter->format(...), $names);

OptionTypeDéfaut
check-functionsbooleanfalse
enabledbooleantrue
levelstring"warning"
prefer-test-attributewarning

Suggests using PHPUnit's #[Test] attribute instead of the test method name prefix.

When a method name starts with test, it can be replaced with a #[Test] attribute and a shorter method name. This is the modern PHPUnit style (PHPUnit 10+).

Cette règle requiert que l'intégration PHPUnit soit activée.

À éviter
<?php

use PHPUnit\Framework\TestCase;

class UserTest extends TestCase
{
    public function testItReturnsFullName(): void {}
}
À privilégier
<?php

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;

class UserTest extends TestCase
{
    #[Test]
    public function itReturnsFullName(): void {}
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
psl-array-functionswarning

This rule enforces the usage of Psl array functions over their PHP counterparts. Psl array functions are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

$filtered = array_filter($xs, fn($x) => $x > 2);
À privilégier
<?php

$filtered = Psl\Vec\filter($xs, fn($x) => $x > 2);

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
psl-data-structureswarning

This rule enforces the usage of Psl data structures over their SPL counterparts.

Psl data structures are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

declare(strict_types=1);

$stack = new SplStack();
À privilégier
<?php

declare(strict_types=1);

use Psl\DataStructure\Stack;

$stack = new Stack();

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
psl-datetimewarning

This rule enforces the usage of Psl DateTime classes and functions over their PHP counterparts.

Psl DateTime classes and functions are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

$dateTime = new DateTime();
À privilégier
<?php

$dateTime = new Psl\DateTime\DateTime();

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
psl-math-functionswarning

This rule enforces the usage of Psl math functions over their PHP counterparts. Psl math functions are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

$abs = abs($number);
À privilégier
<?php

$abs = Psl\Math\abs($number);

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
psl-randomness-functionswarning

This rule enforces the usage of Psl randomness functions over their PHP counterparts.

Psl randomness functions are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

$randomInt = random_int(0, 10);
À privilégier
<?php

$randomInt = Psl\SecureRandom\int(0, 10);

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
psl-regex-functionswarning

This rule enforces the usage of Psl regex functions over their PHP counterparts.

Psl regex functions are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

$result = preg_match('/\w+/', 'Hello, World!');
À privilégier
<?php

$result = Psl\Regex\matches('Hello, World!', '/\w+/');

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
psl-sleep-functionswarning

This rule enforces the usage of Psl sleep functions over their PHP counterparts.

Psl sleep functions are preferred because they are type-safe, provide more consistent behavior, and allow other tasks within the event loop to continue executing while the current Fiber pauses.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

sleep(1);
À privilégier
<?php

use Psl\Async;
use Psl\DateTime;

Async\sleep(DateTime\Duration::seconds(1));

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
psl-string-functionswarning

This rule enforces the usage of Psl string functions over their PHP counterparts.

Psl string functions are preferred because they are type-safe and provide more consistent behavior.

Cette règle requiert que l'intégration Psl soit activée.

À éviter
<?php

$capitalized = ucfirst($string);
À privilégier
<?php

$capitalized = Psl\Str\capitalize($string);

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
require-namespacewarning

Detects files that contain definitions (classes, interfaces, enums, traits, functions, or constants) but do not declare a namespace. Using namespaces helps avoid naming conflicts and improves code organization.


À éviter
<?php

class Foo {}
À privilégier
<?php

namespace App;

class Foo {}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
single-class-per-filewarning

Ensures that each file contains at most one class-like definition (class, interface, enum, or trait).


À éviter
<?php

namespace App;

class Foo
{
}

class Bar
{
}
À privilégier
<?php

namespace App;

class Foo
{
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
use-wp-functionswarning

This rule encourages using WordPress's wrapper functions instead of native PHP functions for common tasks like HTTP requests, filesystem operations, and data handling. The WordPress APIs provide a consistent, secure, and reliable abstraction that works across different hosting environments.

Cette règle requiert que l'intégration WordPress soit activée.

À éviter
<?php

// For remote requests:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://example.com/api/data');
// ...

// For filesystem operations:
file_put_contents('/path/to/my-file.txt', 'data');
À privilégier
<?php

// For remote requests:
$response = wp_remote_get('https://example.com/api/data');

// For filesystem operations:
global $wp_filesystem;
require_once ABSPATH . 'wp-admin/includes/file.php';
WP_Filesystem();
$wp_filesystem->put_contents( '/path/to/my-file.txt', 'data' );

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
prefer-interfacenote

Detects when an implementation class is used instead of the interface.

Cette règle requiert que l'intégration Symfony soit activée.

À éviter
<?php

use Symfony\Component\Serializer\Serializer;

class UserController
{
    public function __construct(Serializer $serializer)
    {
        $this->serializer = $serializer;
    }
}
À privilégier
<?php

use Symfony\Component\Serializer\SerializerInterface;

class UserController
{
    public function __construct(SerializerInterface $serializer)
    {
        $this->serializer = $serializer;
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
prefer-while-loopnote

Suggests using a while loop instead of a for loop when the for loop does not have any initializations or increments. This can make the code more readable and concise.


À éviter
<?php

for (; $i < 10;) {
    echo $i;

    $i++;
}
À privilégier
<?php

while ($i < 10) {
    echo $i;

    $i++;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
prefer-arrow-functionhelp

Promotes the use of arrow functions (fn() => ...) over traditional closures (function() { ... }).

This rule identifies closures that consist solely of a single return statement and suggests converting them to arrow functions.

Cette règle requiert PHP 7.4.0 ou supérieur.

À éviter
<?php

$a = function($x) {
    return $x + 1;
};
À privilégier
<?php

$a = fn($x) => $x + 1;

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
prefer-early-continuehelp

Suggests using early continue pattern when a loop body contains only a single if statement.

This improves code readability by reducing nesting and making the control flow more explicit.


À éviter
<?php

for ($i = 0; $i < 10; $i++) {
    if ($condition) {
        doSomething();
    }
}
À privilégier
<?php

for ($i = 0; $i < 10; $i++) {
    if (!$condition) {
        continue;
    }
    doSomething();
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
max_allowed_statementsnumber0
prefer-pre-incrementhelp

Enforces the use of pre-increment (++$i) and pre-decrement (--$i) over post-increment ($i++) and post-decrement ($i--).

Pre-increment is marginally more efficient and is the convention used by the Symfony coding standards.

Cette règle requiert que l'intégration Symfony soit activée.

À éviter
<?php

$i++;
$count--;
À privilégier
<?php

++$i;
--$count;

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
prefer-self-return-typehelp

Suggests using self when a method's return type refers to its own enclosing class by name.

Using self decouples the signature from the class name, so renaming the class doesn't require updating return types. It also communicates intent more clearly: 'this returns an instance of the same class'.

Note: this rule does not apply to traits, because self inside a trait resolves to the using class, not the trait itself. If you want to return a subclass in inheritance-aware factory patterns, use static instead of self.


À éviter
<?php

final class Box
{
    public static function create(): Box
    {
        return new Box();
    }
}
À privilégier
<?php

final class Box
{
    public static function create(): self
    {
        return new self();
    }
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
prefer-static-closurehelp

Suggests adding the static keyword to closures and arrow functions that don't use $this.

Static closures don't bind $this, making them more memory-efficient and their intent clearer.


À éviter
<?php

class Foo {
    public function bar() {
        // Missing static - doesn't use $this
        $fn = fn($x) => $x * 2;

        // Missing static - doesn't use $this
        $closure = function($x) {
            return $x * 2;
        };
    }
}
À privilégier
<?php

class Foo {
    public function bar() {
        // Static closure - doesn't use $this
        $fn = static fn($x) => $x * 2;

        // Non-static - uses $this
        $fn2 = fn() => $this->doSomething();

        // Static function - doesn't use $this
        $closure = static function($x) {
            return $x * 2;
        };
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
prefer-view-arrayhelp

Prefer passing data to views using the array parameter in the view() function, rather than chaining the with() method.`

Using the array parameter directly is more concise and readable.

Cette règle requiert que l'intégration Laravel soit activée.

À éviter
<?php

return view('user.profile')->with([
    'user' => $user,
    'profile' => $profile,
]);
À privilégier
<?php

return view('user.profile', [
    'user' => $user,
    'profile' => $profile,
]);

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
sorted-integer-keyshelp

Detects array literals with integer keys that are not in ascending order.

PHP internally uses a "packed array" optimization for arrays with integer keys in natural ascending order, which consumes significantly less memory and is faster. When integer keys are out of order, PHP falls back to a regular hash table.


À éviter
<?php

$weights = [
    3  => 0.506,
    5  => 0.21246,
    10 => 0.10823,
    20 => 0.06206,
    2  => 0.06011,
    4  => 0.01233,
];
À privilégier
<?php

$weights = [
    2  => 0.06011,
    3  => 0.506,
    4  => 0.01233,
    5  => 0.21246,
    10 => 0.10823,
    20 => 0.06206,
];

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
use-compound-assignmenthelp

Enforces the use of compound assignment operators (e.g., +=, .=) over their more verbose equivalents ($var = $var + ...).

Using compound assignments is more concise and idiomatic. For string concatenation (.=), it can also be more performant as it avoids creating an intermediate copy of the string.


À éviter
<?php

$count = $count + 1;
$message = $message . ' Hello';
À privilégier
<?php

$count += 1;
$message .= ' Hello';

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
yoda-conditionshelp

This rule enforces the use of "Yoda" conditions for comparisons. The variable should always be on the right side of the comparison, while the constant, literal, or function call is on the left. This prevents the common bug of accidentally using an assignment (=) instead of a comparison (==), which would cause a fatal error in a Yoda condition instead of a silent logical bug.


À éviter
<?php

// Vulnerable to the accidental assignment bug, e.g., if ($is_active = true).
if ( $is_active === true ) { /* ... */ }
À privilégier
<?php

if ( true === $is_active ) { /* ... */ }
if ( 5 === $count ) { /* ... */ }

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"

Cohérence

Uniformité stylistique sur l'ensemble du code. Choisissez une façon de faire, ces règles aident tout le monde à s'y tenir.

assertion-stylewarning

Enforces a consistent style for PHPUnit assertion calls within test methods.

Maintaining a consistent style (e.g., always using static:: or $this->) improves code readability and helps enforce team-wide coding standards in test suites. This rule can be configured to enforce the preferred style.

Cette règle requiert que l'intégration PHPUnit soit activée.

À éviter
<?php
// configured style: "static"
final class SomeTest extends TestCase
{
    public function testSomething(): void
    {
        $this->assertTrue(true); // Incorrect style
        self::assertFalse(false); // Incorrect style
    }
}
À privilégier
<?php
// configured style: "static"
final class SomeTest extends TestCase
{
    public function testSomething(): void
    {
        static::assertTrue(true);
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
stylestring"static"
file-namewarning

Ensures that a file containing a single class-like definition is named after that definition.

For example, a file containing class Foo must be named Foo.php. Optionally, this rule can also check functions: a file containing a single function foo must be named foo.php.


À éviter
<?php
// File: test.php

namespace App;

class Foo
{
}
À privilégier
<?php
// File: test.php

namespace App;

class test
{
}

OptionTypeDéfaut
check-functionsbooleanfalse
enabledbooleantrue
levelstring"warning"
no-alternative-syntaxwarning

Detects the use of alternative syntax for control structures (endif, endwhile, endfor, endforeach, endswitch).

The brace-style syntax is preferred for consistency with the rest of the codebase and is the convention used by the Symfony coding standards.


À éviter
<?php

if ($condition):
    echo 'yes';
endif;
À privilégier
<?php

if ($condition) {
    echo 'yes';
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-hash-commentwarning

Detects shell-style comments ('#') in PHP code. Double slash comments ('//') are preferred in PHP, as they are more consistent with the language's syntax and are easier to read.


À éviter
<?php

# This is a shell-style comment.
À privilégier
<?php

// This is a good comment.

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
array-stylenote

Suggests using the short array style [..] instead of the long array style array(..), or vice versa, depending on the configuration. The short array style is more concise and is the preferred way to define arrays in PHP.


À éviter
<?php

// By default, 'short' is enforced, so array(...) triggers a warning:
$arr = array(1, 2, 3);
À privilégier
<?php

// By default, `style` is 'short', so this snippet is valid:
$arr = [1, 2, 3];

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
stylestring"short"
block-statementnote

Enforces that if, else, for, foreach, while, do-while statements always use a block statement body ({ ... }) even if they contain only a single statement.

This improves readability and prevents potential errors when adding new statements.


À éviter
<?php

if (true)
    echo "Hello";

for ($i = 0; $i < 10; $i++)
    echo $i;
À privilégier
<?php

if (true) {
    echo "Hello";
}

for ($i = 0; $i < 10; $i++) {
    echo $i;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
braced-string-interpolationnote

Enforces the use of curly braces around variables within string interpolation.

Using curly braces ({$variable}) within interpolated strings ensures clarity and avoids potential ambiguity, especially when variables are followed by alphanumeric characters. This rule promotes consistent and predictable code.


À éviter
<?php

$a = "Hello, $name!";
$b = "Hello, ${name}!";
$c = "Hello, ${$name}!";
$d = "Hello, ${$object->getMethod()}!";
À privilégier
<?php

$a = "Hello, {$name}!";
$b = "Hello, {$name}!";
$c = "Hello, {$$name}!";
$d = "Hello, {${$object->getMethod()}}!";

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
no-alias-functionnote

Detects usage of function aliases (e.g., diskfreespace instead of disk_free_space) and suggests calling the canonical (original) function name instead. This is primarily for consistency and clarity.


À éviter
<?php

// 'diskfreespace' is an alias for 'disk_free_space'
$freeSpace = diskfreespace("/");
À privilégier
<?php

// 'disk_free_space' is the proper name instead of 'diskfreespace'
$freeSpace = disk_free_space("/");

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
no-php-tag-terminatornote

Discourages the use of ?><?php as a statement terminator. Recommends using a semicolon (;) instead for clarity and consistency.


À éviter
<?php

echo "Hello World" ?><?php
À privilégier
<?php

echo "Hello World";

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
no-trailing-spacenote

Detects trailing whitespace at the end of comments. Trailing whitespace can cause unnecessary diffs and formatting issues, so it is recommended to remove it.


À éviter
<?php

// This is a comment with trailing whitespace.
À privilégier
<?php

// This is a good comment.

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
string-stylenote

Enforces a consistent string style: either prefer string interpolation over concatenation, or prefer concatenation over interpolation.

With style: interpolation (default), flags concatenation like "foo" . $a . "bar" and suggests "foo{$a}bar" instead.

With style: concatenation, flags interpolation like "foo{$a}bar" and suggests "foo" . $a . "bar" instead.

Only simple, interpolable expressions are considered: variables, property accesses, array accesses, and method calls. Concatenation involving function calls, static access, or complex expressions is never flagged.


À éviter
<?php

// With the default `style: interpolation`:
$a = "Hello, " . $name . "!";
$b = "value: " . $obj->name;
À privilégier
<?php

// With the default `style: interpolation`:
$a = "Hello, {$name}!";
$b = "value: {$obj->name}";

// Complex expressions stay as concatenation (never flagged):
$c = "result: " . strtolower($input);
$d = "class: " . Foo::class;

OptionTypeDéfaut
enabledbooleanfalse
levelstring"note"
stylestring"interpolation"
ambiguous-constant-accesshelp

Enforces that all constant references made from within a namespace are explicit.

When an unqualified constant like PHP_VERSION is referenced from within a namespace, PHP performs a runtime fallback check (current namespace -> global namespace). This ambiguity can lead to unexpected behavior if a constant with the same name is later defined in the namespace.

Making references explicit improves readability and prevents bugs.


À éviter
<?php

namespace App;

// Ambiguous: could be App\PHP_VERSION or \PHP_VERSION
$version = PHP_VERSION;
À privilégier
<?php

namespace App;

use const PHP_VERSION;

// OK: Explicitly imported
$version1 = PHP_VERSION;

// OK: Explicitly global
$version2 = \PHP_VERSION;

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
ambiguous-function-callhelp

Enforces that all function calls made from within a namespace are explicit.

When an unqualified function like strlen() is called from within a namespace, PHP performs a runtime fallback check (current namespace -> global namespace). This ambiguity prevents PHP from performing powerful compile-time optimizations, such as replacing a call to strlen() with the highly efficient STRLEN opcode.

Making calls explicit improves readability, prevents bugs, and allows for significant performance gains in some cases.


À éviter
<?php

namespace App;

// Ambiguous: could be App\strlen or \strlen
$length = strlen("hello");
À privilégier
<?php

namespace App;

use function strlen;

// OK: Explicitly imported
$length1 = strlen("hello");

// OK: Explicitly global
$length2 = \strlen("hello");

// OK: Explicitly namespaced
$value = namespace\my_function();

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
class-namehelp

Detects class declarations that do not follow class naming convention.

Class names should be in class case, also known as PascalCase.


À éviter
<?php

class my_class {}

class myClass {}

class MY_CLASS {}
À privilégier
<?php

class MyClass {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
psrbooleanfalse
constant-namehelp

Detects constant declarations that do not follow constant naming convention.

Constant names should be in constant case, also known as UPPER_SNAKE_CASE.


À éviter
<?php

const myConstant = 42;
const my_constant = 42;
const My_Constant = 42;

class MyClass {
    public const int myConstant = 42;
    public const int my_constant = 42;
    public const int My_Constant = 42;
}
À privilégier
<?php

const MY_CONSTANT = 42;

class MyClass {
    public const int MY_CONSTANT = 42;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
enum-namehelp

Detects enum declarations that do not follow class naming convention.

Enum names should be in class case, also known as PascalCase.


À éviter
<?php

enum my_enum {}
enum myEnum {}
enum MY_ENUM {}
À privilégier
<?php

enum MyEnum {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
function-namehelp

Detects function declarations that do not follow camel or snake naming convention.

Function names should be in camel case or snake case, depending on the configuration.


À éviter
<?php

function MyFunction() {}

function My_Function() {}
À privilégier
<?php

function my_function() {}

OptionTypeDéfaut
camelbooleanfalse
eitherbooleanfalse
enabledbooleantrue
levelstring"help"
interface-namehelp

Detects interface declarations that do not follow class naming convention.

Interface names should be in class case and suffixed with Interface, depending on the configuration.


À éviter
<?php

interface myInterface {}
interface my_interface {}
interface MY_INTERFACE {}
À privilégier
<?php

interface MyInterface {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
psrbooleanfalse
lowercase-keywordhelp

Enforces that PHP keywords (like if, else, return, function, etc.) be written in lowercase. Using uppercase or mixed case is discouraged for consistency and readability.

When the drupal integration is enabled, TRUE, FALSE, and NULL are exempted to match Drupal's coding standards (and the drupal formatter preset).


À éviter
<?PHP

IF (TRUE) {
    ECHO "Keywords not in lowercase";
} ELSE {
    RETURN;
}
À privilégier
<?php

if (true) {
    echo "All keywords in lowercase";
} else {
    return;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
lowercase-type-hinthelp

Enforces that PHP type hints (like void, bool, int, float, etc.) be written in lowercase. Using uppercase or mixed case is discouraged for consistency and readability.


À éviter
<?php

function example(Int $param): VOID {
    return;
}
À privilégier
<?php

function example(int $param): void {
    return;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
method-namehelp

Detects method declarations that do not follow the configured naming convention.

By default, method names should be in camelCase. Magic methods (prefixed with __) are always excluded.

The use-snake-case-for-tests option enforces snake_case for test methods (names starting with test), which is a common convention in PHPUnit.


À éviter
<?php

class Foo
{
    public function GetName(): string {}
    public function set_name(string $name): void {}
}
À privilégier
<?php

class Foo
{
    public function getName(): string {}
    public function setName(string $name): void {}
}

OptionTypeDéfaut
camelbooleantrue
eitherbooleanfalse
enabledbooleanfalse
levelstring"help"
use-snake-case-for-testsbooleanfalse
no-fully-qualified-global-class-likehelp

Disallows fully-qualified class-like references within a namespace.

Instead of using the backslash prefix (e.g., new \DateTime() or \Exception in a type hint), prefer an explicit use import statement. This improves readability and keeps imports centralized at the top of the file.


À éviter
<?php

namespace App;

$dt = new \DateTime();

function foo(\DateTime $dt): \Exception {}
À privilégier
<?php

namespace App;

use DateTime;
use Exception;

$dt = new DateTime();

function foo(DateTime $dt): Exception {}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
no-fully-qualified-global-constanthelp

Disallows fully-qualified references to global constants within a namespace.

Instead of using the backslash prefix (e.g., \PHP_VERSION), prefer an explicit use const import statement. This improves readability and keeps imports centralized at the top of the file.


À éviter
<?php

namespace App;

$version = \PHP_VERSION;
À privilégier
<?php

namespace App;

use const PHP_VERSION;

$version = PHP_VERSION;

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
no-fully-qualified-global-functionhelp

Disallows fully-qualified references to global functions within a namespace.

Instead of using the backslash prefix (e.g., \strlen()), prefer an explicit use function import statement. This improves readability and keeps imports centralized at the top of the file.


À éviter
<?php

namespace App;

$length = \strlen("hello");
À privilégier
<?php

namespace App;

use function strlen;

$length = strlen("hello");

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
property-namehelp

Detects class property declarations that do not follow camel or snake naming convention.

Property names should be in camel case or snake case, depending on the configuration.


À éviter
<?php

final class Foo {
    public string $My_Property;

    public function __construct(
        public int $My_Promoted_Property,
    ) {}
}
À privilégier
<?php

final class Foo {
    public string $myProperty;

    public function __construct(
        public int $myPromotedProperty,
    ) {}
}

OptionTypeDéfaut
camelbooleantrue
eitherbooleanfalse
enabledbooleanfalse
levelstring"help"
trait-namehelp

Detects trait declarations that do not follow class naming convention. Trait names should be in class case and suffixed with Trait, depending on the configuration.


À éviter
<?php

trait myTrait {}
trait my_trait {}
trait MY_TRAIT {}
À privilégier
<?php

trait MyTrait {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
psrbooleanfalse
variable-namehelp

Detects variable declarations that do not follow camel or snake naming convention.

Variable names should be in camel case or snake case, depending on the configuration.


À éviter
<?php

$MyVariable = 1;
$My_Variable = 2;

function foo($MyParam) {}
À privilégier
<?php

$my_variable = 1;

function foo($my_param) {}

OptionTypeDéfaut
camelbooleanfalse
check-parametersbooleantrue
eitherbooleantrue
enabledbooleanfalse
levelstring"help"

Obsolescence

Fonctionnalités et API PHP marquées comme obsolètes en amont, et qui finiront par être supprimées. Migrez avant qu'elles ne cassent.

deprecated-casterror

Detect the usage of deprecated type casts in PHP code.

In PHP 8.5, the following type casts have been deprecated:

  • (integer): The integer cast has been deprecated in favor of (int).
  • (boolean): The boolean cast has been deprecated in favor of (bool).
  • (double): The double cast has been deprecated in favor of (float).
  • (binary): The binary cast has been deprecated in favor of (string).
Cette règle requiert PHP 8.5.0 ou supérieur.

À éviter
<?php

(integer) $value;
À privilégier
<?php

(int) $value;

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
deprecated-shell-execute-stringerror

Detect the usage of deprecated shell execute strings in PHP code.

In PHP 8.5, the shell execute string syntax (enclosed in backticks, e.g., `ls -l`) has been deprecated.

This rule identifies instances of shell execute strings and provides guidance on how to replace them with safer alternatives, such as using the shell_exec() function or other appropriate methods for executing shell commands.

Cette règle requiert PHP 8.5.0 ou supérieur.

À éviter
<?php

`ls -l`;
À privilégier
<?php

shell_exec('ls -l');

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
deprecated-switch-semicolonerror

Detect the usage of semicolon as a switch case separator.

In PHP 8.5, the use of a semicolon (;) as a case separator in switch statements has been deprecated.

Instead, the colon (:) should be used to separate case statements.

Cette règle requiert PHP 8.5.0 ou supérieur.

À éviter
<?php

switch ($value) {
    case 1;
        // code for case 1
        break;
    case 2;
        // code for case 2
        break;
    default;
        // default case
        break;
}
À privilégier
<?php

switch ($value) {
    case 1:
        // code for case 1
        break;
    case 2:
        // code for case 2
        break;
    default:
        // default case
        break;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
explicit-nullable-paramwarning

Detects parameters that are implicitly nullable and rely on a deprecated feature.

Such parameters are considered deprecated; an explicit nullable type hint is recommended.

Cette règle requiert PHP 8.4.0 ou supérieur.

À éviter
<?php

function foo(string $param = null) {}

function bar(string $param = NULL) {}

function baz(object $param = null) {}
À privilégier
<?php

function foo(?string $param) {}

function bar(null|string $param) {}

function baz(null|object $param = null) {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-underscore-classwarning

Detects class, interface, trait, or enum declarations named _.

Such names are considered deprecated; a more descriptive identifier is recommended.

Cette règle requiert PHP 8.4.0 ou supérieur.

À éviter
<?php

class _ {}
À privilégier
<?php

class MyService {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-void-reference-returnwarning

Detects functions, methods, closures, arrow functions, and set property hooks that return by reference from a void function. Such functions are considered deprecated; returning by reference from a void function is deprecated since PHP 8.0.

Cette règle requiert PHP 8.2.0 ou supérieur.

À éviter
<?php

function &foo(): void {
    // ...
}
À privilégier
<?php

function &foo(): string {
    // ...
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
optional-param-orderwarning

Detects optional parameters defined before required parameters in function-like declarations. Such parameter order is considered deprecated; required parameters should precede optional parameters.

Cette règle requiert PHP 8.0.0 ou supérieur.

À éviter
<?php

function foo(?string $optional = null, string $required): void {}
À privilégier
<?php

function foo(string $required, ?string $optional = null): void {}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"

Maintenabilité

Du code difficile à maintenir dans la durée, trop complexe, trop emmêlé, trop fragile. Ces règles font remonter le coût tôt.

cyclomatic-complexityerror

Checks the cyclomatic complexity of classes, traits, enums, interfaces, functions, and closures.

Cyclomatic complexity is a measure of the number of linearly independent paths through a program's source code.


À éviter
<?php

function validateUser($user) {
    if (!isset($user['email'])) {
        return false;
    }

    if (!filter_var($user['email'], FILTER_VALIDATE_EMAIL)) {
        return false;
    }

    if (!isset($user['age'])) {
        return false;
    }

    if ($user['age'] < 18) {
        return false;
    }

    if ($user['age'] > 120) {
        return false;
    }

    if (!isset($user['name'])) {
        return false;
    }

    if (strlen($user['name']) < 2) {
        return false;
    }

    if (!isset($user['country'])) {
        return false;
    }

    if (!in_array($user['country'], ['US', 'UK', 'CA'])) {
        return false;
    }

    if (isset($user['phone'])) {
        if (!preg_match('/^\d{10}$/', $user['phone'])) {
            return false;
        }
    }

    if (isset($user['preferences'])) {
        if (is_array($user['preferences'])) {
            foreach ($user['preferences'] as $key => $value) {
                if ($key === 'newsletter') {
                    if ($value !== true && $value !== false) {
                        return false;
                    }
                }
            }
        }
    }

    if (isset($user['address'])) {
        if (!isset($user['address']['street'])) {
            return false;
        }
        if (!isset($user['address']['city'])) {
            return false;
        }
    }

    return true;
}
À privilégier
<?php

function validateUser($user) {
    if (!isValidEmail($user['email'])) {
        return false;
    }

    if (!isValidAge($user['age'])) {
        return false;
    }

    if (!hasRequiredFields($user)) {
        return false;
    }

    return true;
}

function isValidEmail($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

function isValidAge($age) {
    return $age >= 18 && $age <= 120;
}

function hasRequiredFields($user) {
    return isset($user['name']) && isset($user['email']);
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
method-thresholdnullnull
thresholdnumber15
excessive-parameter-listerror

Detects functions, closures, and methods with too many parameters.

If the number of parameters exceeds a configurable threshold, an issue is reported.


À éviter
<?php

function createUser($name, $email, $password, $age, $country, $city, $zipCode) {
    return true;
}
À privilégier
<?php

function processOrder($orderId, $userId, $total, $status, $date) {
    return true;
}

OptionTypeDéfaut
constructor-thresholdnullnull
enabledbooleantrue
levelstring"error"
thresholdnumber5
kan-defecterror

Detects classes, traits, interfaces, functions, and closures with high kan defect.

The "Kan Defect" metric is a heuristic for estimating defect proneness in a class or similar structure. It counts control-flow statements (while, do, foreach, if, and switch) and sums them using a formula loosely based on the work of Stephen H. Kan.

References:

  • https://github.com/phpmetrics/PhpMetrics/blob/c43217cd7783bbd54d0b8c1dd43f697bc36ef79d/src/Hal/Metric/Class_/Complexity/KanDefectVisitor.php
  • https://phpmetrics.org/

À éviter
<?php

function handleRequest($request) {
    if (empty($request)) {
        return null;
    }

    if (!isset($request['type'])) {
        return null;
    }

    switch ($request['type']) {
        case 'create':
            if (!isset($request['data'])) {
                return null;
            }
            break;
        case 'update':
            if (!isset($request['id'])) {
                return null;
            }
            break;
        case 'delete':
            if (!isset($request['id'])) {
                return null;
            }
            break;
    }

    if (isset($request['filters'])) {
        foreach ($request['filters'] as $key => $value) {
            switch ($key) {
                case 'status':
                    if ($value === 'active') {
                        // filter
                    }
                    break;
                case 'category':
                    if (!empty($value)) {
                        // filter
                    }
                    break;
            }
        }
    }

    while (!empty($request['items'])) {
        $item = array_shift($request['items']);
        if ($item['valid']) {
            foreach ($item['tags'] as $tag) {
                if ($tag === 'important') {
                    // process
                }
            }
        }
    }

    foreach ($request['metadata'] as $meta) {
        switch ($meta['type']) {
            case 'timestamp':
                break;
            case 'user':
                break;
        }
    }

    return ['status' => 'success'];
}
À privilégier
<?php

function handleRequest($request) {
    $validated = validateRequest($request);
    $processed = processRequest($validated);
    return formatResponse($processed);
}

function validateRequest($request) {
    if (empty($request['type'])) {
        return null;
    }
    return $request;
}

function processRequest($request) {
    return match($request['type']) {
        'create' => createResource($request),
        'update' => updateResource($request),
        'delete' => deleteResource($request),
        default => null
    };
}

function formatResponse($data) {
    return ['status' => 'success', 'data' => $data];
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
thresholdnumber1.6
too-many-enum-caseserror

Detects enums with too many cases.

This rule checks the number of cases in enums. If the number of cases exceeds a configurable threshold, an issue is reported.


À éviter
<?php

enum LargeEnum {
    case A;
    case B;
    case C;
    case D;
    case E;
    case F;
    case G;
    case H;
    case I;
    case J;
    case K;
    case L;
    case M;
    case N;
    case O;
    case P;
    case Q;
    case R;
    case S;
    case T;
    case U;
}
À privilégier
<?php

enum SimpleEnum {
    case A;
    case B;
    case C;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
thresholdnumber20
too-many-methodserror

Detects class-like structures with too many methods.

This rule checks the number of methods in classes, traits, enums, and interfaces. If the number of methods exceeds a configurable threshold, an issue is reported.


À éviter
<?php

class ComplexClass {
    public function a() {}
    public function b() {}
    public function c() {}
    public function d() {}
    public function e() {}
    public function f() {}
    public function g() {}
    public function h() {}
    public function i() {}
    public function j() {}
    public function k() {}
    public function l() {}
    public function m() {}
    public function n() {}
    public function o() {}
    public function p() {}
    public function q() {}
    public function r() {}
    public function s() {}
    public function t() {}
    public function u() {}
}
À privilégier
<?php

class SimpleClass {
    public function a() {}
    public function b() {}
}

OptionTypeDéfaut
count-hooksbooleanfalse
count-setters-and-gettersbooleanfalse
enabledbooleantrue
levelstring"error"
thresholdnumber10
too-many-propertieserror

Detects class-like structures with too many properties.

This rule checks the number of properties in classes, traits, and interfaces. If the number of properties exceeds a configurable threshold, an issue is reported.


À éviter
<?php

class ComplexClass {
    public $a; public $b; public $c; public $d; public $e;
    public $f; public $g; public $h; public $i; public $j; public $k;
}
À privilégier
<?php

class SimpleClass {
    public $a;
    public $b;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
thresholdnumber10
excessive-nestingwarning

Checks if the nesting level in any block exceeds a configurable threshold.

Deeply nested code is harder to read, understand, and maintain. Consider refactoring to use early returns, helper methods, or clearer control flow.

The function-like-threshold option allows setting a separate, typically lower, threshold for individual functions, methods, closures, and property hooks.


À éviter
<?php

if ($a) {
    if ($b) {
        if ($c) {
            if ($d) {
                if ($e) {
                    if ($f) {
                        if ($g) {
                            if ($h) {
                                echo "Too deeply nested!";
                            }
                        }
                    }
                }
            }
        }
    }
}
À privilégier
<?php

if ($condition) {
    while ($otherCondition) {
        echo "Hello"; // nesting depth = 2
    }
}

OptionTypeDéfaut
enabledbooleantrue
function-like-thresholdnullnull
levelstring"warning"
thresholdnumber7
halsteadwarning

Computes Halstead metrics (volume, difficulty, effort) and reports if they exceed configured thresholds.

Halstead metrics are calculated by counting operators and operands in the analyzed code. For more info: https://en.wikipedia.org/wiki/Halstead_complexity_measures


À éviter
<?php

function processOrderData($orders) {
    $result = [];
    $total1 = 0;
    $total2 = 0;
    $total3 = 0;
    $discount1 = 0;
    $discount2 = 0;
    $discount3 = 0;
    $count1 = 0;
    $count2 = 0;
    $count3 = 0;
    $sum1 = 0;
    $sum2 = 0;
    $sum3 = 0;

    for ($i = 0; $i < count($orders); $i++) {
        $order = $orders[$i];
        if ($order['status'] === 'pending') {
            $price = $order['price'];
            $quantity = $order['quantity'];
            $subtotal = $price * $quantity;
            $total1 = $total1 + $subtotal;

            if ($subtotal > 100) {
                $discount1 = $subtotal * 0.1;
                $total1 = $total1 - $discount1;
                $count1 = $count1 + 1;
            }

            if ($subtotal > 200) {
                $discount2 = $subtotal * 0.15;
                $total2 = $total2 + $subtotal - $discount2;
                $count2 = $count2 + 1;
            }

            if ($subtotal > 300) {
                $discount3 = $subtotal * 0.2;
                $total3 = $total3 + $subtotal - $discount3;
                $count3 = $count3 + 1;
            }

            $sum1 = $sum1 + $price;
            $sum2 = $sum2 + $quantity;
            $sum3 = $sum3 + $subtotal;

            for ($j = 0; $j < $quantity; $j++) {
                $itemCost = $price / $quantity;
                $taxRate = 0.08;
                $tax = $itemCost * $taxRate;
                $finalCost = $itemCost + $tax;
                $sum1 = $sum1 + $finalCost;

                if ($finalCost > 50) {
                    $extraDiscount = $finalCost * 0.05;
                    $sum2 = $sum2 + $extraDiscount;
                }
            }
        }
    }

    $result['total1'] = $total1;
    $result['total2'] = $total2;
    $result['total3'] = $total3;
    $result['count1'] = $count1;
    $result['count2'] = $count2;
    $result['count3'] = $count3;
    $result['sum1'] = $sum1;
    $result['sum2'] = $sum2;
    $result['sum3'] = $sum3;

    return $result;
}
À privilégier
<?php

function processOrderData($orders) {
    $filtered = filterValidOrders($orders);
    $totals = calculateTotals($filtered);
    return applyDiscounts($totals);
}

function filterValidOrders($orders) {
    return array_filter($orders, fn($order) => $order['status'] === 'valid');
}

function calculateTotals($orders) {
    return array_map(fn($order) => $order['price'] * $order['quantity'], $orders);
}

function applyDiscounts($totals) {
    return array_map(fn($total) => $total > 100 ? $total * 0.9 : $total, $totals);
}

OptionTypeDéfaut
difficulty-thresholdnumber12.0
effort-thresholdnumber5000.0
enabledbooleantrue
levelstring"warning"
volume-thresholdnumber1000.0
no-gotonote

Detects the use of goto statements and labels. The goto statement can make code harder to read, understand, and maintain. It can lead to "spaghetti code" and make it difficult to follow the flow of execution.


À éviter
<?php

$i = 0;
loop:
if ($i >= 10) {
    goto end;
}

$i++;
goto loop;
end:
À privilégier
<?php

$i = 0;
while ($i < 10) {
    if ($i === 5) {
        break; // Structured control flow.
    }
    $i++;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
no-boolean-flag-parameterhelp

Flags function-like parameters that use a boolean type.

Boolean flag parameters can indicate a violation of the Single Responsibility Principle (SRP). Refactor by extracting the flag logic into its own class or method.


À éviter
<?php

function get_difference(string $a, string $b, bool $ignore_case): string {
    // ...
}
À privilégier
<?php

function get_difference(string $a, string $b): string {
    // ...
}

function get_difference_case_insensitive(string $a, string $b): string {
    // ...
}

OptionTypeDéfaut
enabledbooleantrue
exclude-constructorsbooleantrue
exclude-settersbooleanfalse
levelstring"help"
no-else-clausehelp

Flags if statements that include an else or elseif branch.

Using else or elseif can lead to deeply nested code and complex control flow. This can often be simplified by using early returns (guard clauses), which makes the code easier to read and maintain by reducing its cyclomatic complexity.


À éviter
<?php

function process($user) {
    if ($user->isVerified()) {
        // "Happy path" is nested
        $user->login();
    } else {
        // Logic is split across branches
        return;
    }
}
À privilégier
<?php

function process($user) {
    if (!$user->isVerified()) {
        return; // Early return
    }

    // "Happy path" continues here
    $user->login();
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"

Redondance

Code mort, valeurs inutilisées, constructions sans effet observable. Les retirer garde le code honnête.

inline-variable-returnwarning

Detects unnecessary variable assignments immediately before returning the variable.

When a variable is only used once right after being assigned, the assignment can be inlined into the return statement.


À éviter
<?php

function getValue() {
    $result = computeResult();
    return $result;
}

function getArray() {
    $arr = [1, 2, 3];
    return $arr;
}
À privilégier
<?php

function getValue() {
    return computeResult();
}

function process() {
    $result = computeResult();
    log($result);
    return $result;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-iterator-to-array-in-foreachwarning

Detects iterator_to_array() calls used directly as a foreach expression.

Since foreach natively supports any Traversable, wrapping an iterator in iterator_to_array() is redundant and causes unnecessary memory allocation.


À éviter
<?php

foreach (iterator_to_array($iterator) as $value) {
    // ...
}
À privilégier
<?php

foreach ($iterator as $value) {
    // ...
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-redundant-literal-returnwarning

Detects redundant literal guard patterns where an if statement checks if a variable equals a literal and returns that same literal, followed by returning the variable.

This pattern is redundant because if the variable equals the literal, returning the variable would return the same value anyway.

This includes patterns with else clauses and elseif chains where all branches follow the same redundant pattern.


À éviter
<?php

function getValue($x) {
    if ($x === null) {
        return null;
    }
    return $x;
}

function getWithElse($x) {
    if ($x === null) {
        return null;
    } else {
        return $x;
    }
}

function getWithElseIf($x) {
    if ($x === null) {
        return null;
    } elseif ($x === '') {
        return '';
    }
    return $x;
}
À privilégier
<?php

function getValue($x) {
    return $x;
}

function getValueOrDefault($x, $default) {
    if ($x === null) {
        return $default;
    }
    return $x;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-redundant-usewarning

Detects use statements that import items that are never used or are redundant because they import from the same namespace.


À éviter
<?php
namespace App;

use App\Helpers\ArrayHelper;
use App\Helpers\StringHelper; // StringHelper is not used.

$result = ArrayHelper::combine([]);
À privilégier
<?php
namespace App;

use App\Helpers\ArrayHelper;

$result = ArrayHelper::combine([]);

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-self-assignmentwarning

Detects and removes self-assignments where a variable or property is assigned to itself.

Self-assignments have no effect and are typically mistakes or leftover from refactoring. For object properties, the fix is marked as potentially unsafe because reading or writing properties may have side effects through magic methods (__get, __set) or property hooks (PHP 8.4+).


À éviter
<?php

$a = $a;
$this->x = $this->x;
$foo->bar = $foo->bar;
À privilégier
<?php

$a = $b;
$this->x = $other->x;
$foo->bar = $baz->bar;

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-empty-commentnote

Detects empty comments in the codebase. Empty comments are not useful and should be removed to keep the codebase clean and maintainable.


À éviter
<?php

//
#
/**/
À privilégier
<?php

// This is a useful comment.
//
// And so is this whole single line comment block, including the enclosed empty line.
# This is also a useful comment.
/**
 * This is a docblock.
 */

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
preserve-single-line-commentsbooleanfalse
no-empty-loopnote

Detects loops (for, foreach, while, do-while) that have an empty body. An empty loop body does not perform any actions and is likely a mistake or redundant code.


À éviter
<?php

while (should_wait()) {
    // Empty loop body
}
À privilégier
<?php

foreach ($items as $item) {
    process($item);
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"
no-is-nullnote

Detects usage of the is_null() function and suggests using a strict === null comparison instead.

The is_null() function is redundant because === null achieves the same result with clearer intent and without the overhead of a function call.


À éviter
<?php

if (is_null($value)) {
    // ...
}
À privilégier
<?php

if ($value === null) {
    // ...
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"note"
constant-conditionhelp

Detects if statements where the condition is a constant that always evaluates to true or false.

Such statements are redundant. If the condition is always true, the if wrapper is unnecessary. If it's always false, the enclosed code is dead and can be removed or refactored.


À éviter
<?php
if (true) {
    echo "This will always run";
}

if (false) {
    echo "This is dead code";
}
À privilégier
<?php
if ($variable > 10) {
    echo "Greater than 10";
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-closing-taghelp

Detects redundant closing tags ( ?> ) at the end of a file.


À éviter
<?php

echo "Hello, world!";

?>
À privilégier
<?php

echo "Hello, world!";

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-noophelp

Detects redundant noop statements.


À éviter
<?php

;
À privilégier
<?php

echo "Hello, world!";

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-null-property-inithelp

Detects redundant = null initialization on untyped properties.

Untyped properties already default to null, making an explicit = null initializer unnecessary.


À éviter
<?php

class Foo {
    public $name = null;
}
À privilégier
<?php

class Foo {
    public $name;
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
no-protected-in-finalhelp

Detects protected items in final classes or enums.


À éviter
<?php

final class Foo {
    protected string $foo;
    protected(set) string $bar;
    protected private(set) string $baz;

    protected function fun(): void {
        // ...
    }
}
À privilégier
<?php

final class Foo {
    private string $foo;
    private(set) string $bar;
    private string $baz;

    private function fun(): void {
        // ...
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-binary-string-prefixhelp

Detects the redundant b/B prefix on string literals. The binary string prefix has no effect in PHP and can be safely removed.


À éviter
<?php

$foo = b'hello';
$bar = b"world";
À privilégier
<?php

$foo = 'hello';
$bar = "world";

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-blockhelp

Detects redundant blocks around statements.


À éviter
<?php

{
    echo "Hello, world!";
}
À privilégier
<?php

echo "Hello, world!";

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-continuehelp

Detects redundant continue statements in loops.


À éviter
<?php

while (true) {
    echo "Hello, world!";
    continue; // Redundant `continue` statement
}
À privilégier
<?php

while (true) {
    echo "Hello, world!";
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-elsehelp

Flags if/else statements where the if branch always terminates control flow (via return, throw, exit, die, continue, or break).

When the if branch unconditionally terminates, the else branch becomes unnecessary nesting. Extracting the else body to follow the if flattens the control flow without changing semantics.


À éviter
<?php

function process($user) {
    if (!$user->isVerified()) {
        return;
    } else {
        $user->login();
    }
}
À privilégier
<?php

function process($user) {
    if (!$user->isVerified()) {
        return;
    }

    $user->login();
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"help"
no-redundant-filehelp

Detects redundant files that contain no executable code or declarations.


À éviter
<?php

declare(strict_types=1);
// This file is redundant.
À privilégier
<?php

declare(strict_types=1);

function foo(): void {
    return 42;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-finalhelp

Detects redundant final modifiers on methods in final classes or enum methods.


À éviter
<?php

final class Foo {
    final public function bar(): void {
        // ...
    }
}
À privilégier
<?php

final class Foo {
    public function bar(): void {
        // ...
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-issethelp

Detects redundant arguments in isset() calls where a nested access already implies the parent checks.

For example, isset($d, $d['first'], $d['first']['second']) can be simplified to isset($d['first']['second']) because checking a nested array access or property access implicitly verifies that all parent levels exist.


À éviter
<?php

if (isset($d, $d['first'], $d['first']['second'])) {
    echo 'all present';
}
À privilégier
<?php

if (isset($d['first']['second'])) {
    echo 'all present';
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-labelhelp

Detects redundant goto labels that are declared but not used.


À éviter
<?php

label:
echo "Hello, world!";
À privilégier
<?php

goto end;
echo "Hello, world!";
end:

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-mathhelp

Detects redundant mathematical operations that can be simplified or removed. Includes operations like multiplying by 1/-1, adding 0, modulo 1/-1, etc.


À éviter
<?php

$result = $value * 1;
$sum = 0 + $total;
$difference = $value - 0;
$remainder = $x % 1;
$negative = $value * -1;
À privilégier
<?php

$result = $value * 2;
$sum = 1 + $total;
$difference = $value - 1;
$remainder = $x % 2;

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-method-overridehelp

Detects methods that override a parent method but only call the parent method with the same arguments.


À éviter
<?php

class Parent
{
    public function foo(): void
    {
        // ...
    }
}

class Child extends Parent
{
    public function foo(): void
    {
        parent::foo();
    }
}
À privilégier
<?php

class Parent
{
    public function foo(): void
    {
        // ...
    }
}

class Child extends Parent
{
    public function foo(): void
    {
        parent::foo();

        echo 'Additional logic here';
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-nullsafehelp

Flags the use of the nullsafe operator (?->) in contexts where its null-checking behavior is redundant.

This occurs in two common situations:

  1. When an expression using ?-> is immediately followed by the null coalescing operator (??).
  2. When an expression using ?-> is checked with isset().

In both scenarios, the surrounding language construct (?? or isset()) already handles null values safely, making the ?-> operator superfluous and the code unnecessarily verbose.


À éviter
<?php

$name = $user?->name ?? 'Guest';

if (isset($user?->profile)) {
    // Do something with $user->profile
}
À privilégier
<?php

$name = $user->name ?? 'Guest';

if (isset($user->profile)) {
    // Do something with $user->profile
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-parentheseshelp

Detects redundant parentheses around expressions.


À éviter
<?php

$foo = (42);
À privilégier
<?php

$foo = 42;

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-readonlyhelp

Detects redundant readonly modifiers on properties.


À éviter
<?php

readonly class User
{
    public readonly $name;
}
À privilégier
<?php

readonly class User
{
    public $name;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-string-concathelp

Detects redundant string concatenation expressions.


À éviter
<?php

$foo = "Hello" . " World";
À privilégier
<?php

$foo = "Hello World";

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-write-visibilityhelp

Detects redundant write visibility modifiers on properties.


À éviter
<?php

final class User
{
    public public(set) $name;
}
À privilégier
<?php

final class User
{
    public $name;
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"
no-redundant-yield-fromhelp

Detects redundant use of yield from with single-element array literals.

Using yield from with a single-element array literal creates unnecessary overhead in the generated opcodes. Direct yield is simpler and more efficient.


À éviter
<?php

function gen(): Generator {
    yield from [1];
    yield from ['foo' => new stdClass()];
}
À privilégier
<?php

function gen(): Generator {
    yield 1;
    yield 'foo' => new stdClass();
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"help"

Sécurité

Règles qui signalent des vulnérabilités, vecteurs d'injection, désérialisation non sûre, données non fiables atteignant des points dangereux.

no-db-schema-changeerror

This rule flags any attempt to alter the database schema (using CREATE, ALTER, or DROP) within a $wpdb call. Schema modifications must only occur within a plugin activation hook to prevent catastrophic performance issues and data corruption.

Cette règle requiert que l'intégration WordPress soit activée.

À éviter
<?php

// This schema change runs on every page load, which is very dangerous.
global $wpdb;
$wpdb->query("ALTER TABLE {$wpdb->posts} ADD my_column VARCHAR(255)");
À privilégier
<?php

function my_plugin_activate() {
    global $wpdb;

    // Running schema changes inside an activation hook is safe.
    $wpdb->query("ALTER TABLE {$wpdb->posts} ADD my_column VARCHAR(255)");
}

register_activation_hook(__FILE__, 'my_plugin_activate');

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-insecure-comparisonerror

Detects insecure comparison of passwords or tokens using ==, !=, ===, or !==.

These operators are vulnerable to timing attacks, which can expose sensitive information. Instead, use hash_equals for comparing strings or password_verify for validating hashes.


À éviter
<?php

if ($storedToken == $userToken) {
    // Vulnerable to timing attacks
}
À privilégier
<?php

if (hash_equals($storedToken, $userToken)) {
    // Valid token
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-literal-passworderror

Detects the use of literal values for passwords or sensitive data. Storing passwords or sensitive information as literals in code is a security risk and should be avoided. Use environment variables or secure configuration management instead.


À éviter
<?php

$password = "supersecret";
À privilégier
<?php

$password = getenv('DB_PASSWORD');

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-unescaped-outputerror

This rule ensures that any variable or function call that is output directly to the page is properly escaped. All data must be escaped before printing to prevent Cross-Site Scripting (XSS) vulnerabilities.

Cette règle requiert que l'intégration WordPress soit activée.

À éviter
<?php

// This is a major XSS vulnerability.
echo $_GET['user_comment'];
À privilégier
<?php

echo esc_html( $user_comment );
?>
<a href="<?php echo esc_url( $user_provided_url ); ?>">Link</a>

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
sensitive-parametererror

Requires that parameters that are likely to contain sensitive information (e.g., passwords) are marked with the #[SensitiveParameter] attribute to prevent accidental logging or exposure.

This rule only applies to PHP 8.2 and later, as the SensitiveParameter attribute was introduced in PHP 8.2.

Cette règle requiert PHP 8.2.0 ou supérieur.

À éviter
<?php

function login(string $username, string $password): void {
   // ...
}
À privilégier
<?php

function login(string $username, #[SensitiveParameter] string $password): void {
   // ...
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
tainted-data-to-sinkerror

Detects user (tainted) data being passed directly to sink functions or constructs (such as echo, print, or user-defined "log" functions). If these functions emit or store data without sanitization, it could lead to Cross-Site Scripting (XSS) or other injection attacks.


À éviter
<?php

// This is considered unsafe:
echo $_GET['name'] ?? '';
À privilégier
<?php

// Properly escape data before using a sink like `echo`
echo htmlspecialchars($_GET['name'] ?? '', ENT_QUOTES, 'UTF-8');

OptionTypeDéfaut
enabledbooleantrue
known-sink-functionsarray["printf"]
levelstring"error"
disallowed-functionswarning

Flags calls to functions that are disallowed via rule configuration.

You can specify which functions or extensions should be disallowed through the functions or extensions options. This helps enforce coding standards, security restrictions, or the usage of preferred alternatives.

Each entry can be a simple string or an object with name and optional help:

functions = [
    'eval',
    { name = 'error_log', help = 'Use MyLogger instead.' },
]

À éviter
<?php

curl_init(); // Error: part of a disallowed extension
À privilégier
<?php

function allowed_function(): void {
    // ...
}

allowed_function(); // Not flagged

OptionTypeDéfaut
enabledbooleantrue
extensionsarray[]
functionsarray[]
levelstring"warning"
disallowed-type-instantiationwarning

Flags direct instantiation of specific types that are disallowed via rule configuration.

This rule helps enforce architectural patterns such as factory methods or provider patterns by preventing direct instantiation of specific classes. This is useful for ensuring consistent configuration, centralizing object creation, and maintaining architectural boundaries.

Each entry can be a simple string or an object with name and optional help:

[linter.rules]
disallowed-type-instantiation = {
    enabled = true,
    types = [
        'HttpService\\Client',
        { name = 'DatabaseConnection', help = 'Use DatabaseFactory::create() instead' },
    ]
}

À éviter
<?php

// Direct instantiation of disallowed type
$client = new HttpService\Client();

// Another disallowed instantiation
$db = new DatabaseConnection('localhost', 'user', 'pass');
À privilégier
<?php

// Using factory pattern instead of direct instantiation
$client = ClientProvider::getClient();

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
typesarray[]
no-roles-as-capabilitieswarning

This rule flags the use of user roles (e.g., 'administrator') in functions that expect a granular capability (e.g., 'edit_posts'). Checking against specific capabilities is a core security principle in WordPress.

Cette règle requiert que l'intégration WordPress soit activée.

À éviter
<?php

// This check is brittle and will fail if roles are customized.
if ( current_user_can( 'editor' ) ) { /* ... */ }
À privilégier
<?php

if ( current_user_can( 'edit_posts' ) ) { /* ... */ }

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-short-opening-tagwarning

Disallows the use of short opening tags (<?).

The availability of <? depends on the short_open_tag directive in php.ini. If this setting is disabled on a server, any code within the short tags will be exposed as plain text, which is a significant security risk. Using the full <?php opening tag is the only guaranteed portable way to ensure your code is always interpreted correctly.


À éviter
<?

echo "Hello, World!";
À privilégier
<?php

echo "Hello, World!";

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
require-preg-quote-delimiterwarning

This rule requires that when using preg_quote(), the second $delimiter argument is always provided. If the string being quoted contains the same character used for your regex delimiter (e.g., /), failing to provide the second argument will prevent that character from being escaped, which can break the regular expression.


À éviter
<?php

// If $user_input contains '/', the regex will be invalid.
$pattern = '/' . preg_quote( $user_input ) . '/';
À privilégier
<?php

// The delimiter is provided, ensuring it gets escaped if necessary.
$pattern = '/' . preg_quote( $user_input, '/' ) . '/';

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-debug-symbolsnote

Flags calls to debug functions like var_dump, print_r, dd, etc.

These functions are useful for debugging, but they should not be committed to version control as they can expose sensitive information and are generally not intended for production environments.


À éviter
<?php

function process_request(array $data) {
    var_dump($data); // Debug call that should be removed
    // ...
}
À privilégier
<?php

// Production-safe code
error_log('Processing user request.');

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"

Sûreté

Constructions qui compilent mais explosent à l'exécution. Ces règles attrapent les pièges avant les utilisateurs.

no-error-control-operatorerror

Detects the use of the error control operator @.

The error control operator suppresses errors and makes debugging more difficult.


À éviter
<?php

$result = @file_get_contents('example.txt');
À privilégier
<?php

try {
    $result = file_get_contents('example.txt');
} catch (Throwable $e) {
    // Handle error
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-evalerror

Detects unsafe uses of the eval construct. The eval construct executes arbitrary code, which can be a major security risk if not used carefully.


À éviter
<?php

eval('echo "Hello, world!";');
À privilégier
<?php

// Safe alternative to eval
$result = json_decode($jsonString);

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-ffierror

Detects unsafe use of the PHP FFI (Foreign Function Interface) extension.

The FFI extension allows interaction with code written in other languages, such as C, C++, and Rust. This can introduce potential security risks and stability issues if not handled carefully.

If you are confident in your use of FFI and understand the risks, you can disable this rule in your Mago configuration.


À éviter
<?php

use FFI;

$ffi = FFI::cdef("void* malloc(size_t size);");
$ffi->malloc(1024); // Allocate memory but never free it
À privilégier
<?php

// Using a safe alternative to FFI
$data = 'some data';
$hash = hash('sha256', $data);

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-globalerror

Detects the use of the global keyword and the $GLOBALS variable.

The global keyword introduces global state into your function, making it harder to reason about and test.


À éviter
<?php

function foo(): void {
    global $bar;
    // ...
}
À privilégier
<?php

function foo(string $bar): void {
    // ...
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-request-variableerror

Detects the use of the $_REQUEST variable, which is considered unsafe.

Use $_GET, $_POST, or $_COOKIE instead for better clarity.


À éviter
<?php

$identifier = $_REQUEST['id'];
À privilégier
<?php

$identifier = $_GET['id'];

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-shell-execute-stringerror

Detects the use of shell execute strings (...) in PHP code.


À éviter
<?php

$output = `ls -l`;
À privilégier
<?php

$output = shell_exec('ls -l');

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-unsafe-finallyerror

Detects control flow statements in finally blocks.

Control flow statements in finally blocks override control flows from try and catch blocks, leading to unexpected behavior.


À éviter
<?php

function example(): int {
    try {
        return get_value();
    } finally {
        return 42; // Unsafe control flow statement in finally block
    }
}
À privilégier
<?php

function example(): int {
    try {
        return get_value();
    } finally {
        // no control flow statements
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
no-request-allwarning

Detects the use of $request->all() or Request::all() in Laravel applications.

Such calls retrieve all input values, including ones you might not expect or intend to handle. It is recommended to use $request->only([...]) to specify the inputs you need explicitly, ensuring better security and validation.

Cette règle requiert que l'intégration Laravel soit activée.

À éviter
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Store a new user.
     */
    public function store(Request $request): RedirectResponse
    {
        $data = $request->all();

        // ...
    }
}
À privilégier
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Store a new user.
     */
    public function store(Request $request): RedirectResponse
    {
        $data = $request->only(['name', 'email', 'password']);

        // ...
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-service-state-mutationwarning

Detects mutations to $this->property inside service methods.

In worker-mode PHP runtimes (FrankenPHP, RoadRunner, Swoole), services persist across requests. Mutating $this->property in a service method introduces shared mutable state that leaks between requests, leading to subtle and hard-to-reproduce bugs.

Mutations include direct assignment ($this->count = 0), compound assignment ($this->count += 1), increment/decrement ($this->count++, ++$this->count), array append ($this->items[] = $item), and unset($this->cache).

The __construct and reset methods are allowed by default.

Cette règle requiert que l'intégration Symfony soit activée.

À éviter
<?php

namespace App\Service;

final class InvoiceService
{
    private int $processedCount = 0;

    public function process(Invoice $invoice): void
    {
        $this->processedCount++;
    }
}
À privilégier
<?php

namespace App\Service;

final class InvoiceService
{
    public function __construct(
        private readonly InvoiceRepository $repository,
    ) {}

    public function process(Invoice $invoice): void
    {
        $total = $invoice->getTotal();
        $this->repository->save($invoice);
    }
}

OptionTypeDéfaut
allowed-methodsarray["__construct","reset"]
enabledbooleanfalse
exclude-namespacesarray["App\\Entity\\","App\\DTO\\","App\\ValueObject\\"]
include-namespacesarray["App\\"]
levelstring"warning"
reset-interfacesarray["Symfony\\Contracts\\Service\\ResetInterface"]

Correction

Bugs et erreurs de logique. Les règles de cette catégorie attrapent du code qui fait probablement autre chose que ce que l'auteur voulait.

no-onlyerror

Detects 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.

Cette règle requiert que l'intégration Pest soit activée.

À éviter
<?php

test('example test', function () {
    expect(true)->toBeTrue();
})->only();

it('does something', function () {
    expect(1)->toBe(1);
})->only();
À privilégier
<?php

test('example test', function () {
    expect(true)->toBeTrue();
});

it('does something', function () {
    expect(1)->toBe(1);
});

OptionTypeDéfaut
enabledbooleantrue
levelstring"error"
assert-descriptionwarning

Detects 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.


À éviter
<?php

assert($user->isActivated());
À privilégier
<?php

assert($user->isActivated(), 'User MUST be activated at this point.');

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
identity-comparisonwarning

Detects equality and inequality comparisons that should use identity comparison operators.


À éviter
<?php

if ($a == $b) {
    echo '$a is same as $b';
}
À privilégier
<?php

if ($a === $b) {
    echo '$a is same as $b';
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
ineffective-format-ignore-nextwarning

Detects @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.


À éviter
<?php

// This doesn't work - marker is inside an array literal
$arr = [ // @mago-format-ignore-next
    1,
    2,
];
À privilégier
<?php

// This works - marker is before a statement
// @mago-format-ignore-next
const GRID = [
  [1, 2, 3], [1, 2, ], [0,    0],
];

foo();

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
ineffective-format-ignore-regionwarning

Detects @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.


À éviter
<?php

// This doesn't work - markers are inside a function call
foo( // @mago-format-ignore-start
    $x,
    $y
// @mago-format-ignore-end
);
À privilégier
<?php

// This works - markers are between statements
// @mago-format-ignore-start
$x = 1;  $y = 2;  // preserved as-is
// @mago-format-ignore-end

foo();

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-assign-in-argumentwarning

Detects assignments in function call arguments which can lead to unexpected behavior and make the code harder to read and understand.


À éviter
<?php

foo($x = 5);
À privilégier
<?php

$x = 5;
foo($x);

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-assign-in-conditionwarning

Detects assignments in conditions which can lead to unexpected behavior and make the code harder to read and understand.


À éviter
<?php

if ($x = 1) {
    // ...
}
À privilégier
<?php

$x = 1;
if ($x == 1) {
    // ...
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-dead-storewarning

Flags assignments to a variable whose value is overwritten by a later assignment without being read in between. The earlier assignment is dead; its value never reaches anything observable.

Detection is limited to linear (non-branching) flow. Writes inside conditional branches (if/else, loops, match arms, try paths, switch cases) don't pair up with writes in sibling branches, so this rule produces no false positives for code like if ($cond) { $x = 1; } else { $x = 2; } return $x;.

Variables whose name starts with an underscore ($_, $_foo) are ignored. Variables declared via global or static are also ignored.

The rule analyses one function-like scope at a time. It bails out of any scope that uses variable variables ($$x, ${expr}) or calls extract().


À éviter
<?php

function f() {
    $x = 1; // dead - overwritten before being read
    $x = compute();
    return $x;
}
À privilégier
<?php

function f() {
    $x = compute();
    return $x;
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-empty-catch-clausewarning

Warns 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.


À éviter
<?php

try {
    // some code
} catch(Exception $e) {
    // This block is empty and swallows the exception.
}
À privilégier
<?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());
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
no-redundant-variablewarning

Flags variables that are written or declared but whose value is never read.

Detects fully-unused variables (assigned and never referenced) as well as variables whose only mention is on the write side, for example, an undefined name passed to a function as a potential by-reference output where the result is never observed by the caller.

Variables whose name starts with an underscore ($_, $_foo) are treated as intentionally-discarded and are ignored. Variables declared via global or static are also ignored, since they are bindings to external scope.

The rule analyses one function-like scope at a time. It bails out of any scope that uses variable variables ($$x, ${expr}) or calls extract(), since those introduce names the linter cannot resolve.


À éviter
<?php

function greet(string $name): string
{
    $unused = compute_something();

    return "Hello, $name!";
}
À privilégier
<?php

function greet(string $name): string
{
    $greeting = "Hello, $name!";

    return $greeting;
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-unused-closure-capturewarning

Flags variables in a closure's use (...) clause that are never read or written inside the closure body.

Captures only earn their keep when the body refers to them. A capture that nothing observes is usually a leftover from a refactor or a typo in the captured name.

Names beginning with an underscore ($_, $_foo) are treated as intentionally-discarded and are ignored. By-reference captures (use (&$x)) are also ignored, they are commonly used for their side-effect on the outer scope, even when the inner body doesn't otherwise touch the binding. The rule bails out of any closure body that uses variable variables ($$x, ${expr}) or calls extract().


À éviter
<?php

$base = 10;
$add = function (int $x) use ($base): int {
    return $x;
};
À privilégier
<?php

$base = 10;
$add = function (int $x) use ($base): int {
    return $x + $base;
};

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-unused-globalwarning

Flags global $x; declarations whose name is never read or written inside the surrounding function-like scope.

A global statement only earns its keep when later code refers to the imported binding. If the name is never used, the statement is dead , usually a leftover from a refactor or a typo in the imported name.

Names beginning with an underscore ($_, $_foo) are treated as intentionally-discarded and are ignored. The rule bails out of any scope that uses variable variables ($$x, ${expr}) or calls extract(), since those introduce names the linter cannot resolve.


À éviter
<?php

function f(): void {
    global $forgotten;
}
À privilégier
<?php

function bump(): void {
    global $counter;
    $counter++;
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
no-unused-staticwarning

Flags static $x; declarations whose name is never read or written inside the surrounding function-like scope.

A static declaration only earns its keep when later code refers to the binding. If the name is never used after the declaration, the statement is dead, usually a leftover from a refactor.

Names beginning with an underscore ($_, $_foo) are treated as intentionally-discarded and are ignored. The rule bails out of any scope that uses variable variables ($$x, ${expr}) or calls extract(), since those introduce names the linter cannot resolve.


À éviter
<?php

function f(): void {
    static $forgotten = 0;
}
À privilégier
<?php

function next_id(): int {
    static $counter = 0;
    return ++$counter;
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
strict-assertionswarning

Detects non-strict assertions in test methods. Assertions should use strict comparison methods, such as assertSame or assertNotSame instead of assertEquals or assertNotEquals.

Cette règle requiert que l'intégration PHPUnit soit activée.

À éviter
<?php

declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
    public function testSomething(): void
    {
        $this->assertEquals(42, 42);
    }
}
À privilégier
<?php

declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
    public function testSomething(): void
    {
        $this->assertSame(42, 42);
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
strict-behaviorwarning

Detects 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.

Cette règle requiert PHP 7.0.0 ou supérieur.

À éviter
<?php

in_array(1, ['foo', 'bar', 'baz']);
À privilégier
<?php

in_array(1, ['foo', 'bar', 'baz'], strict: true);

OptionTypeDéfaut
allow-loose-behaviorbooleanfalse
enabledbooleantrue
levelstring"warning"
strict-typeswarning

Detects missing declare(strict_types=1); statement at the beginning of the file.

Cette règle requiert PHP 7.0.0 ou supérieur.

À éviter
<?php

echo "Hello, World!";
À privilégier
<?php

declare(strict_types=1);

echo "Hello, World!";

OptionTypeDéfaut
allow-disablingbooleanfalse
enabledbooleantrue
levelstring"warning"
switch-continue-to-breakwarning

Detects 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.


À éviter
<?php

switch ($value) {
    case 1:
        echo 'one';
        continue;
}
À privilégier
<?php

switch ($value) {
    case 1:
        echo 'one';
        break;
}

OptionTypeDéfaut
enabledbooleanfalse
levelstring"warning"
use-specific-assertionswarning

Suggests 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.

Cette règle requiert que l'intégration PHPUnit soit activée.

À éviter
<?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);
    }
}
À privilégier
<?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);
    }
}

OptionTypeDéfaut
enabledbooleantrue
levelstring"warning"
invalid-open-tagnote

Detects 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.


À éviter
<php?

echo 'Hello, world!';
À privilégier
<?php

echo 'Hello, world!';

OptionTypeDéfaut
enabledbooleantrue
levelstring"note"

↳ Modifier cette page →