carthage-software/mago
Mago is an extremely fast PHP linter, formatter, and static analyzer written in Rust. It brings Rust-inspired speed and reliability to PHP projects with a modern toolchain and great developer experience, plus multiple install options (script, Homebrew, Composer).
This document details the rules available in the Safety category.
| Rule | Code |
|---|---|
| No Error Control Operator | no-error-control-operator |
| No Eval | no-eval |
| No FFI | no-ffi |
| No Global | no-global |
| No Request All | no-request-all |
| No Request Variable | no-request-variable |
| No Service State Mutation | no-service-state-mutation |
| No Shell Execute String | no-shell-execute-string |
| No Unsafe Finally | no-unsafe-finally |
no-error-control-operatorDetects the use of the error control operator @.
The error control operator suppresses errors and makes debugging more difficult.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
try {
$result = file_get_contents('example.txt');
} catch (Throwable $e) {
// Handle error
}
<?php
$result = [@file_get_contents](https://github.com/file_get_contents)('example.txt');
no-evalDetects unsafe uses of the eval construct.
The eval construct executes arbitrary code, which can be a major security risk if not used carefully.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
// Safe alternative to eval
$result = json_decode($jsonString);
<?php
eval('echo "Hello, world!";');
no-ffiDetects 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.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
// Using a safe alternative to FFI
$data = 'some data';
$hash = hash('sha256', $data);
<?php
use FFI;
$ffi = FFI::cdef("void* malloc(size_t size);");
$ffi->malloc(1024); // Allocate memory but never free it
no-globalDetects 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.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
function foo(string $bar): void {
// ...
}
<?php
function foo(): void {
global $bar;
// ...
}
no-request-allDetects 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.
Laravel| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?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']);
// ...
}
}
<?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();
// ...
}
}
no-request-variableDetects the use of the $_REQUEST variable, which is considered unsafe.
Use $_GET, $_POST, or $_COOKIE instead for better clarity.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
$identifier = $_GET['id'];
<?php
$identifier = $_REQUEST['id'];
no-service-state-mutationDetects 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.
Symfony| Option | Type | Default |
|---|---|---|
enabled |
boolean |
false |
level |
string |
"warning" |
include-namespaces |
array |
["App\\"] |
exclude-namespaces |
array |
["App\\Entity\\","App\\DTO\\","App\\ValueObject\\"] |
allowed-methods |
array |
["__construct","reset"] |
reset-interfaces |
array |
["Symfony\\Contracts\\Service\\ResetInterface"] |
<?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);
}
}
<?php
namespace App\Service;
final class InvoiceService
{
private int $processedCount = 0;
public function process(Invoice $invoice): void
{
$this->processedCount++;
}
}
no-shell-execute-stringDetects the use of shell execute strings (...) in PHP code.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
$output = shell_exec('ls -l');
<?php
$output = `ls -l`;
no-unsafe-finallyDetects control flow statements in finally blocks.
Control flow statements in finally blocks override control flows from try and catch blocks,
leading to unexpected behavior.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
function example(): int {
try {
return get_value();
} finally {
// no control flow statements
}
}
<?php
function example(): int {
try {
return get_value();
} finally {
return 42; // Unsafe control flow statement in finally block
}
}
How can I help you explore Laravel packages today?