---
title: 'PHP 8.4: Property Hooks, Lazy Objects, New DOM & Migration Guide | DevSense'
description: 'Upgrade-Hinweise für PHP 8.4 (von 8.3): Property Hooks, asymmetrische Sichtbarkeit, Lazy Objects, #[Deprecated], request_parse_body(), neue Dom\* API, neue array_* Helper und die wichtigsten BC-Breaks und Deprecations.'
faq:
    - { question: 'Was sind Property Hooks in PHP 8.4?', answer: 'Property Hooks ermöglichen es Ihnen, benutzerdefinierte Logik für den Zugriff (get) und die Änderung (set) von Eigenschaften direkt bei der Eigenschaftsdefinition abzufangen und zu definieren. Dadurch entfällt der Boilerplate-Code für Getter und Setter.' }
    - { question: 'Kann eine Eigenschaft mit Hooks als Referenz übergeben werden?', answer: 'Nein, Eigenschaften mit Hooks können nicht als Referenz übergeben werden, da ihre Werte über Hooks gelesen oder geschrieben werden und möglicherweise keine direkte Speicherreferenz besitzen.' }
    - { question: 'Was ist asymmetrische Sichtbarkeit (asymmetric visibility) in PHP 8.4?', answer: 'Die asymmetrische Sichtbarkeit ermöglicht es Ihnen, unterschiedliche Sichtbarkeitsebenen für das Lesen und Schreiben einer Eigenschaft festzulegen. Beispielsweise kann eine Eigenschaft öffentlich lesbar (public), aber nur privat änderbar (private set) sein.' }
    - { question: 'Wofür wird request_parse_body() verwendet?', answer: 'Es ermöglicht das Parsen von RFC1867-Request-Bodys (multipart/form-data) bei HTTP-Methoden außerhalb von POST, wie z. B. PUT oder PATCH.' }
published: '2026-05-31'
---
# PHP 8.4: Hauptmerkmale & Interaktiver Leitfaden

PHP 8.4 ist ein echtes Power-Tools-Release. Es verändert grundlegend, wie wir Invarianten an Eigenschaftsgrenzen ausdrücken (Property Hooks, asymmetrische Sichtbarkeit), führt Internals auf Framework-Ebene ein (Lazy Objects) und löst langjährige Probleme der DOM-Erweiterung durch WHATWG-konforme DOM-Klassen.

In diesem Leitfaden tauchen wir tief in diese Features ein, nutzen verständliche Beispiele, untersuchen häufige Stolpersteine und testen Ihr Wissen am Ende!

---

## Inhalt
* [Property Hooks (Eigenschafts-Hooks)](#property-hooks)
* [Asymmetrische Eigenschafts-Sichtbarkeit](#asymmetric-visibility)
* [Lazy Objects (Ghosts & Proxies)](#lazy-objects)
* [Deprecations im Userland mit `#[Deprecated]`](#deprecated-attribute)
* [WHATWG-konformer DOM-Namespace (`Dom\*`)](#dom-modern)
* [Multipart-Parsing für Nicht-POST-Requests: `request_parse_body()`](#request-parse-body)
* [Neue Array- & Mathe-Helper](#new-functions)
* [Rückwärtskompatibilität und BC-Breaks (Migrationshinweise)](#backward-incompatible)
* [🧠 Selbsttest-Fragen](#self-check)

---

<a id="property-hooks"></a>
## Property Hooks (get/set-Logik auf Eigenschaften)

Jahre lang mussten PHP-Entwickler seitenweise Boilerplate-Getter und -Setter schreiben, nur um Werte vor dem Speichern zu validieren oder zu formatieren. PHP 8.4 führt **Property Hooks** direkt in der Eigenschaftsdeklaration ein.

Es gibt zwei Arten von Hooks:
* `set`: Wird beim Schreiben in eine Eigenschaft ausgelöst.
* `get`: Wird beim Lesen einer Eigenschaft ausgelöst.

Sehen wir uns das in der Praxis an:

```php
// app/DTO/Person.php
final class Person
{
    // Ein Hook kann mithilfe von $this->propName in eine Backing-Eigenschaft schreiben
    public string $firstName {
        set => ucfirst(strtolower($value));
    }

    public string $lastName {
        set {
            if (strlen($value) < 2) {
                throw new InvalidArgumentException('Last name is too short!');
            }
            $this->lastName = $value;
        }
    }

    // Berechnete (virtuelle) Eigenschaft ohne physischen Speicher!
    public string $fullName {
        get => $this->firstName . ' ' . $this->lastName;
    }
}
```

> [!NOTE]
> **Wussten Sie schon?**
> Eigenschaften, die nur einen `get`-Hook definieren, sind standardmäßig virtuell und belegen im Objekt keinen Arbeitsspeicher oder physischen Platz in der Datenbank.

### ⚠️ Häufige Fehler

**1. Versuch, eine Eigenschaft mit Hooks per Referenz zu übergeben**
Da der Zugriff auf eine Eigenschaft mit Hooks unter der Haube eine Funktion aufruft, können Sie diese nicht per Referenz übergeben:

```php
// app/Services/ContactService.php
function cleanString(string &$str) {
    $str = trim($str);
}

$person = new Person();
cleanString($person->firstName); // ❌ Fatal Error: Cannot pass property Person::$firstName by reference
```

**2. Unbeabsichtigte Endlosrekursion in `set`-Hooks**
Wenn Sie `$this->propertyName` innerhalb des eigenen Hooks mithilfe von `$this->propertyName = $value` zuweisen, kompiliert der Code zwar, aber wenn Sie die Backing-Variable nicht korrekt ansprechen, lösen Sie eine Endlosrekursion aus. Glücklicherweise weisen Kurzschreibweisen (`=>`) dem Backing-Wert automatisch den Wert zu.

---

<a id="asymmetric-visibility"></a>
## Asymmetrische Eigenschafts-Sichtbarkeit (`public private(set)`)

In PHP 8.4 können Sie nun unterschiedliche Sichtbarkeiten für das Lesen und Schreiben von Eigenschaften definieren. Dies ist eine enorme Verbesserung für schreibgeschützte DTOs und Aggregate Roots im Domain-Driven Design (DDD).

```php
// app/Models/User.php
final class User
{
    // Überall lesbar, aber nur innerhalb dieser Klasse änderbar
    public private(set) string $email;

    public protected(set) int $loginAttempts = 0;

    public function __construct(string $email)
    {
        $this->email = $email;
    }

    public function incrementAttempts(): void
    {
        $this->loginAttempts++;
    }
}
```

> [!NOTE]
> **Wussten Sie schon?**
> Die asymmetrische Sichtbarkeit erfordert, dass die Schreiboperation (`set`) **restriktiver** sein muss als die Leseoperation. Beispielsweise ist `private public(set)` ungültig und führt zu einem Syntaxfehler.

---

<a id="lazy-objects"></a>
## Lazy Objects (Ghosts & Proxies via Reflection)

PHP 8.4 führt native Unterstützung für **Lazy Objects** ein. Dies richtet sich primär an Framework- und ORM-Entwickler (wie Doctrine oder Laravel Eloquent), die das Laden teurer Datenbankbeziehungen oder API-Aufrufe verzögern möchten, bis tatsächlich auf eine Eigenschaft zugegriffen wird.

Bisher mussten Bibliotheken komplexe Proxy-Klassen on-the-fly generieren. Nun unterstützt die Reflection-API dies nativ:

```php
// app/Services/Container.php
$initializer = static function (LargeService $service) {
    $service->loadHeavyConfiguration();
};

$r = new ReflectionClass(LargeService::class);
// Erstellt eine Lazy-Ghost-Instanz von LargeService
$lazyService = $r->newLazyGhost($initializer);
```

Die Initialisierungsfunktion des Objekts wird erst ausgeführt, wenn Sie versuchen, auf eine Eigenschaft von `$lazyService` zuzugreifen.

---

<a id="deprecated-attribute"></a>
## Deprecations im Userland mit `#[Deprecated]`

Zuvor mussten Entwickler `E_USER_DEPRECATED` manuell auslösen, um eigene Funktionen oder Methoden als veraltet zu markieren. Jetzt können Sie das native Attribut `#[\Deprecated]` verwenden:

```php
// app/Helpers/LegacyMath.php
class LegacyMath
{
    #[\Deprecated(message: "Use BCMath or native division instead", since: "1.4.0")]
    public static function divide(int $a, int $b): float
    {
        return $a / $b;
    }
}
```

Wenn jemand diese Methode aufruft, löst PHP automatisch eine Deprecation-Warnung mit Ihrer Nachricht und der Version aus.

---

<a id="dom-modern"></a>
## WHATWG-konformer DOM-Namespace (`Dom\*`)

Lange Zeit litt PHPs `DOMDocument` unter einer schlechten HTML5-Kompatibilität. PHP 8.4 führt eine moderne, WHATWG-konforme DOM-API unter dem Namespace `Dom` ein.

```php
// app/Services/HtmlParser.php
// Parsen moderner HTML5-Strukturen ohne unschöne Workarounds
$dom = Dom\HTMLDocument::createFromString('<main><article>Modern HTML5!</article></main>');
$node = $dom->querySelector('article');

echo $node->textContent; // Ausgabe: Modern HTML5!
```

Bestehende Klassen wie `DOMDocument` bleiben aus Gründen der Abwärtskompatibilität erhalten, Entwicklern wird jedoch empfohlen, auf `Dom\HTMLDocument` und `Dom\XMLDocument` zu migrieren.

---

<a id="request-parse-body"></a>
## Multipart-Parsing für Nicht-POST-Requests: `request_parse_body()`

Wenn Sie jemals eine REST-API in PHP gebaut haben, bei der Benutzer Dateien via `PUT` oder `PATCH` hochladen, kennen Sie das Problem: `$_FILES` und `$_POST` werden nur für `POST`-Requests gefüllt. Entwickler mussten den rohen Stream manuell parsen.

PHP 8.4 bietet uns `request_parse_body()`, das multipart/form-data-Requests (RFC1867) für alle HTTP-Verben parst:

```php
// public/index.php
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
    [$postData, $files] = request_parse_body();
    
    // $postData enthält die Formularfelder
    // $files enthält die hochgeladenen Dateien (analog zu $_FILES aufgebaut)
}
```

---

<a id="new-functions"></a>
## Neue Array- & Mathe-Helper

PHP 8.4 bringt nützliche Hilfsfunktionen für den Entwickleralltag mit:

### 1. `array_find()`, `array_find_key()`, `array_any()`, `array_all()`

```php
// app/Services/Validator.php
$users = [
    ['name' => 'Alice', 'role' => 'user'],
    ['name' => 'Bob', 'role' => 'admin'],
];

// Findet den ersten Administrator
$admin = array_find($users, fn($u) => $u['role'] === 'admin');

// Prüft, ob mindestens ein Benutzer die Rolle 'user' hat
$hasUsers = array_any($users, fn($u) => $u['role'] === 'user');
```

### 2. Multibyte String Trimming (`mb_trim`)

Das native `trim()` ist nicht multibyte-sicher und lässt nachlaufende Whitespace-Zeichen bei UTF-8-Zeichenketten stehen. PHP 8.4 behebt dies mit `mb_trim()`, `mb_ltrim()` und `mb_rtrim()`.

---

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

Bevor Sie Produktionsserver auf PHP 8.4 aktualisieren, prüfen Sie Ihre Codebasis auf folgende Änderungen:

1. **Implizit Nullable-Parameter sind veraltet**
   ```php
   // ❌ Veraltet in PHP 8.4
   function save(string $name = null) {}

   // ✅ Korrekter Ansatz
   function save(?string $name = null) {}
   ```
2. **Verhalten von `exit()` und `die()`**
   Diese sind nun echte Funktionen statt Sprachkonstrukten. Das bedeutet, sie unterliegen den Standard-Typkonvertierungen und `strict_types`. Die Übergabe eines ungültigen Objekts oder Arrays löst einen `TypeError` aus.
3. **Migration von Ressourcen zu Objekten**
   Erweiterungen wie DBA, ODBC und SOAP geben nun Objekte statt Ressourcen zurück. Wenn Sie `is_resource()` auf diese Verbindungen anwenden, wird `false` zurückgegeben.

---

<a id="self-check"></a>
## 🧠 Selbsttest-Fragen

Testen wir, was Sie gelernt haben! Beantworten Sie diese Fragen:
1. **Richtig oder Falsch?** Können Sie sowohl einen `get`- als auch einen `set`-Hook für eine virtuelle Eigenschaft definieren?
2. Warum wirft `cleanString($person->firstName)` einen Fatal Error, wenn `$firstName` Property Hooks besitzt?
3. Was passiert, wenn Sie eine Eigenschaft mit `private public(set)` deklarieren?
4. Welche Hilfsfunktion in PHP 8.4 sollten Sie verwenden, um zu prüfen, ob *mindestens ein* Element in einem Array eine Validierungs-Callback erfüllt?

<details>
<summary><b>Antworten anzeigen</b></summary>

1. **Falsch.** Eine virtuelle Eigenschaft hat keinen physischen Speicher. Sie können keinen `set`-Hook definieren, der direkt in sie schreibt (es sei denn, der `set`-Hook schreibt in eine andere Eigenschaft innerhalb der Klasse).
2. Da Eigenschaften mit Hooks berechnete Werte sind, die über Hook-Routinen aufgerufen werden; sie besitzen keine feste Speicheradresse und können daher nicht per Referenz übergeben werden.
3. Dies führt zu einem Syntaxfehler. Die Schreibsichtbarkeit (`set`) muss genauso restriktiv oder restriktiver sein als die Lesesichtbarkeit (`get`).
4. Sie sollten `array_any()` verwenden.
</details>