Vorbereitung auf PHP Interviews

PHP Interviewfragen

15 ausgewählte PHP Backend-Interviewfragen, gruppiert nach Erfahrungsstufe. Nutzen Sie diese, um Grundlagen, praktische Kompromisse und Überlegungen für die Produktionsumgebung auf Senior-Niveau zu überprüfen.

PHP KI-Interview startenKeine Kreditkarte erforderlich. 1 kostenlose Sitzung verfügbar.
Technisches Interview-Training auf EnglischEin Modus, in dem Nicht-Muttersprachler technische Interviews üben können.

Junior Fragen

1Wie unterscheidet sich das Typsystem von PHP zwischen schwacher und strikter Typisierung, und welche Änderungen ergeben sich, wenn `declare(strict_types=1)` aktiviert ist?

PHP ist standardmäßig schwach/koerzitiv bei skalaren Typdeklarationen: Wenn eine Funktion skalare Parameter- oder Rückgabetypen deklariert, kann PHP kompatible Werte wie numerische Strings in Zahlen umwandeln, anstatt sie abzulehnen. `declare(strict_types=1)` macht die Überprüfung skalarer Typen für die betreffende Datei strikt, sodass inkompatible skalare Werte nicht koerziert werden und ein `TypeError` geworfen wird. Die Direktive ist dateibezogen: Für Argumente benutzerdefinierter Funktionen kontrolliert die Datei des Aufrufenden die Striktness; für Rückgabeanweisungen kontrolliert die Datei, in der die Funktion definiert ist, die Striktness des Rückgabewerts. Sie macht PHP nicht global strikt oder deaktiviert alle Konvertierungen in der Sprache.

<?php
// lib.php
function takesInt(int $x): int {
    return $x;
}

// weak.php, no strict_types
echo takesInt('123'); // coerces string to int

// strict.php
declare(strict_types=1);
echo takesInt('123'); // TypeError
Diese Frage mit einem KI-Coach beantworten

2Erklären Sie den Unterschied zwischen `==` und `===` in PHP, einschließlich der Fallstricke bei numerischen Strings, Arrays, Objekten und `in_array()`.

`==` führt einen losen Vergleich durch, sodass PHP Typen vor dem Vergleich umwandeln kann (type juggling); `===` führt einen strikten Vergleich durch, der sowohl den gleichen Wert als auch den gleichen Typ ohne implizite Typumwandlung (type coercion) erfordert. Numerische Strings sind eine häufige Falle, da sie in losen Vergleichen als Zahlen behandelt werden können. Arrays und Objekte haben ihre eigenen Vergleichsregeln: Arrays werden nach Schlüsseln und Werten verglichen, während `==` für Objekte klassengleiche Objekte anhand ihrer Eigenschaften vergleicht und `===` für Objekte exakt die gleiche Instanz erfordert. `in_array()` verwendet standardmäßig den losen Vergleich, daher sollte `true` als drittes Argument übergeben werden, wenn der Typ übereinstimmen muss.

<?php
var_dump(123 == '123');      // loose: numeric string is converted
var_dump(123 === '123');     // strict: int !== string

$values = [0, 1, 2];
var_dump(in_array('0', $values));       // loose
var_dump(in_array('0', $values, true)); // strict
Diese Frage mit einem KI-Coach beantworten

3Wie unterscheiden sich `mixed`, `void` und `never` als PHP-Rückgabetypen, und welche Garantien bieten sie?

Als Rückgabetypen geben `mixed`, `void` und `never` unterschiedliche Garantien. `mixed` bedeutet, dass die Funktion jeden Wert zurückgeben kann, einschließlich `null`. `void` bedeutet, dass die Funktion nicht dazu gedacht ist, einen sinnvollen Wert zurückzugeben; sie darf `return;` verwenden, aber keinen Ausdruck zurückgeben. `never` bedeutet, dass die Funktion nicht normal zu ihrem Aufrufer zurückkehrt: sie muss immer eine Ausnahme werfen, das Programm beenden oder den Kontrollfluss anderweitig terminieren. `never` ist strenger als `void`, da die Ausführung nach einem normalen Aufruf einer `never`-Funktion nicht fortgesetzt werden kann.

<?php
function parse(string $raw): mixed {
    return json_decode($raw, true);
}

function logMessage(string $message): void {
    error_log($message);
    return; // allowed
}

function fail(string $message): never {
    throw new RuntimeException($message);
}

logMessage('starting');
fail('stop');
echo 'unreachable';
Diese Frage mit einem KI-Coach beantworten

4Was ist `iterable` in PHP, und wie steht es in Beziehung zu `array`, `Traversable`, `Iterator` und `Generator`?

In PHP ist `iterable` eine Typdeklaration, die "ein `array` oder ein Objekt, das `Traversable` implementiert" bedeutet. `Traversable` ist das Basis-Marker-Interface für Objekte, die von `foreach` verwendet werden können; benutzerdefinierter Code implementiert normalerweise `Iterator` oder `IteratorAggregate`, welche `Traversable` erweitern. Ein `Generator` ist das Objekt, das von einer Generatorfunktion unter Verwendung von `yield` zurückgegeben wird; es implementiert `Iterator` und ist daher auch `Traversable` und wird überall dort akzeptiert, wo `iterable` erforderlich ist.

function printItems(iterable $items): void {
    foreach ($items as $item) {
        echo $item, PHP_EOL;
    }
}

printItems(['a', 'b']);

function letters(): Generator {
    yield 'x';
    yield 'y';
}

printItems(letters());
Diese Frage mit einem KI-Coach beantworten

5Was sind die Sichtbarkeitsregeln `public`, `protected` und `private` in PHP, und wie interagieren sie mit Vererbung und Traits?

In PHP sind `public`-Member von überall her zugänglich, `protected`-Member sind von der deklarierenden Klasse und Unterklassen zugänglich, und `private`-Member sind nur von der Klasse zugänglich, die sie deklariert. Bei der Vererbung können Unterklassen geerbte `public`- und `protected`-Member verwenden, aber die `private`-Member einer Elternklasse sind nicht als geerbte API (Application Programming Interface) zugänglich. Beim Überschreiben von Methoden kann eine Unterklasse die Sichtbarkeit im Allgemeinen nicht reduzieren; beispielsweise muss eine `public`-Elternmethode `public` bleiben. Traits werden in die verwendende Klasse eingebunden, und Trait-Member folgen den PHP-Sichtbarkeitsregeln. Die Sichtbarkeit von Trait-Methoden kann auch beim Import des Traits mit `as` angepasst oder umbenannt werden.

<?php
class Base {
    public function a() {}
    protected function b() {}
    private function c() {}
}

trait Logs {
    public function log(string $message): void {}
}

class Child extends Base {
    use Logs { log as protected writeLog; }

    public function test(): void {
        $this->a();              // OK
        $this->b();              // OK
        // $this->c();           // Not accessible: private to Base
        $this->writeLog('ok');   // OK inside class, protected alias
    }
}

$child = new Child();
$child->a();
// $child->b();           // Not accessible from outside
// $child->writeLog('x'); // Not accessible from outside
Diese Frage mit einem KI-Coach beantworten

6Wie funktionieren Traits in PHP, und wie werden Konflikte gelöst, wenn mehrere Traits dieselbe Methode definieren?

Traits sind PHPs Mechanismus zur horizontalen Code-Wiederverwendung: Eine Klasse kann einen oder mehrere Traits verwenden, und die Member des Traits werden in die Klasse integriert. Traits sind keine eigenständigen Klassen und können nicht direkt instanziiert werden. Wenn mehrere Traits Methoden mit demselben Namen bereitstellen, erfordert PHP eine explizite Konfliktlösung. Der `insteadof`-Operator wählt aus, welche Trait-Methode für den in Konflikt stehenden Namen verwendet wird. Der `as`-Operator kann einen Alias für eine Trait-Methode erstellen und auch die Sichtbarkeit der importierten Methode ändern. Klassenmethoden überschreiben Trait-Methoden, und Trait-Methoden haben Vorrang vor geerbten übergeordneten Methoden.

<?php
trait JsonLogger {
    public function log(string $message): void {
        echo "json:$message\n";
    }
}

trait FileLogger {
    public function log(string $message): void {
        echo "file:$message\n";
    }
}

class Service {
    use JsonLogger, FileLogger {
        JsonLogger::log insteadof FileLogger;
        FileLogger::log as logToFile;
        JsonLogger::log as protected logJson;
    }
}

$service = new Service();
$service->log('hello');
$service->logToFile('hello');
// $service->logJson('hello'); // not accessible from outside; protected alias
Diese Frage mit einem KI-Coach beantworten

7Was sind PHP Enums, einschließlich reiner (pure) und Backed Enums, und wie verbessern sie die Domänenmodellierung?

PHP Enums definieren einen geschlossenen Satz benannter Fälle für einen Typ, wie z.B. Bestellstatus oder Benutzerrollen. Reine Enums (Pure Enums) haben nur benannte Fälle; Backed Enums weisen jedem Fall einen eindeutigen String- oder Int-Wert zu, was für die Speicherung, APIs (Application Programming Interfaces) oder Interoperabilität nützlich ist. Enums verbessern die Domänenmodellierung, indem sie anfällige rohe Strings, Ganzzahlen oder Konstanten durch typensichere, selbstdokumentierende Werte ersetzen. PHP Enums können auch Methoden haben und Schnittstellen implementieren; alle Enums unterstützen das Auflisten ihrer Fälle, und Backed Enums unterstützen die Konvertierung zu und von ihren skalaren Werten.

enum OrderState
{
    case Draft;
    case Paid;
    case Shipped;
}

enum PaymentStatus: string
{
    case Pending = 'pending';
    case Complete = 'complete';
    case Failed = 'failed';
}

$status = PaymentStatus::tryFrom('complete');
var_dump($status === PaymentStatus::Complete);
Diese Frage mit einem KI-Coach beantworten

Middle Fragen

8Wie drücken skalare Typen, Nullable-Typen, Union-Typen, Intersection-Typen und DNF-Typen PHP-API-Verträge aus, und wann sollte jeder davon verwendet werden?

PHP-Typdeklarationen beschreiben die Werte, die eine API (Application Programming Interface) akzeptiert oder zurückgibt. Skalare Typen wie `int`, `float`, `string` und `bool` werden verwendet, wenn genau eine primitive Art erwartet wird. Nullable-Typen, geschrieben als `?T` oder `T|null`, erlauben entweder einen Wert vom Typ `T` oder `null`. Union-Typen wie `A|B` erlauben eine von mehreren Alternativen. Intersection-Typen wie `A&B` erfordern, dass ein Wert alle aufgelisteten Typen erfüllt, typischerweise mehrere Schnittstellen. DNF-Typen (Disjunktive Normalform) kombinieren Unions und Intersections, zum Beispiel `(A&B)|C`, um komplexere Verträge auszudrücken. Jeder Typ sollte verwendet werden, wenn er dem tatsächlichen API-Versprechen entspricht, um unnötig breite oder verwirrende Typen zu vermeiden.

<?php
interface Cacheable {}

class Report implements Cacheable, JsonSerializable {
    public function jsonSerialize(): mixed { return []; }
}

function findName(int $id): ?string {
    return $id > 0 ? 'Alice' : null;
}

function normalize(int|string $id): string {
    return (string) $id;
}

function store(Cacheable&JsonSerializable $item): void {
    // must satisfy both interfaces
}

function handle((Cacheable&JsonSerializable)|string $input): void {
    // either an object satisfying both contracts, or a string
}
Diese Frage mit einem KI-Coach beantworten

9Was ist der Unterschied zwischen `callable`, `Closure`, First-Class Callables, Invokable Objects und `callable` Arrays in PHP?

`callable` ist ein weit gefasster Typ/Vertrag für jeden Wert, den PHP aufrufen kann, wie z. B. eine Funktionsnamen-Zeichenkette, eine `Closure`, ein invokable object oder ein Methodenreferenz-Array wie `[$object, 'method']` oder `[ClassName::class, 'method']`. Eine `Closure` ist ein konkretes anonymes Funktions-Objekt und selbst aufrufbar (`callable`). Die Syntax für First-Class Callables, wie `$obj->method(...)` oder `strlen(...)`, erstellt eine `Closure` aus einem `callable` Ausdruck. Ein invokable object ist `callable`, weil es die Methode `__invoke()` definiert. `callable` Arrays sind die traditionelle, zwei Elemente umfassende Form der Methodenreferenz. `is_callable()` kann zur Laufzeit verwendet werden, um die Aufrufbarkeit zu überprüfen.

class Greeter {
    public function hello(string $name): string { return "Hello $name"; }
    public function __invoke(string $name): string { return "Hi $name"; }
}

$g = new Greeter();

$closure = fn(string $name) => "Hey $name";
$arrayCallable = [$g, 'hello'];
$firstClass = $g->hello(...);
$invokable = $g;

foreach ([$closure, $arrayCallable, $firstClass, $invokable] as $cb) {
    echo $cb('Sam'), PHP_EOL;
}
Diese Frage mit einem KI-Coach beantworten

10Wie geht PHP mit Integer-Überlauf, Gleitkomma-Genauigkeit und exakten Dezimalberechnungen um?

PHP-Integer sind plattformabhängig dimensioniert, mit Grenzen, die durch Konstanten wie `PHP_INT_MAX` angezeigt werden. PHP behält native Integer-Ergebnisse nach einem Überlauf nicht automatisch als Integer beliebiger Genauigkeit bei; überlaufende Integer-Arithmetik kann zu einem `float` werden oder anderweitig die exakte Integer-Semantik verlieren. PHP-Floats sind binäre Gleitkommawerte, daher sind viele Dezimalbrüche, wie `0.1`, nur annähernd genau. Für exakte Dezimal- oder Geldberechnungen sollten Floats vermieden und stattdessen ein exakter Ansatz wie Integer-Untereinheiten (minor units) verwendet werden, wo angebracht, oder Tools für Festkomma-/beliebige Genauigkeit bei Dezimalzahlen wie BCMath. GMP ist nützlich für Integer-Arithmetik beliebiger Genauigkeit.

var_dump(0.1 + 0.2 === 0.3);
printf('%.17f\n', 0.1 + 0.2);
Diese Frage mit einem KI-Coach beantworten

11Erklären Sie PHP-Referenzen (`&`), Copy-on-Write und wie Referenzen unerwartete Nebenwirkungen erzeugen können.

PHP-Variablen werden intern durch zvals repräsentiert. Für Werte wie Arrays und Strings verwendet PHP Copy-on-Write: Die Zuweisung `$b = $a` teilt normalerweise den gleichen zugrunde liegenden Wert, bis eine Variable geändert wird; zu diesem Zeitpunkt trennt/kopiert PHP sie. Eine mit `&` erstellte Referenz macht Variablen zu Aliasen desselben Containers, sodass eine Änderung an einer Variable die andere ändert und das Trennverhalten umgehen kann, das man von normalen Zuweisungen erwartet. Die Übergabe per Referenz ermöglicht es einer Funktion, die Variable des Aufrufers zu ändern. Referenzen können überraschende Nebenwirkungen verursachen, insbesondere bei `foreach ($array as &$value)`: Die Schleifenvariable bleibt nach der Schleife eine Referenz auf das letzte Element, sodass eine spätere Wiederverwendung dieses Elements versehentlich überschreiben kann, es sei denn, Sie führen ein `unset($value)` durch.

$a = [1, 2];
$b = $a;      // shared until modification
$b[0] = 99;   // separates; $a is unchanged
var_dump($a, $b);

$c =& $a;     // alias/reference
$c[0] = 42;   // modifies $a too
var_dump($a, $c);
Diese Frage mit einem KI-Coach beantworten

12Wie funktionieren Objektreferenzen, Zuweisung, `clone`, `__clone`, flache Kopie (shallow copy) und tiefe Kopie (deep copy) in PHP?

In PHP speichert eine Objektvariable eine Referenz auf ein Objekt, nicht eine vollständige Kopie des Objekts. Die Zuweisung `$b = $a` kopiert die Referenz, sodass beide Variablen auf dasselbe Objekt verweisen. Änderungen am Objekt über eine der Variablen sind auch über die andere sichtbar, ohne die Verwendung von `&`. `clone $a` erstellt eine neue Objektinstanz mit einer flachen Kopie der Eigenschaften des ursprünglichen Objekts. Wenn eine Eigenschaft selbst ein Objekt ist, verweist die Eigenschaft des geklonten Objekts immer noch auf dasselbe verschachtelte Objekt, es sei denn, Sie klonen es explizit. Die Methode `__clone()` wird nach der flachen Kopie auf dem neuen Objekt aufgerufen und ist der Ort, um IDs zurückzusetzen, Ressourcen zu trennen oder eine tiefe Kopie von verschachtelten Objekten durchzuführen. Eine tiefe Kopie bedeutet das rekursive Kopieren der verschachtelten, veränderlichen Objekte, die nicht geteilt werden sollen.

class Address {
    public function __construct(public string $city) {}
}

class User {
    public function __construct(public string $name, public Address $address) {}

    public function __clone() {
        $this->address = clone $this->address; // make nested object independent
    }
}

$u1 = new User('Ann', new Address('Paris'));
$u2 = $u1;          // same object handle
$u2->name = 'Beth';
echo $u1->name, PHP_EOL;

$u3 = clone $u1;    // new User object; __clone deep-clones address
$u3->address->city = 'Rome';
echo $u1->address->city, PHP_EOL;
echo $u3->address->city, PHP_EOL;
Diese Frage mit einem KI-Coach beantworten

Senior Fragen

13Wie würden Sie einen Mangel an Datenbankverbindungen beheben und das Verbindungsmanagement für PHP-FPM (PHP FastCGI Process Manager) und langlebige Worker gestalten?

Um einen Mangel an Datenbankverbindungen zu beheben, würde ich Datenbankverbindungslimits und aktuelle Sessions mit der gesamten PHP-Parallelität vergleichen: Jeder aktive PHP-FPM-Child-Prozess kann seine eigene Datenbankverbindung halten, und mehrere Anwendungspools, Hosts, Cron-Jobs, Warteschlangen-Worker, Migrationen und Deployments vervielfachen den Bedarf. Ich würde Datenbank-Session-Ansichten, Verbindungsstatus, Sessions mit Leerlauf in Transaktionen, langsame Abfragen, Sperrwartezeiten, Fehlerprotokolle, Pool-Metriken und aktuelle Verkehrs- oder Worker-Anzahländerungen überprüfen. Für PHP-FPM sollte man `max_children` und die Parallelität pro Host innerhalb des Datenbankbudgets halten, kurze, an den Request gebundene Transaktionen/Verbindungen verwenden, umgehend committen oder zurückrollen und das Halten von Verbindungen während langsamer externer Aufrufe vermeiden. Persistente PDO (PHP Data Objects)-Verbindungen können den Verbindungs-Overhead reduzieren, aber sie können viele inaktive Verbindungen pro FPM-Prozess belegen und den Session-Status beibehalten, daher ist Vorsicht geboten. Ein Pooler oder Proxy wie PgBouncer kann viele PHP-Clients auf weniger Datenbankserver-Verbindungen multiplexen, wenn die Anwendung kompatibel ist. Langlebige Worker benötigen eine explizite Handhabung des Verbindungslebenszyklus: Gesundheitsprüfungen, Neuverbindung nach Inaktivitäts-Timeouts/Neustarts/Failover/Forks, Zurücksetzen des Session-Status und Wiederholung nur sicherer oder idempotenter Arbeit.

Example:
- 6 application servers
- 40 PHP-FPM max_children per server
- 2 separate FPM pools that can hit the database

Potential concurrent PHP request DB connections = 6 * 40 * 2 = 480

If PostgreSQL max_connections is 300 and queues/cron/admin tools also connect,
connection exhaustion is expected during traffic spikes.
Diese Frage mit einem KI-Coach beantworten

14Wie würden Sie Lese-Replikate, Lese-/Schreib-Splitting, Replikationsverzögerung und das Failover der primären Datenbank in PHP handhaben?

Die Handhabung von Lese-Replikativen und Failover in PHP erfordert die Aufteilung des Lese- und Schreibverkehrs, die Reduzierung der Replikationsverzögerung und die Verwaltung von Verbindungsunterbrechungen während des Failovers. Auf Anwendungsebene leitet die Datenbankkonfiguration oder Middleware Leseabfragen (`SELECT`) an Lese-Replikate und Schreibabfragen (`INSERT`, `UPDATE`, `DELETE`) an die primäre Instanz weiter (alternativ verwaltet über Datenbank-Proxies wie ProxySQL oder AWS RDS Proxy). Da die Replikation asynchron ist, kann eine Replikationsverzögerung zu veralteten Lesevorgängen führen. Um eine 'read-your-writes'-Konsistenz zu gewährleisten, sollte die Anwendung Lesevorgänge innerhalb einer aktiven Transaktion an die primäre Datenbank leiten und nach einem Schreibvorgang nachfolgende Lesevorgänge für eine spezifische Benutzersitzung oder den Anfragelebenszyklus vorübergehend an die primäre Datenbank binden. Während eines Primär-Failovers stufen moderne Datenbank-Cluster ein Replikat hoch und aktualisieren DNS-/Proxy-Endpunkte. In PHP muss die Verbindungsschicht Verbindungsverluste und Nur-Lese-Fehler abfangen, veraltete PDO-Sockets verwerfen und kontrollierte Wiederholungsversuche mit exponentiellem Backoff anwenden.

<?php
// config/database.php
return [
    'mysql' => [
        'read' => [
            'host' => [env('DB_REPLICA_HOST_1'), env('DB_REPLICA_HOST_2')],
        ],
        'write' => [
            'host' => [env('DB_PRIMARY_HOST')],
        ],
        'sticky'    => true,
        'driver'    => 'mysql',
        'database'  => env('DB_DATABASE'),
        'username'  => env('DB_USERNAME'),
        'password'  => env('DB_PASSWORD'),
        'charset'   => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
    ],
];
Diese Frage mit einem KI-Coach beantworten

15Wie werden Datenbankmigrationen und Schemaänderungen in PHP-Anwendungen sicher und mit null oder minimaler Ausfallzeit bereitgestellt?

Die Bereitstellung von Zero-Downtime-Datenbankmigrationen in PHP-Anwendungen erfordert die Entkopplung der Schema-Entwicklung von der Bereitstellung des Anwendungscodes mithilfe des Expand-and-Contract-Musters (Parallel Run) zusammen mit nicht-blockierenden Online-DDL-Praktiken (Data Definition Language). In der **Expand-Phase** werden rückwärtskompatible Schemaänderungen angewendet (z.B. das Hinzufügen von Nullable-Spalten, neuen Tabellen oder gleichzeitigen Indizes unter Verwendung von Tools wie `pt-online-schema-change`, `gh-ost` oder nativer Online-DDL in `MySQL`/`PostgreSQL`). Die Anwendung wird dann bereitgestellt, um Dual-Writing zu unterstützen (Schreiben in alte und neue Schema-Strukturen), während von beiden gelesen wird. Historische Datensätze werden dann asynchron in gedrosselten Hintergrund-Batches nachgefüllt, um das Sperren von Tabellen oder das Verursachen von Replikationsverzögerungen zu verhindern. In der **Contract-Phase**, sobald die Datenkonsistenz validiert und die Lesezugriffe vollständig auf die neue Struktur umgestellt sind, entfernt eine Code-Bereitstellung Verweise auf das alte Schema. Schließlich löscht eine Bereinigungsmigration veraltete Spalten oder Tabellen. Ein sicheres Rollback erfordert, dass jeder Zwischen-Schema-Zustand während rollierender Releases sowohl mit der vorherigen als auch mit der eingehenden Anwendungsversion vollständig kompatibel bleibt.

<?php
// Step 1: Migration (Expand) -> Add new column as nullable
Schema::table('users', function (Blueprint $table) {
    $table->string('full_name')->nullable()->after('name');
});

// Step 2: Code deployment -> Write to both, read with fallback
class User extends Model {
    public function setNameAttribute($value) {
        $this->attributes['name'] = $value;
        $this->attributes['full_name'] = $value; // Dual write
    }
}

// Step 3: Background worker backfills existing rows in small batches
// Step 4: Code deployment -> Read/write exclusively from 'full_name'
// Step 5: Migration (Contract) -> Drop legacy 'name' column
Diese Frage mit einem KI-Coach beantworten