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
psl-array-functionspsl-data-structurespsl-datetimepsl-math-functionspsl-outputpsl-randomness-functionspsl-regex-functionspsl-sleep-functionspsl-string-functions
Spiral
Symfony
Tempest
WordPress
no-db-schema-changeno-direct-db-queryno-roles-as-capabilitiesno-unescaped-outputuse-wp-functions
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-empty¶error
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.
<?php
if (!empty($myArray)) {
// ...
}
<?php
if ($myArray === []) {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
explicit-octal¶warning
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.
<?php
$a = 0123;
<?php
$a = 0o123;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
instanceof-stringable¶warning
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.
<?php
function stringify(mixed $value): string {
if (is_object($value) && method_exists($value, '__toString')) {
return (string) $value;
}
return '';
}
<?php
function stringify(mixed $value): string {
if ($value instanceof Stringable) {
return (string) $value;
}
return '';
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
literal-named-argument¶warning
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.
<?php
function set_option(string $key, bool $enable_feature) {}
set_option('feature_x', true); // ❌ intent unclear
<?php
function set_option(string $key, bool $enable_feature) {}
set_option(key: 'feature_x', enable_feature: true); // ✅ clear intent
| Option | Type | Défaut |
|---|---|---|
check-first-argument | boolean | false |
enabled | boolean | true |
level | string | "warning" |
threshold | number | 1 |
no-hash-emoji¶warning
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.
<?php
#️⃣ This is a comment
#️⃣[MyAttribute] <- not a valid attribute
class Foo {}
<?php
# This is a comment
#[MyAttribute]
class Foo {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-isset¶warning
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.
<?php
if (isset($value)) {
// ...
}
<?php
if ($value !== null) {
// ...
}
$result = $value ?? 'default';
| Option | Type | Défaut |
|---|---|---|
allow-array-checks | boolean | false |
enabled | boolean | true |
level | string | "warning" |
no-multi-assignments¶warning
Flags any instances of multiple assignments in a single statement. This can lead to confusion and unexpected behavior, and is generally considered poor practice.
<?php
$a = $b = 0;
<?php
$b = 0;
$a = $b;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-nested-ternary¶warning
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.
<?php
$allowed = $user->isAdmin() ? true : ($user->isEditor() ? true : false);
<?php
if ($user->isAdmin()) {
$allowed = true;
} else {
$allowed = $user->isEditor();
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-shorthand-ternary¶warning
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.
<?php
$value = $foo ?: $default;
$value = $foo ? : $default;
<?php
$value = $foo ?? $default;
$value = $foo ? $foo : $default;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-variable-variable¶warning
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.
<?php
$foo = 'bar';
$varName = 'foo';
echo $$varName; // Outputs 'bar'
<?php
$foo = 'bar';
echo $foo; // Outputs 'bar'
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
readable-literal¶warning
Enforces using underscore separators in numeric literals for improved readability.
Cette règle requiert PHP 7.4.0 ou supérieur.
<?php
$a = 1000000;
$b = 0xCAFEF00D;
$c = 0b01011111;
<?php
$a = 1_000_000;
$b = 0xCAFE_F00D;
$c = 0b0101_1111;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
min-digits | number | 5 |
str-contains¶warning
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.
<?php
$a = 'hello world';
$b = 'world';
if (strpos($a, $b) !== false) {
echo 'Found';
}
<?php
$a = 'hello world';
$b = 'world';
if (str_contains($a, $b)) {
echo 'Found';
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
str-starts-with¶warning
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.
<?php
$a = 'hello world';
$b = 'hello';
if (strpos($a, $b) === 0) {
echo 'Found';
}
<?php
$a = 'hello world';
$b = 'hello';
if (str_starts_with($a, $b)) {
echo 'Found';
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
tagged-fixme¶warning
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.
<?php
// FIXME: This is an invalid FIXME comment.
<?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.
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
tagged-todo¶warning
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.
<?php
// TODO: This is an invalid TODO comment.
<?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.
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
use-dedicated-expectation¶warning
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.
<?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();
});
<?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');
});
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
use-simpler-expectation¶warning
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.
<?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();
});
<?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);
});
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
use-specific-expectations¶warning
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.
<?php
test('generic comparisons', function () {
expect($value)->toBe(true);
expect($value)->toBe(false);
expect($value)->toBe(null);
expect($array)->toBe([]);
expect($value)->not->toBeFalse();
});
<?php
test('specific matchers', function () {
expect($value)->toBeTrue();
expect($value)->toBeFalse();
expect($value)->toBeNull();
expect($array)->toBeEmpty();
});
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
valid-docblock¶note
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.
<?php
/**
* For more information, {@see https://example.com
*
* @param int $a
*
* @return int
*/
function foo($a) {
return $a;
}
<?php
/**
* For more information, {@see https://example.com}.
*
* @param int $a
*
* @return int
*/
function foo($a) {
return $a;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
missing-docs¶help
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.
<?php
function foo() {}
<?php
/**
* A helpful piece of documentation.
*/
function foo() {}
| Option | Type | Défaut |
|---|---|---|
classes | boolean | false |
constants | boolean | true |
enabled | boolean | false |
enum-cases | boolean | true |
enums | boolean | false |
functions | boolean | true |
interfaces | boolean | false |
level | string | "help" |
methods | boolean | true |
properties | boolean | true |
statics | boolean | true |
traits | boolean | false |
no-negated-ternary¶help
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.
<?php
$x = !$foo ? 1 : 0;
<?php
$x = $foo ? 0 : 1;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
no-short-bool-cast¶help
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.
<?php
$active = !!$value;
<?php
$active = (bool) $value;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "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-controller¶error
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; ouLaravel; ouTempest; ouSpiral; ouCakePHP; ouYii.
<?php
namespace App\Http\Controllers;
class UserController
{
// ...
}
<?php
namespace App\Http\Controllers;
final class UserController
{
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-inline¶error
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.
Hello
<?php
echo "Hello, world!";
?>
Goodbye
<?php
namespace App;
echo "Hello, world!";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "error" |
psl-output¶error
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.
<?php
echo "Hello, world!";
<?php
Psl\IO\write_line("Hello, world!");
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
combine-consecutive-issets¶warning
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.
<?php
if (isset($a) && isset($b)) {
// ...
}
<?php
if (isset($a, $b)) {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
loop-does-not-iterate¶warning
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.
<?php
for ($i = 0; $i < 3; $i++) {
break; // The loop never truly iterates, as this break is unconditional.
}
<?php
for ($i = 0; $i < 3; $i++) {
echo $i;
if ($some_condition) {
break; // This break is conditional.
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
middleware-in-routes¶warning
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.
<?php
namespace App\Http\Controllers;
class UserController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
}
<?php
// routes/web.php
Route::get('/user', 'UserController@index')->middleware('auth');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-array-accumulation-in-loop¶warning
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.
<?php
$result = [];
foreach ($items as $item) {
$result = array_merge($result, $item);
}
<?php
$chunks = [];
foreach ($items as $item) {
$chunks[] = $item;
}
$result = array_merge(...$chunks);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-direct-db-query¶warning
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.
<?php
global $wpdb;
$posts = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_author = 1");
<?php
$posts = get_posts(['author' => $author_id]);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-ini-set¶warning
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.
<?php
// This can override server settings in an unpredictable way.
ini_set( 'display_errors', 1 );
ini_set( 'memory_limit', '256M' );
<?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' );
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-literal-namespace-string¶warning
Flags hardcoded fully qualified class name strings. Use ::class notation
instead for better IDE support, refactoring safety, and static analysis.
<?php
$className = 'App\Models\User';
<?php
$className = \App\Models\User::class;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-parameter-shadowing¶warning
Detects when a function or method parameter is shadowed by a loop variable or catch variable, making the original parameter value inaccessible.
<?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
}
}
<?php
function read(array $domains, array $locales): void
{
$translations = getTranslations($domains, $locales);
foreach ($translations as $namespace => $namespaceLocales) {
// $locales is still accessible
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-side-effects-with-declarations¶warning
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.
<?php
echo 'Loading utility file...';
class StringHelper
{
public static function slugify(string $input): string
{
return '';
}
}
<?php
namespace App;
class UserManager
{
public function find(int $id): ?User
{
return null;
}
}
| Option | Type | Défaut |
|---|---|---|
allow-class-alias | boolean | true |
allow-class-exists | boolean | true |
allow-conditional-declarations | boolean | true |
enabled | boolean | false |
level | string | "warning" |
no-sprintf-concat¶warning
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.
<?php
$name = 'World';
$greeting = 'Hello, ' . sprintf('%s!', $name);
<?php
$name = 'World';
$greeting = sprintf('Hello, %s!', $name);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
prefer-anonymous-migration¶warning
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.
<?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();
<?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');
}
};
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
prefer-explode-over-preg-split¶warning
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
flagsargument (if present) is literal0.
<?php
$parts = preg_split('/, /', $csv);
<?php
$parts = explode(', ', $csv);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
prefer-first-class-callable¶warning
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.
<?php
$names = ['Alice', 'Bob', 'Charlie'];
$uppercased_names = array_map(fn($name) => $formatter->format($name), $names);
<?php
$names = ['Alice', 'Bob', 'Charlie'];
$uppercased_names = array_map($formatter->format(...), $names);
| Option | Type | Défaut |
|---|---|---|
check-functions | boolean | false |
enabled | boolean | true |
level | string | "warning" |
prefer-test-attribute¶warning
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.
<?php
use PHPUnit\Framework\TestCase;
class UserTest extends TestCase
{
public function testItReturnsFullName(): void {}
}
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;
class UserTest extends TestCase
{
#[Test]
public function itReturnsFullName(): void {}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
psl-array-functions¶warning
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.
<?php
$filtered = array_filter($xs, fn($x) => $x > 2);
<?php
$filtered = Psl\Vec\filter($xs, fn($x) => $x > 2);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
psl-data-structures¶warning
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.
<?php
declare(strict_types=1);
$stack = new SplStack();
<?php
declare(strict_types=1);
use Psl\DataStructure\Stack;
$stack = new Stack();
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
psl-datetime¶warning
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.
<?php
$dateTime = new DateTime();
<?php
$dateTime = new Psl\DateTime\DateTime();
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
psl-math-functions¶warning
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.
<?php
$abs = abs($number);
<?php
$abs = Psl\Math\abs($number);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
psl-randomness-functions¶warning
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.
<?php
$randomInt = random_int(0, 10);
<?php
$randomInt = Psl\SecureRandom\int(0, 10);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
psl-regex-functions¶warning
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.
<?php
$result = preg_match('/\w+/', 'Hello, World!');
<?php
$result = Psl\Regex\matches('Hello, World!', '/\w+/');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
psl-sleep-functions¶warning
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.
<?php
sleep(1);
<?php
use Psl\Async;
use Psl\DateTime;
Async\sleep(DateTime\Duration::seconds(1));
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
psl-string-functions¶warning
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.
<?php
$capitalized = ucfirst($string);
<?php
$capitalized = Psl\Str\capitalize($string);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
require-namespace¶warning
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.
<?php
class Foo {}
<?php
namespace App;
class Foo {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
single-class-per-file¶warning
Ensures that each file contains at most one class-like definition (class, interface, enum, or trait).
<?php
namespace App;
class Foo
{
}
class Bar
{
}
<?php
namespace App;
class Foo
{
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
use-wp-functions¶warning
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.
<?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');
<?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' );
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
prefer-interface¶note
Detects when an implementation class is used instead of the interface.
Cette règle requiert que l'intégration Symfony soit activée.
<?php
use Symfony\Component\Serializer\Serializer;
class UserController
{
public function __construct(Serializer $serializer)
{
$this->serializer = $serializer;
}
}
<?php
use Symfony\Component\Serializer\SerializerInterface;
class UserController
{
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
prefer-while-loop¶note
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.
<?php
for (; $i < 10;) {
echo $i;
$i++;
}
<?php
while ($i < 10) {
echo $i;
$i++;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
prefer-arrow-function¶help
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.
<?php
$a = function($x) {
return $x + 1;
};
<?php
$a = fn($x) => $x + 1;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
prefer-early-continue¶help
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.
<?php
for ($i = 0; $i < 10; $i++) {
if ($condition) {
doSomething();
}
}
<?php
for ($i = 0; $i < 10; $i++) {
if (!$condition) {
continue;
}
doSomething();
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
max_allowed_statements | number | 0 |
prefer-pre-increment¶help
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.
<?php
$i++;
$count--;
<?php
++$i;
--$count;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
prefer-self-return-type¶help
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.
<?php
final class Box
{
public static function create(): Box
{
return new Box();
}
}
<?php
final class Box
{
public static function create(): self
{
return new self();
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
prefer-static-closure¶help
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.
<?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;
};
}
}
<?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;
};
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
prefer-view-array¶help
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.
<?php
return view('user.profile')->with([
'user' => $user,
'profile' => $profile,
]);
<?php
return view('user.profile', [
'user' => $user,
'profile' => $profile,
]);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
sorted-integer-keys¶help
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.
<?php
$weights = [
3 => 0.506,
5 => 0.21246,
10 => 0.10823,
20 => 0.06206,
2 => 0.06011,
4 => 0.01233,
];
<?php
$weights = [
2 => 0.06011,
3 => 0.506,
4 => 0.01233,
5 => 0.21246,
10 => 0.10823,
20 => 0.06206,
];
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
use-compound-assignment¶help
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.
<?php
$count = $count + 1;
$message = $message . ' Hello';
<?php
$count += 1;
$message .= ' Hello';
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
yoda-conditions¶help
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.
<?php
// Vulnerable to the accidental assignment bug, e.g., if ($is_active = true).
if ( $is_active === true ) { /* ... */ }
<?php
if ( true === $is_active ) { /* ... */ }
if ( 5 === $count ) { /* ... */ }
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "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-style¶warning
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.
<?php
// configured style: "static"
final class SomeTest extends TestCase
{
public function testSomething(): void
{
$this->assertTrue(true); // Incorrect style
self::assertFalse(false); // Incorrect style
}
}
<?php
// configured style: "static"
final class SomeTest extends TestCase
{
public function testSomething(): void
{
static::assertTrue(true);
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
style | string | "static" |
file-name¶warning
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.
<?php
// File: test.php
namespace App;
class Foo
{
}
<?php
// File: test.php
namespace App;
class test
{
}
| Option | Type | Défaut |
|---|---|---|
check-functions | boolean | false |
enabled | boolean | true |
level | string | "warning" |
no-alternative-syntax¶warning
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.
<?php
if ($condition):
echo 'yes';
endif;
<?php
if ($condition) {
echo 'yes';
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-hash-comment¶warning
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.
<?php
# This is a shell-style comment.
<?php
// This is a good comment.
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
array-style¶note
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.
<?php
// By default, 'short' is enforced, so array(...) triggers a warning:
$arr = array(1, 2, 3);
<?php
// By default, `style` is 'short', so this snippet is valid:
$arr = [1, 2, 3];
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
style | string | "short" |
block-statement¶note
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.
<?php
if (true)
echo "Hello";
for ($i = 0; $i < 10; $i++)
echo $i;
<?php
if (true) {
echo "Hello";
}
for ($i = 0; $i < 10; $i++) {
echo $i;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
braced-string-interpolation¶note
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.
<?php
$a = "Hello, $name!";
$b = "Hello, ${name}!";
$c = "Hello, ${$name}!";
$d = "Hello, ${$object->getMethod()}!";
<?php
$a = "Hello, {$name}!";
$b = "Hello, {$name}!";
$c = "Hello, {$$name}!";
$d = "Hello, {${$object->getMethod()}}!";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
no-alias-function¶note
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.
<?php
// 'diskfreespace' is an alias for 'disk_free_space'
$freeSpace = diskfreespace("/");
<?php
// 'disk_free_space' is the proper name instead of 'diskfreespace'
$freeSpace = disk_free_space("/");
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
no-php-tag-terminator¶note
Discourages the use of ?><?php as a statement terminator. Recommends using a semicolon
(;) instead for clarity and consistency.
<?php
echo "Hello World" ?><?php
<?php
echo "Hello World";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
no-trailing-space¶note
Detects trailing whitespace at the end of comments. Trailing whitespace can cause unnecessary diffs and formatting issues, so it is recommended to remove it.
<?php
// This is a comment with trailing whitespace.
<?php
// This is a good comment.
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
string-style¶note
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.
<?php
// With the default `style: interpolation`:
$a = "Hello, " . $name . "!";
$b = "value: " . $obj->name;
<?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;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "note" |
style | string | "interpolation" |
ambiguous-constant-access¶help
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.
<?php
namespace App;
// Ambiguous: could be App\PHP_VERSION or \PHP_VERSION
$version = PHP_VERSION;
<?php
namespace App;
use const PHP_VERSION;
// OK: Explicitly imported
$version1 = PHP_VERSION;
// OK: Explicitly global
$version2 = \PHP_VERSION;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
ambiguous-function-call¶help
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.
<?php
namespace App;
// Ambiguous: could be App\strlen or \strlen
$length = strlen("hello");
<?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();
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
class-name¶help
Detects class declarations that do not follow class naming convention.
Class names should be in class case, also known as PascalCase.
<?php
class my_class {}
class myClass {}
class MY_CLASS {}
<?php
class MyClass {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
psr | boolean | false |
constant-name¶help
Detects constant declarations that do not follow constant naming convention.
Constant names should be in constant case, also known as UPPER_SNAKE_CASE.
<?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;
}
<?php
const MY_CONSTANT = 42;
class MyClass {
public const int MY_CONSTANT = 42;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
enum-name¶help
Detects enum declarations that do not follow class naming convention.
Enum names should be in class case, also known as PascalCase.
<?php
enum my_enum {}
enum myEnum {}
enum MY_ENUM {}
<?php
enum MyEnum {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
function-name¶help
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.
<?php
function MyFunction() {}
function My_Function() {}
<?php
function my_function() {}
| Option | Type | Défaut |
|---|---|---|
camel | boolean | false |
either | boolean | false |
enabled | boolean | true |
level | string | "help" |
interface-name¶help
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.
<?php
interface myInterface {}
interface my_interface {}
interface MY_INTERFACE {}
<?php
interface MyInterface {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
psr | boolean | false |
lowercase-keyword¶help
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).
<?PHP
IF (TRUE) {
ECHO "Keywords not in lowercase";
} ELSE {
RETURN;
}
<?php
if (true) {
echo "All keywords in lowercase";
} else {
return;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
lowercase-type-hint¶help
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.
<?php
function example(Int $param): VOID {
return;
}
<?php
function example(int $param): void {
return;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
method-name¶help
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.
<?php
class Foo
{
public function GetName(): string {}
public function set_name(string $name): void {}
}
<?php
class Foo
{
public function getName(): string {}
public function setName(string $name): void {}
}
| Option | Type | Défaut |
|---|---|---|
camel | boolean | true |
either | boolean | false |
enabled | boolean | false |
level | string | "help" |
use-snake-case-for-tests | boolean | false |
no-fully-qualified-global-class-like¶help
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.
<?php
namespace App;
$dt = new \DateTime();
function foo(\DateTime $dt): \Exception {}
<?php
namespace App;
use DateTime;
use Exception;
$dt = new DateTime();
function foo(DateTime $dt): Exception {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
no-fully-qualified-global-constant¶help
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.
<?php
namespace App;
$version = \PHP_VERSION;
<?php
namespace App;
use const PHP_VERSION;
$version = PHP_VERSION;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
no-fully-qualified-global-function¶help
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.
<?php
namespace App;
$length = \strlen("hello");
<?php
namespace App;
use function strlen;
$length = strlen("hello");
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
property-name¶help
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.
<?php
final class Foo {
public string $My_Property;
public function __construct(
public int $My_Promoted_Property,
) {}
}
<?php
final class Foo {
public string $myProperty;
public function __construct(
public int $myPromotedProperty,
) {}
}
| Option | Type | Défaut |
|---|---|---|
camel | boolean | true |
either | boolean | false |
enabled | boolean | false |
level | string | "help" |
trait-name¶help
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.
<?php
trait myTrait {}
trait my_trait {}
trait MY_TRAIT {}
<?php
trait MyTrait {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
psr | boolean | false |
variable-name¶help
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.
<?php
$MyVariable = 1;
$My_Variable = 2;
function foo($MyParam) {}
<?php
$my_variable = 1;
function foo($my_param) {}
| Option | Type | Défaut |
|---|---|---|
camel | boolean | false |
check-parameters | boolean | true |
either | boolean | true |
enabled | boolean | false |
level | string | "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-cast¶error
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.
<?php
(integer) $value;
<?php
(int) $value;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
deprecated-shell-execute-string¶error
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.
<?php
`ls -l`;
<?php
shell_exec('ls -l');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
deprecated-switch-semicolon¶error
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.
<?php
switch ($value) {
case 1;
// code for case 1
break;
case 2;
// code for case 2
break;
default;
// default case
break;
}
<?php
switch ($value) {
case 1:
// code for case 1
break;
case 2:
// code for case 2
break;
default:
// default case
break;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
explicit-nullable-param¶warning
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.
<?php
function foo(string $param = null) {}
function bar(string $param = NULL) {}
function baz(object $param = null) {}
<?php
function foo(?string $param) {}
function bar(null|string $param) {}
function baz(null|object $param = null) {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-underscore-class¶warning
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.
<?php
class _ {}
<?php
class MyService {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-void-reference-return¶warning
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.
<?php
function &foo(): void {
// ...
}
<?php
function &foo(): string {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
optional-param-order¶warning
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.
<?php
function foo(?string $optional = null, string $required): void {}
<?php
function foo(string $required, ?string $optional = null): void {}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "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-complexity¶error
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.
<?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;
}
<?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']);
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
method-threshold | null | null |
threshold | number | 15 |
excessive-parameter-list¶error
Detects functions, closures, and methods with too many parameters.
If the number of parameters exceeds a configurable threshold, an issue is reported.
<?php
function createUser($name, $email, $password, $age, $country, $city, $zipCode) {
return true;
}
<?php
function processOrder($orderId, $userId, $total, $status, $date) {
return true;
}
| Option | Type | Défaut |
|---|---|---|
constructor-threshold | null | null |
enabled | boolean | true |
level | string | "error" |
threshold | number | 5 |
kan-defect¶error
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/
<?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'];
}
<?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];
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
threshold | number | 1.6 |
too-many-enum-cases¶error
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.
<?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;
}
<?php
enum SimpleEnum {
case A;
case B;
case C;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
threshold | number | 20 |
too-many-methods¶error
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.
<?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() {}
}
<?php
class SimpleClass {
public function a() {}
public function b() {}
}
| Option | Type | Défaut |
|---|---|---|
count-hooks | boolean | false |
count-setters-and-getters | boolean | false |
enabled | boolean | true |
level | string | "error" |
threshold | number | 10 |
too-many-properties¶error
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.
<?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;
}
<?php
class SimpleClass {
public $a;
public $b;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
threshold | number | 10 |
excessive-nesting¶warning
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.
<?php
if ($a) {
if ($b) {
if ($c) {
if ($d) {
if ($e) {
if ($f) {
if ($g) {
if ($h) {
echo "Too deeply nested!";
}
}
}
}
}
}
}
}
<?php
if ($condition) {
while ($otherCondition) {
echo "Hello"; // nesting depth = 2
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
function-like-threshold | null | null |
level | string | "warning" |
threshold | number | 7 |
halstead¶warning
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
<?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;
}
<?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);
}
| Option | Type | Défaut |
|---|---|---|
difficulty-threshold | number | 12.0 |
effort-threshold | number | 5000.0 |
enabled | boolean | true |
level | string | "warning" |
volume-threshold | number | 1000.0 |
no-goto¶note
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.
<?php
$i = 0;
loop:
if ($i >= 10) {
goto end;
}
$i++;
goto loop;
end:
<?php
$i = 0;
while ($i < 10) {
if ($i === 5) {
break; // Structured control flow.
}
$i++;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
no-boolean-flag-parameter¶help
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.
<?php
function get_difference(string $a, string $b, bool $ignore_case): string {
// ...
}
<?php
function get_difference(string $a, string $b): string {
// ...
}
function get_difference_case_insensitive(string $a, string $b): string {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
exclude-constructors | boolean | true |
exclude-setters | boolean | false |
level | string | "help" |
no-else-clause¶help
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.
<?php
function process($user) {
if ($user->isVerified()) {
// "Happy path" is nested
$user->login();
} else {
// Logic is split across branches
return;
}
}
<?php
function process($user) {
if (!$user->isVerified()) {
return; // Early return
}
// "Happy path" continues here
$user->login();
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
Redondance
Code mort, valeurs inutilisées, constructions sans effet observable. Les retirer garde le code honnête.
inline-variable-return¶warning
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.
<?php
function getValue() {
$result = computeResult();
return $result;
}
function getArray() {
$arr = [1, 2, 3];
return $arr;
}
<?php
function getValue() {
return computeResult();
}
function process() {
$result = computeResult();
log($result);
return $result;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-iterator-to-array-in-foreach¶warning
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.
<?php
foreach (iterator_to_array($iterator) as $value) {
// ...
}
<?php
foreach ($iterator as $value) {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-redundant-literal-return¶warning
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.
<?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;
}
<?php
function getValue($x) {
return $x;
}
function getValueOrDefault($x, $default) {
if ($x === null) {
return $default;
}
return $x;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-redundant-use¶warning
Detects use statements that import items that are never used or are redundant
because they import from the same namespace.
<?php
namespace App;
use App\Helpers\ArrayHelper;
use App\Helpers\StringHelper; // StringHelper is not used.
$result = ArrayHelper::combine([]);
<?php
namespace App;
use App\Helpers\ArrayHelper;
$result = ArrayHelper::combine([]);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-self-assignment¶warning
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+).
<?php
$a = $a;
$this->x = $this->x;
$foo->bar = $foo->bar;
<?php
$a = $b;
$this->x = $other->x;
$foo->bar = $baz->bar;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-empty-comment¶note
Detects empty comments in the codebase. Empty comments are not useful and should be removed to keep the codebase clean and maintainable.
<?php
//
#
/**/
<?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.
*/
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
preserve-single-line-comments | boolean | false |
no-empty-loop¶note
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.
<?php
while (should_wait()) {
// Empty loop body
}
<?php
foreach ($items as $item) {
process($item);
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
no-is-null¶note
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.
<?php
if (is_null($value)) {
// ...
}
<?php
if ($value === null) {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "note" |
constant-condition¶help
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.
<?php
if (true) {
echo "This will always run";
}
if (false) {
echo "This is dead code";
}
<?php
if ($variable > 10) {
echo "Greater than 10";
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-closing-tag¶help
Detects redundant closing tags ( ?> ) at the end of a file.
<?php
echo "Hello, world!";
?>
<?php
echo "Hello, world!";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-noop¶help
Detects redundant noop statements.
<?php
;
<?php
echo "Hello, world!";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-null-property-init¶help
Detects redundant = null initialization on untyped properties.
Untyped properties already default to null, making an explicit
= null initializer unnecessary.
<?php
class Foo {
public $name = null;
}
<?php
class Foo {
public $name;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
no-protected-in-final¶help
Detects protected items in final classes or enums.
<?php
final class Foo {
protected string $foo;
protected(set) string $bar;
protected private(set) string $baz;
protected function fun(): void {
// ...
}
}
<?php
final class Foo {
private string $foo;
private(set) string $bar;
private string $baz;
private function fun(): void {
// ...
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-binary-string-prefix¶help
Detects the redundant b/B prefix on string literals. The binary string prefix
has no effect in PHP and can be safely removed.
<?php
$foo = b'hello';
$bar = b"world";
<?php
$foo = 'hello';
$bar = "world";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-block¶help
Detects redundant blocks around statements.
<?php
{
echo "Hello, world!";
}
<?php
echo "Hello, world!";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-continue¶help
Detects redundant continue statements in loops.
<?php
while (true) {
echo "Hello, world!";
continue; // Redundant `continue` statement
}
<?php
while (true) {
echo "Hello, world!";
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-else¶help
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.
<?php
function process($user) {
if (!$user->isVerified()) {
return;
} else {
$user->login();
}
}
<?php
function process($user) {
if (!$user->isVerified()) {
return;
}
$user->login();
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "help" |
no-redundant-file¶help
Detects redundant files that contain no executable code or declarations.
<?php
declare(strict_types=1);
// This file is redundant.
<?php
declare(strict_types=1);
function foo(): void {
return 42;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-final¶help
Detects redundant final modifiers on methods in final classes or enum methods.
<?php
final class Foo {
final public function bar(): void {
// ...
}
}
<?php
final class Foo {
public function bar(): void {
// ...
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-isset¶help
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.
<?php
if (isset($d, $d['first'], $d['first']['second'])) {
echo 'all present';
}
<?php
if (isset($d['first']['second'])) {
echo 'all present';
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-label¶help
Detects redundant goto labels that are declared but not used.
<?php
label:
echo "Hello, world!";
<?php
goto end;
echo "Hello, world!";
end:
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-math¶help
Detects redundant mathematical operations that can be simplified or removed. Includes operations like multiplying by 1/-1, adding 0, modulo 1/-1, etc.
<?php
$result = $value * 1;
$sum = 0 + $total;
$difference = $value - 0;
$remainder = $x % 1;
$negative = $value * -1;
<?php
$result = $value * 2;
$sum = 1 + $total;
$difference = $value - 1;
$remainder = $x % 2;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-method-override¶help
Detects methods that override a parent method but only call the parent method with the same arguments.
<?php
class Parent
{
public function foo(): void
{
// ...
}
}
class Child extends Parent
{
public function foo(): void
{
parent::foo();
}
}
<?php
class Parent
{
public function foo(): void
{
// ...
}
}
class Child extends Parent
{
public function foo(): void
{
parent::foo();
echo 'Additional logic here';
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-nullsafe¶help
Flags the use of the nullsafe operator (?->) in contexts where its null-checking behavior is redundant.
This occurs in two common situations:
- When an expression using
?->is immediately followed by the null coalescing operator (??). - When an expression using
?->is checked withisset().
In both scenarios, the surrounding language construct (?? or isset()) already handles null values safely,
making the ?-> operator superfluous and the code unnecessarily verbose.
<?php
$name = $user?->name ?? 'Guest';
if (isset($user?->profile)) {
// Do something with $user->profile
}
<?php
$name = $user->name ?? 'Guest';
if (isset($user->profile)) {
// Do something with $user->profile
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-parentheses¶help
Detects redundant parentheses around expressions.
<?php
$foo = (42);
<?php
$foo = 42;
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-readonly¶help
Detects redundant readonly modifiers on properties.
<?php
readonly class User
{
public readonly $name;
}
<?php
readonly class User
{
public $name;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-string-concat¶help
Detects redundant string concatenation expressions.
<?php
$foo = "Hello" . " World";
<?php
$foo = "Hello World";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-write-visibility¶help
Detects redundant write visibility modifiers on properties.
<?php
final class User
{
public public(set) $name;
}
<?php
final class User
{
public $name;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "help" |
no-redundant-yield-from¶help
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.
<?php
function gen(): Generator {
yield from [1];
yield from ['foo' => new stdClass()];
}
<?php
function gen(): Generator {
yield 1;
yield 'foo' => new stdClass();
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "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-change¶error
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.
<?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)");
<?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');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-insecure-comparison¶error
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.
<?php
if ($storedToken == $userToken) {
// Vulnerable to timing attacks
}
<?php
if (hash_equals($storedToken, $userToken)) {
// Valid token
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-literal-password¶error
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.
<?php
$password = "supersecret";
<?php
$password = getenv('DB_PASSWORD');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-unescaped-output¶error
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.
<?php
// This is a major XSS vulnerability.
echo $_GET['user_comment'];
<?php
echo esc_html( $user_comment );
?>
<a href="<?php echo esc_url( $user_provided_url ); ?>">Link</a>
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
sensitive-parameter¶error
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.
<?php
function login(string $username, string $password): void {
// ...
}
<?php
function login(string $username, #[SensitiveParameter] string $password): void {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
tainted-data-to-sink¶error
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.
<?php
// This is considered unsafe:
echo $_GET['name'] ?? '';
<?php
// Properly escape data before using a sink like `echo`
echo htmlspecialchars($_GET['name'] ?? '', ENT_QUOTES, 'UTF-8');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
known-sink-functions | array | ["printf"] |
level | string | "error" |
disallowed-functions¶warning
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.' },
]
<?php
curl_init(); // Error: part of a disallowed extension
<?php
function allowed_function(): void {
// ...
}
allowed_function(); // Not flagged
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
extensions | array | [] |
functions | array | [] |
level | string | "warning" |
disallowed-type-instantiation¶warning
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' },
]
}
<?php
// Direct instantiation of disallowed type
$client = new HttpService\Client();
// Another disallowed instantiation
$db = new DatabaseConnection('localhost', 'user', 'pass');
<?php
// Using factory pattern instead of direct instantiation
$client = ClientProvider::getClient();
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
types | array | [] |
no-roles-as-capabilities¶warning
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.
<?php
// This check is brittle and will fail if roles are customized.
if ( current_user_can( 'editor' ) ) { /* ... */ }
<?php
if ( current_user_can( 'edit_posts' ) ) { /* ... */ }
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-short-opening-tag¶warning
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.
<?
echo "Hello, World!";
<?php
echo "Hello, World!";
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
require-preg-quote-delimiter¶warning
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.
<?php
// If $user_input contains '/', the regex will be invalid.
$pattern = '/' . preg_quote( $user_input ) . '/';
<?php
// The delimiter is provided, ensuring it gets escaped if necessary.
$pattern = '/' . preg_quote( $user_input, '/' ) . '/';
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-debug-symbols¶note
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.
<?php
function process_request(array $data) {
var_dump($data); // Debug call that should be removed
// ...
}
<?php
// Production-safe code
error_log('Processing user request.');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |
Sûreté
Constructions qui compilent mais explosent à l'exécution. Ces règles attrapent les pièges avant les utilisateurs.
no-error-control-operator¶error
Detects the use of the error control operator @.
The error control operator suppresses errors and makes debugging more difficult.
<?php
$result = @file_get_contents('example.txt');
<?php
try {
$result = file_get_contents('example.txt');
} catch (Throwable $e) {
// Handle error
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-eval¶error
Detects unsafe uses of the eval construct.
The eval construct executes arbitrary code, which can be a major security risk if not used carefully.
<?php
eval('echo "Hello, world!";');
<?php
// Safe alternative to eval
$result = json_decode($jsonString);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-ffi¶error
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.
<?php
use FFI;
$ffi = FFI::cdef("void* malloc(size_t size);");
$ffi->malloc(1024); // Allocate memory but never free it
<?php
// Using a safe alternative to FFI
$data = 'some data';
$hash = hash('sha256', $data);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-global¶error
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.
<?php
function foo(): void {
global $bar;
// ...
}
<?php
function foo(string $bar): void {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-request-variable¶error
Detects the use of the $_REQUEST variable, which is considered unsafe.
Use $_GET, $_POST, or $_COOKIE instead for better clarity.
<?php
$identifier = $_REQUEST['id'];
<?php
$identifier = $_GET['id'];
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-shell-execute-string¶error
Detects the use of shell execute strings (...) in PHP code.
<?php
$output = `ls -l`;
<?php
$output = shell_exec('ls -l');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-unsafe-finally¶error
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.
<?php
function example(): int {
try {
return get_value();
} finally {
return 42; // Unsafe control flow statement in finally block
}
}
<?php
function example(): int {
try {
return get_value();
} finally {
// no control flow statements
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
no-request-all¶warning
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.
<?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();
// ...
}
}
<?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']);
// ...
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-service-state-mutation¶warning
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.
<?php
namespace App\Service;
final class InvoiceService
{
private int $processedCount = 0;
public function process(Invoice $invoice): void
{
$this->processedCount++;
}
}
<?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);
}
}
| Option | Type | Défaut |
|---|---|---|
allowed-methods | array | ["__construct","reset"] |
enabled | boolean | false |
exclude-namespaces | array | ["App\\Entity\\","App\\DTO\\","App\\ValueObject\\"] |
include-namespaces | array | ["App\\"] |
level | string | "warning" |
reset-interfaces | array | ["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-only¶error
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.
<?php
test('example test', function () {
expect(true)->toBeTrue();
})->only();
it('does something', function () {
expect(1)->toBe(1);
})->only();
<?php
test('example test', function () {
expect(true)->toBeTrue();
});
it('does something', function () {
expect(1)->toBe(1);
});
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "error" |
assert-description¶warning
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.
<?php
assert($user->isActivated());
<?php
assert($user->isActivated(), 'User MUST be activated at this point.');
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
identity-comparison¶warning
Detects equality and inequality comparisons that should use identity comparison operators.
<?php
if ($a == $b) {
echo '$a is same as $b';
}
<?php
if ($a === $b) {
echo '$a is same as $b';
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
ineffective-format-ignore-next¶warning
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.
<?php
// This doesn't work - marker is inside an array literal
$arr = [ // @mago-format-ignore-next
1,
2,
];
<?php
// This works - marker is before a statement
// @mago-format-ignore-next
const GRID = [
[1, 2, 3], [1, 2, ], [0, 0],
];
foo();
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
ineffective-format-ignore-region¶warning
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.
<?php
// This doesn't work - markers are inside a function call
foo( // @mago-format-ignore-start
$x,
$y
// @mago-format-ignore-end
);
<?php
// This works - markers are between statements
// @mago-format-ignore-start
$x = 1; $y = 2; // preserved as-is
// @mago-format-ignore-end
foo();
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-assign-in-argument¶warning
Detects assignments in function call arguments which can lead to unexpected behavior and make the code harder to read and understand.
<?php
foo($x = 5);
<?php
$x = 5;
foo($x);
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-assign-in-condition¶warning
Detects assignments in conditions which can lead to unexpected behavior and make the code harder to read and understand.
<?php
if ($x = 1) {
// ...
}
<?php
$x = 1;
if ($x == 1) {
// ...
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-dead-store¶warning
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().
<?php
function f() {
$x = 1; // dead - overwritten before being read
$x = compute();
return $x;
}
<?php
function f() {
$x = compute();
return $x;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-empty-catch-clause¶warning
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.
<?php
try {
// some code
} catch(Exception $e) {
// This block is empty and swallows the exception.
}
<?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());
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
no-redundant-variable¶warning
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.
<?php
function greet(string $name): string
{
$unused = compute_something();
return "Hello, $name!";
}
<?php
function greet(string $name): string
{
$greeting = "Hello, $name!";
return $greeting;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-unused-closure-capture¶warning
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().
<?php
$base = 10;
$add = function (int $x) use ($base): int {
return $x;
};
<?php
$base = 10;
$add = function (int $x) use ($base): int {
return $x + $base;
};
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-unused-global¶warning
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.
<?php
function f(): void {
global $forgotten;
}
<?php
function bump(): void {
global $counter;
$counter++;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
no-unused-static¶warning
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.
<?php
function f(): void {
static $forgotten = 0;
}
<?php
function next_id(): int {
static $counter = 0;
return ++$counter;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
strict-assertions¶warning
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.
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SomeTest extends TestCase
{
public function testSomething(): void
{
$this->assertEquals(42, 42);
}
}
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SomeTest extends TestCase
{
public function testSomething(): void
{
$this->assertSame(42, 42);
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
strict-behavior¶warning
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.
<?php
in_array(1, ['foo', 'bar', 'baz']);
<?php
in_array(1, ['foo', 'bar', 'baz'], strict: true);
| Option | Type | Défaut |
|---|---|---|
allow-loose-behavior | boolean | false |
enabled | boolean | true |
level | string | "warning" |
strict-types¶warning
Detects missing declare(strict_types=1); statement at the beginning of the file.
Cette règle requiert PHP 7.0.0 ou supérieur.
<?php
echo "Hello, World!";
<?php
declare(strict_types=1);
echo "Hello, World!";
| Option | Type | Défaut |
|---|---|---|
allow-disabling | boolean | false |
enabled | boolean | true |
level | string | "warning" |
switch-continue-to-break¶warning
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.
<?php
switch ($value) {
case 1:
echo 'one';
continue;
}
<?php
switch ($value) {
case 1:
echo 'one';
break;
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | false |
level | string | "warning" |
use-specific-assertions¶warning
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.
<?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);
}
}
<?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);
}
}
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "warning" |
invalid-open-tag¶note
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.
<php?
echo 'Hello, world!';
<?php
echo 'Hello, world!';
| Option | Type | Défaut |
|---|---|---|
enabled | boolean | true |
level | string | "note" |