← Alle Artikel

CarbonImmutable per Default: ein Drei-Zeilen-Patch, der eine Klasse Bugs erledigt

Carbon-Instanzen sind veränderlich. Das ist solange harmlos, bis du sie zwischen Methoden weiterreichst. Eine winzige Stelle in der AppServiceProvider, die das Problem grundsätzlich aus dem Weg räumt.

#Laravel #PHP

Ein kleiner Tipp aus dem Aufbau von Portchecks, den ich seit einer Weile in jedem neuen Projekt setze: Datums- und Zeitobjekte sind bei mir grundsätzlich unveränderlich.

Wo das Problem entsteht

Carbon ist standardmäßig mutable. Das heißt, jeder Aufruf wie $date->addMinutes(5) ändert das ursprüngliche Objekt – und nicht nur das, was du an die nächste Methode weiterreichst.

$nextRun = $check->next_run_at;
$soon = $nextRun->addMinutes(5);

// Beide zeigen jetzt auf dieselbe, um 5 Minuten verschobene Zeit.
// Nicht offensichtlich, aber genau das ist die Klasse Bugs,
// die du eine Stunde später debuggst.

Solange ein Carbon-Objekt nur lokal in einer Methode lebt, ist das egal. Sobald du es speicherst, an einen anderen Service übergibst oder in eine Eloquent-Property steckst, ist es ein Mienenfeld. Besonders fies: solche Bugs zeigen sich oft erst in der Queue, nie im Test.

Der Patch

Eine Zeile im boot() der AppServiceProvider:

use Illuminate\Support\Carbon;

public function boot(): void
{
    Carbon::useFactory(\Illuminate\Support\Facades\Date::useClass(\Carbon\CarbonImmutable::class));
}

Oder noch knapper, weil Laravel das längst direkt unterstützt:

\Illuminate\Support\Facades\Date::use(\Carbon\CarbonImmutable::class);

Ab dem Moment liefert now(), jede Eloquent-Cast-Spalte ($cast = ['next_run_at' => 'datetime']) und alles, was über die Date-Facade läuft, eine CarbonImmutable-Instanz. Methoden wie addMinutes() geben jetzt ein neues Objekt zurück, statt das alte zu verändern. Das ursprüngliche Objekt bleibt, was es war.

Was sich ändert

In 99 % der Fälle: nichts. Du merkst es einfach nicht. Die meisten Carbon-Operationen werden ohnehin ergebnisorientiert genutzt und zugewiesen.

Was sich ändert, sind die Stellen, an denen du dich vorher auf Mutation verlassen hast. Dort musst du das Ergebnis explizit zuweisen:

// vorher (mutable, funktioniert):
$date->addMinutes(5);
echo $date;

// nachher (immutable):
$date = $date->addMinutes(5);
echo $date;

Die Stellen findet Pest oder PHPUnit dir innerhalb einer Test-Suite zuverlässig.

Warum sich das lohnt

Weil das einer dieser Wechsel ist, die nichts Sichtbares verbessern – aber eine ganze Kategorie an stillen Bugs ausschließen. Die Sorte, bei der du dich später fragst, warum ein next_run_at plötzlich fünf Minuten in der Vergangenheit liegt, obwohl niemand die Zeile geändert hat.

In Portchecks war das eine der ersten Code-Zeilen nach dem Setup. Seitdem ein Datums-Bug weniger, mit dem ich rechnen muss.

Diese Seite nutzt ausschließlich technisch notwendige Cookies. Kein Tracking, keine Werbung.