CAP.PRESCAN.SIDE_EFFECT — Side effect in a migration
- Category: safety
- Level: 0
- Stability: stable
- Suites: lint
This migration would reach outside the database if it ran — a notification, an HTTP request, a queued job, a mail, a cache write. Pretend mode intercepts SQL and nothing else, so the PHP around it runs for real: a lint run that mailed a customer would be the worst thing this tool could do. The migration is reported and never pretend-executed.
PHP's own ways to reach out count the same as the facades: exec(), shell_exec() and a command in backticks, system(), passthru(), proc_open(), popen() and pcntl_exec() start a process as Process does, and mail() and mb_send_mail() send a mail as Mail does.
So does the container. app('cache'), resolve('mailer'), app()->make('queue'), Container::getInstance()->make('events') and App::make('filesystem') hand back what Cache, Mail, Queue, Event and Storage reach, and so do the helpers cache(), session() and broadcast(). Spatie's published create_permission_tables migration ends with app('cache')->store(…)->forget(…), and a lint run that executed it would clear a real cache key. The key must be written as it is, a string or Foo::class: a key computed at run time names nothing a reading of the file can know.
This is not a claim that the effect is wrong — sending a notification is a fine thing for application code to do. It is a claim that a lint run must not be the thing that does it. Move the effect into a deploy step or a job the deploy dispatches. Shadow mode holds the migration back as well, because the effect would leave its throwaway database just the same.
Flagged
public function up(): void
{
Schema::table('users', function (Blueprint $table): void {
$table->string('plan')->default('free');
});
// A lint run would send this for real, to real people.
Notification::send(User::all(), new PlanChanged);
}
Preferred
public function up(): void
{
Schema::table('users', function (Blueprint $table): void {
$table->string('plan')->default('free');
});
}
// The announcement is a deploy step, not a schema change. It runs when
// the deploy says so, and a lint run cannot trigger it by looking.