---
title: 'PHP 7.3 from 7.2: Flexible Heredoc, JSON Exceptions, PCRE2 & Migration | DevSense'
description: 'Upgrade-Leitfaden für PHP 7.3: Flexibles Heredoc/Nowdoc, abschließende Kommata in Aufrufen, Referenz-Destructuring, is_countable, array_key_first/last, JsonException, Argon2id, PCRE2 – und BC-Breaks, die realen Code betreffen.'
faq:
    - { question: 'Was ist die flexible Heredoc/Nowdoc-Syntax in PHP 7.3?', answer: 'PHP 7.3 erlaubt es, das schließende Label eines Heredoc oder Nowdoc einzurücken. Die Einrückung des schließenden Labels bestimmt, wie viel Einrückung von allen Zeilen innerhalb des Textkörpers entfernt wird.' }
    - { question: 'Wie funktioniert JsonException in PHP 7.3?', answer: "Durch Übergabe des Flags 'JSON_THROW_ON_ERROR' an 'json_encode()' oder 'json_decode()' wirft PHP bei einem Fehler eine 'JsonException', anstatt manuelle Prüfungen mit 'json_last_error()' zu erfordern." }
    - { question: "Was ändert sich bei der Anweisung 'continue' in einem 'switch'?", answer: "Die Verwendung von 'continue' innerhalb einer 'switch'-Anweisung erzeugt nun eine Warnung, da es sich identisch zu 'break' verhält. Um eine äußere Schleife fortzusetzen, sollte stattdessen 'continue 2' verwendet werden." }
    - { question: 'Welche Hilfsfunktionen wurden in PHP 7.3 eingeführt?', answer: "PHP 7.3 führte 'is_countable()' ein, um zu prüfen, ob eine Variable gezählt werden kann, sowie 'array_key_first()' und 'array_key_last()', um den ersten und letzten Schlüssel eines Arrays zu ermitteln." }
published: '2026-05-31'
---
# PHP 7.3: Hauptmerkmale

Haben Sie schon einmal Stunden damit verbracht, einen lautlosen JSON-Parsing-Fehler in der Produktion zu debuggen, weil Sie vergessen haben, `json_last_error()` zu prüfen? PHP 7.3 behebt dieses Problem für Entwickler (DX), indem es die `JsonException` über das Flag `JSON_THROW_ON_ERROR` einführt. Es stellt ein wichtiges Release für Sicherheit und Komfort dar, verbessert die Ergonomie der Syntax durch eingerückte Heredocs und abschließende Kommata in Aufrufen und fügt wichtige Hilfsfunktionen zur Standardbibliothek hinzu.

## Inhalt
* [Flexibles Heredoc und Nowdoc](#flexible-heredoc)
* [Abschließende Kommata in Funktions- und Methodenaufrufen](#trailing-commas-calls)
* [Referenz-Zuweisungen in `list()` und `[]`](#list-references)
* [`instanceof` mit Literalen](#instanceof-literals)
* [`CompileError`](#compile-error)
* [Neue und aktualisierte Core-Hilfsfunktionen](#core-helpers)
* [JSON: `JSON_THROW_ON_ERROR` und `JsonException`](#json-exceptions)
* [Passwort-Hashing: Argon2id](#argon2id)
* [Bemerkenswerte Erweiterungen & Stdlib](#extensions-stdlib)
* [Praktische Rezepte](#practical-recipes)
* [Häufige Fehler](#common-mistakes)
* [Einschränkungen](#limitations)
* [Rückwärtskompatibilität und BC-Breaks (Migrationshinweise)](#backward-incompatible)
* [Veraltete Features (frühzeitig beheben)](#deprecations)
* [Weitere Änderungen & Betrieb (Build, INI, Performance)](#other-changes)
* [Interaktives Quiz](#interactive-quiz)
* [Schlussgedanken](#closing-thoughts)

---

<a id="flexible-heredoc"></a>
## Flexibles Heredoc und Nowdoc

Das schließende Label muss **nicht mehr** zwingend in Spalte 0 der alten, starren Form stehen: Es darf nun **eingerückt** werden, und dieselbe Einrückung wird **von jeder Zeile** im Textkörper abgezogen. Das macht eingebettetes SQL, HTML und CLI-Hilfetext in PSR-12-freundlichem Code weitaus lesbarer.

**Achtung bei der Migration:** Wenn der Textkörper **dasselbe Token enthält** wie das schließende Label (das nun eingerückt sein kann), interpretiert PHP dies möglicherweise als **vorzeitiges Ende des Strings** – wählen Sie **eindeutige, lange Labels** (z. B. `SQL_GET_USER` statt nur `SQL`).

```php
// src/QueryBuilder.php
$query = <<<SQL
    SELECT id, name
    FROM users
    WHERE active = 1
    SQL;
```

> [!NOTE]
> Wussten Sie schon? Die Einrückung des schließenden Labels bestimmt die Einrückung, die vom Text entfernt wird. Wenn Sie den Textkörper *weniger* einrücken als das schließende Label, wirft PHP einen ParseError.

---

<a id="trailing-commas-calls"></a>
## Abschließende Kommata in Funktions- und Methodenaufrufen

Aufrufe dürfen mit einem abschließenden Komma enden – praktisch für mehrzeilige Diffs und generierte Argumentlisten:

```php
// src/LoggerService.php
$this->logger->info(
    'User saved',
    [
        'id' => $user->id,
        'ip' => $request->ip(),
    ],
);
```

Dies gilt für **Aufrufe**, nicht für die Deklaration von Funktionsparametern `function foo($a,)` in PHP 7.3 (abschließende Kommata in Parameterlisten wurden erst in PHP 8.0 eingeführt).

> [!NOTE]
> Wussten Sie schon? Abschließende Kommata in Funktions*deklarationen* (Definition von Parametern) sind in PHP 7.3 weiterhin ein Syntaxfehler. Sie werden nur bei *Aufrufen* unterstützt.

---

<a id="list-references"></a>
## Referenz-Zuweisungen in `list()` und `[]`

Destructuring kann Variablen **per Referenz** binden:

```php
// src/Destructure.php
[&$head, $middle, &$tail] = $parts;
// Dasselbe mit list():
list(&$a, $b) = $pair;
```

Verwenden Sie dies nur, wenn Sie Teile einer gemeinsam genutzten Struktur bewusst verändern möchten. Andernfalls sollten Sie Kopien (Values) bevorzugen, um den Datenfluss übersichtlich zu halten.

---

<a id="instanceof-literals"></a>
## `instanceof` mit Literalen

Die linke Seite darf ein **Literal** sein; das Ergebnis ist immer **`false`**. Dies dient hauptsächlich der **Konsistenz** und der Erleichterung der statischen Analyse, nicht der Laufzeitlogik.

```php
// src/Validation.php
$result = (123 instanceof User); // false
```

---

<a id="compile-error"></a>
## `CompileError`

`CompileError` wird als Basis für bestimmte **Fehler zur Kompilierzeit** eingeführt; `ParseError` **erbt** davon. Dies zeigt sich anfangs am deutlichsten, wenn **`token_get_all($code, TOKEN_PARSE)`** auf Parse-Probleme stößt – einige Situationen, die zuvor Fatal Errors waren, können nun als Exceptions in Entwicklungswerkzeugen abgefangen werden.

```php
// src/ErrorHandling.php
try {
    eval('invalid code');
} catch (CompileError $e) {
    echo "Kompilierung fehlgeschlagen: " . $e->getMessage();
}
```

---

<a id="core-helpers"></a>
## Neue und aktualisierte Core-Hilfsfunktionen

* **`array_key_first($array)`** / **`array_key_last($array)`** – vermeiden Sie Key-Hacks mit `reset()`/`end()`; das Verhalten ist für Ihren Array-Typ vordefiniert (siehe Handbuch für leere Arrays).
* **`is_countable($value)`** – `true` für Arrays und Objekte, die `Countable` implementieren – ersetzt die übliche Prüfung `is_array($x) || $x instanceof \Countable` vor `count()`.
* **`hrtime($as_number = false)`** – monotone hochauflösende Zeit (Nanosekunden); bevorzugen Sie dies gegenüber `microtime(true)` für **Intervalle** (keine Kalenderzeit).
* **`net_get_interfaces()`** – listet Netzwerkschnittstellen auf, wo dies unterstützt wird (plattformabhängig).

```php
// src/Helpers.php
$firstKey = array_key_first($myArray);
if (is_countable($data)) {
    $count = count($data);
}
```

> [!NOTE]
> Wussten Sie schon? `hrtime()` nutzt die monotone Uhr des Systems, die immun gegen Änderungen der Systemzeit (wie NTP-Synchronisierungen) ist. Dies macht sie zur einzigen zuverlässigen Methode, um Ausführungszeitintervalle zu messen.

---

<a id="json-exceptions"></a>
## JSON: `JSON_THROW_ON_ERROR` und `JsonException`

Übergeben Sie **`JSON_THROW_ON_ERROR`** an **`json_encode()`** / **`json_decode()`**, um eine **`JsonException`** zu erhalten, anstatt mit **`json_last_error()`** hantieren zu müssen. **`JSON_PARTIAL_OUTPUT_ON_ERROR`** hat weiterhin Vorrang, wenn beide Flags gesetzt sind.

```php
// src/JsonParser.php
try {
    $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    // Fehler behandeln
}
```

---

<a id="argon2id"></a>
## Passwort-Hashing: Argon2id

Wenn PHP mit einer ausreichend neuen **libargon2** kompiliert wurde, akzeptiert **`password_hash()`** die Option **`PASSWORD_ARGON2ID`**. Damit steht Ihnen die **i+d-Hybridvariante** von Argon2 neben der bestehenden Argon2i-Unterstützung zur Verfügung.

```php
// src/Security.php
$hash = password_hash('secret', PASSWORD_ARGON2ID);
```

---

<a id="extensions-stdlib"></a>
## Bemerkenswerte Erweiterungen & Stdlib

* **PCRE**: Engine auf **PCRE2** aktualisiert – subtile Verhaltensänderungen (strengere Zeichenklassen-Bereiche usw.); **`preg_quote()`** maskiert nun auch **`#`**.
* **Multibyte (mbstring)**: **Unicode 11**, **>2GB Strings**, schnellere Case-Operationen; **Full Case-Folding** für case-insensitiven Vergleich; **`mb_ereg_*` benannte Captures** sind näher an der PCRE-Ergonomie ausgerichtet.
* **LDAP**: LDAP-**Controls** wurden in Such-/Modifikations-APIs und **`ldap_parse_result()`** integriert; Optionen für Server-/Client-Controls wurden verbessert.
* **MySQLi / PDO MySQL**: Prepared Statements können nun **Bruchteile von Sekunden** für Spalten vom Typ `DATETIME(6)` / `TIMESTAMP(6)` zurückgeben.
* **FPM**: Neue Protokollierungsoptionen (`log_limit`, `log_buffering`, `decorate_workers_output`); **`getallheaders()`** ist unter FPM verfügbar.
* **Sessions / Cookies**: **`session_set_cookie_params(array $options)`** und **`session.cookie_samesite`**; **`setcookie()` / `setrawcookie()`** akzeptieren ein `$options`-Array inklusive **`samesite`**.
* **Stream**: IPv6-Namen von **`stream_socket_get_name()`** verwenden das **eingeklammerte** Host-Format (z. B. `[::1]:1337`).
* **SPL Autoload**: Wenn ein Autoloader **eine Exception wirft**, werden **nachfolgende Autoloader übersprungen**.
* **IMAP**: **rsh/ssh**-Logins sind **standardmäßig deaktiviert** – aktivieren Sie diese nur mit Vorsicht (`imap.enable_insecure_rsh`).

---

<a id="practical-recipes"></a>
## Praktische Rezepte

### Prüfung vor `count()`

```php
// src/recipes.php
if (is_countable($rows)) {
    $n = count($rows);
}
```

### Stabile erste/letzte Schlüssel ermitteln

```php
// src/recipes.php
$firstKey = array_key_first($map);
$lastKey = array_key_last($map);
```

### Striktes JSON-Parsing

```php
// src/recipes.php
try {
    $payload = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    // Behandeln oder wrappen
}
```

### Hochauflösende Zeitmessung

```php
// src/recipes.php
$t0 = hrtime(true);
// ...
$elapsedNs = hrtime(true) - $t0;
```

---

<a id="common-mistakes"></a>
## ⚠️ Häufige Fehler

Hier sind typische Fehler, die Entwickler in PHP 7.3 machen:

### 1. Unabsichtliches Einrücken des schließenden Heredoc-Labels
Wenn Zeilen im Textkörper *weniger* eingerückt sind als das schließende Label, oder das schließende Label nachfolgende Leerzeichen enthält, wird ein ParseError geworfen.
```php
// src/BadHeredoc.php
// Schlecht: Der Textkörper ist weniger eingerückt als das schließende Label, wirft ParseError
$text = <<<TEXT
Line 1
    Line 2
      TEXT;
```
```php
// src/GoodHeredoc.php
// Gut: Die Einrückung des schließenden Labels entspricht der minimalen Einrückung des Textkörpers
$text = <<<TEXT
    Line 1
    Line 2
    TEXT;
```

### 2. Verwendung von `continue` innerhalb von `switch`
`continue` innerhalb von Switch-Anweisungen verhält sich in PHP wie `break`. Dies führt oft zu Endlosschleifen, wenn Entwickler erwarten, dass damit zum nächsten Schleifendurchlauf gesprungen wird. PHP 7.3 gibt dafür eine Warnung aus.
```php
// src/BadContinueSwitch.php
// Schlecht: Gibt Warnung aus: "continue" targeting "switch" is equivalent to "break"
foreach ($items as $item) {
    switch ($item) {
        case 'skip':
            continue;
    }
}
```
```php
// src/GoodContinueSwitch.php
// Gut: Verwenden Sie "continue 2", um die äußere foreach-Schleife anzusprechen
foreach ($items as $item) {
    switch ($item) {
        case 'skip':
            continue 2;
    }
}
```

---

<a id="limitations"></a>
## Einschränkungen

* **Abschließende Kommata**: Werden nur in Funktions- und Methodenaufrufen unterstützt. Der Versuch, `function foo($a, $b,) {}` (abschließendes Komma in der Deklaration) zu schreiben, führt zu einem Syntax-ParseError.
* **Flexibles Heredoc**: Das schließende Label muss auf einer eigenen Zeile stehen und darf keinen Text (außer einem Semikolon oder Zeilenumbruch) nach sich ziehen. Jedes nachfolgende Leerzeichen nach dem schließenden Bezeichner führt zu einem Kompilierfehler.

---

<a id="backward-incompatible"></a>
## Rückwärtskompatibilität und BC-Breaks (Migrationshinweise)

### Core

* **Flexibles Heredoc/Nowdoc**: Strings, die **das schließende Label enthalten**, können ihre Bedeutung ändern oder zu **Parse-Fehlern** führen – benennen Sie Labels um oder maskieren Sie den Inhalt.
* **`continue` in `switch`**: Erzeugt nun eine **Warnung** (in PHP verhielt es sich wie `break`). **Prüfen Sie `switch` + `continue`.**
* **`ArrayAccess` mit numerischen String-Schlüsseln**: Bei literalen **`"123"`**-Schlüsseln **verzichtet PHP auf die automatische Konvertierung** zu Integer für `ArrayAccess` – **`offsetGet("123")`** wird aufgerufen statt **`offsetGet(123)`**.
* **Statische Eigenschaften über Referenztricks**: Vererbung von statischen Eigenschaften kann nicht mehr über Referenzzuweisungen **aufgeteilt** werden.
* **Referenzen aus Array-/Eigenschaftszugriffen**: Referenzen, die von verketteten **Array-/Eigenschaftslesevorgängen** zurückgegeben werden, werden **sofort aufgelöst**.
* **Entpacken von `...$traversable`**: **Nicht-ganzzahlige Schlüssel** in `Traversable` **werden bei Aufrufen nicht mehr entpackt**.
* **Warnungen zu Exceptions hochgestuft (`EH_THROW`)**: Solche Exceptions **befüllen nicht mehr** **`error_get_last()`**.
* **`TypeError`-Meldungen**: Falsche Typen werden als **`int` / `bool`** ausgegeben, nicht mehr als **`integer` / `boolean`**.
* **`compact()`**: Die Übergabe von Namen **undefinierter Variablen** erzeugt eine **Notice**.
* **`getimagesize()`** (und ähnliche): Der BMP-MIME-Typ wird als **`image/bmp`** (IANA) gemeldet, nicht mehr als **`image/x-ms-bmp`**.
* **Cookies (ab PHP 7.3.23)**: Eingehende Cookie-**Namen** werden **nicht mehr URL-dekodiert** (Sicherheitsanpassung).

### BCMath

* Warnungen werden über den **Standard-Error-Handler** von PHP geleitet.
* **`bcmul()` / `bcpow()`** halten die **angeforderte Genauigkeit (scale)** strenger ein.

### IMAP

* **rsh/ssh**-Tunneling ist **deaktiviert**, es sei denn, Sie aktivieren explizit **`imap.enable_insecure_rsh`**.

### Multibyte-Regex (`mb_ereg_*`)

* **Benannte Captures** verändern die **Struktur des Match-Arrays** und die Ersetzungssyntax von **`mb_ereg_replace()`**.

### MySQLi und PDO MySQL

* **Bruchteile von Sekunden** sind nun in den gebundenen Ergebniswerten sichtbar.

### Reflection

* String-Exporte verwenden die Typbezeichnungen **`int` / `bool`** in der Ausgabe.

### SimpleXML

* Numerische Operationen auf SimpleXML-Textknoten wählen zwischen **int und float**, anstatt immer über **int** zu konvertieren.

---

<a id="deprecations"></a>
## Veraltete Features (frühzeitig beheben)

Wichtige Core-Elemente:

* **Case-insensitive `define()`** (`true` als drittes Argument) und die Verwendung eines **anderen Cases** als bei der Definition.
* **Namespaced-Funktionen namens `assert()`** – vermeiden Sie diese; die Engine behandelt **`assert()`** speziell.
* **Nicht-String-Needles** in der **`strpos`-Familie** – konvertieren Sie diese explizit.
* **`fgetss()`**, **`gzgetss()`**, **`SplFileObject::fgetss()`** und der Filter **`string.strip_tags`**.
* **`FILTER_FLAG_SCHEME_REQUIRED` / `FILTER_FLAG_HOST_REQUIRED`** mit **`FILTER_VALIDATE_URL`** (redundant).
* **`image2wbmp()`** (GD).
* **`Normalizer::NONE`** mit ICU ≥ 56.
* Undokumentierte **`mbereg_*()`-Aliase** – wechseln Sie zu **`mb_ereg_*()`**.
* Deprecation der INI-Einstellung **`pdo_odbc.db2_instance_name`**.

---

<a id="interactive-quiz"></a>
## Interaktives Quiz

Testen Sie Ihr Wissen über PHP 7.3:

1. **Welche Aussage zu abschließenden Kommata in PHP 7.3 ist korrekt?**
   * A) Sie sind in Funktionsaufrufen erlaubt, aber nicht in Funktionsdeklarationen.
   * B) Sie sind in Funktionsdeklarationen erlaubt, aber nicht in Funktionsaufrufen.
   * C) Sie sind sowohl in Deklarationen als auch in Aufrufen erlaubt.
   * D) Sie sind weiterhin überall Syntaxfehler.

   <details>
   <summary>Klicken, um die Antwort anzuzeigen</summary>
   
   **Richtige Antwort: A**  
   PHP 7.3 erlaubt abschließende Kommata bei Funktions-/Methodenaufrufen, aber das Deklarieren einer Funktion mit einem abschließenden Komma in der Parameterliste bleibt ein ParseError.
   </details>

2. **Wie verhält sich PHP 7.3, wenn es auf ein `continue` in einer `switch`-Anweisung stößt?**
   * A) Es verhält sich stillschweigend wie ein `continue 2` für die äußere Schleife.
   * B) Es wirft einen fatalen ParseError.
   * C) Es gibt eine Warnung aus, dass 'continue' in einem 'switch' äquivalent zu 'break' ist.
   * D) Es setzt die Ausführung normal und ohne Warnung fort.

   <details>
   <summary>Klicken, um die Antwort anzuzeigen</summary>
   
   **Richtige Antwort: C**  
   PHP 7.3 löst dafür eine Warnung aus, da `continue` in einem `switch` keine Iterationen einer äußeren Schleife überspringt, sondern sich wie `break` verhält.
   </details>

3. **Welche Funktion wurde in PHP 7.3 eingeführt, um zu prüfen, ob eine Variable an `count()` übergeben werden kann?**
   * A) `is_iterable()`
   * B) `is_countable()`
   * C) `can_count()`
   * D) `is_array_or_countable()`

   <details>
   <summary>Klicken, um die Antwort anzuzeigen</summary>
   
   **Richtige Antwort: B**  
   `is_countable()` gibt `true` zurück, wenn die Variable ein Array oder eine Instanz einer Klasse ist, die `Countable` implementiert.
   </details>

---

<a id="closing-thoughts"></a>
## Schlussgedanken

PHP 7.3 belohnt ein **zielgerichtetes Code-Review**: Suchen Sie nach **`continue` + `switch`**, **`compact(`**, **`ArrayAccess`**-Implementierungen, **Heredoc**-Labels und dem **Entpacken von `...$generator`-Argumenten**. Führen Sie Tests für **Regex** und **Datum/Uhrzeit** erneut aus und gehen Sie dann weiter – **7.4** wird weitaus größere Sprachfeatures hinzufügen, sodass das Bereinigen von PHP 7.3-Deprecations den Rauschen beim nächsten Versionssprung reduziert.