---
title: 'PHP 8.4: Property Hooks, Lazy Objects, New DOM & Migration Guide | DevSense'
description: 'Notas de actualización de PHP 8.4 (desde 8.3): Property Hooks, visibilidad asimétrica, Lazy Objects, #[Deprecated], request_parse_body(), nueva API Dom\* y cambios incompatibles.'
faq:
    - { question: '¿Qué son los Property Hooks en PHP 8.4?', answer: 'Los property hooks le permiten interceptar y definir lógica personalizada para el acceso (get) y modificación (set) de propiedades directamente en su declaración, eliminando los getters y setters repetitivos.' }
    - { question: '¿Puede una propiedad con hooks pasarse por referencia?', answer: 'No, las propiedades con hooks no pueden pasarse por referencia porque sus valores se leen o escriben a través de hooks y pueden no tener una referencia de memoria directa.' }
    - { question: '¿Qué es la visibilidad asimétrica en PHP 8.4?', answer: 'La visibilidad asimétrica permite establecer diferentes niveles de visibilidad para leer y escribir una propiedad. Por ejemplo, una propiedad puede leerse públicamente (public) pero modificarse solo de forma privada (private set).' }
    - { question: '¿Para qué se utiliza request_parse_body()?', answer: 'Permite analizar cuerpos de solicitud RFC1867 (multipart/form-data) en métodos HTTP distintos de POST, como PUT o PATCH.' }
published: '2026-05-31'
---
# PHP 8.4: Características Principales y Guía Interactiva

PHP 8.4 es una versión de gran importancia orientada a la productividad. Cambia por completo la forma en que expresamos las invariantes en los límites de las propiedades (Property Hooks, visibilidad asimétrica), introduce funciones internas de nivel de framework (Lazy Objects) y resuelve problemas históricos de la extensión DOM con clases DOM que cumplen con los estándares WHATWG.

En esta guía, profundizaremos en estas características utilizando ejemplos sencillos, exploraremos los errores comunes y probaremos sus conocimientos al final.

---

## Índice
* [Property Hooks (Hooks de propiedad)](#property-hooks)
* [Visibilidad asimétrica de propiedades](#asymmetric-visibility)
* [Lazy Objects (Ghosts y Proxies)](#lazy-objects)
* [Depreciaciones de espacio de usuario con `#[Deprecated]`](#deprecated-attribute)
* [Namespace DOM moderno conforme a WHATWG (`Dom\*`)](#dom-modern)
* [Análisis multipart que no sean POST: `request_parse_body()`](#request-parse-body)
* [Nuevos asistentes de arrays y matemáticas](#new-functions)
* [Cambios incompatibles con versiones anteriores y depreciaciones](#backward-incompatible)
* [🧠 Preguntas de autoevaluación](#self-check)

---

<a id="property-hooks"></a>
## Property Hooks (lógica get/set en propiedades)

Durante años, los desarrolladores de PHP tuvieron que escribir verbosos getters y setters redundantes solo para validar o formatear valores antes de guardarlos. PHP 8.4 introduce los **Property Hooks** directamente en la declaración de la propiedad.

Hay dos tipos de hooks:
* `set`: se activa al escribir en una propiedad.
* `get`: se activa al leer una propiedad.

Veamos esto en acción:

```php
// app/DTO/Person.php
final class Person
{
    // Un hook puede escribir en una propiedad de respaldo usando $this->propName
    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;
        }
    }

    // ¡Propiedad calculada (virtual) sin almacenamiento de respaldo!
    public string $fullName {
        get => $this->firstName . ' ' . $this->lastName;
    }
}
```

> [!NOTE]
> **¿Sabía que?**
> Las propiedades que definen solo un hook `get` son virtuales de forma predeterminada y no ocupan memoria ni almacenamiento de respaldo en la base de datos en los objetos.

### ⚠️ Errores Comunes

**1. Intentar pasar una propiedad con hooks por referencia**
Debido a que acceder a una propiedad con hooks llama a una función internamente, no puede pasarla por referencia:

```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. Crear accidentalmente una recursión infinita en hooks `set`**
Si asigna a `$this->propertyName` dentro de su propio hook usando `$this->propertyName = $value`, compilará pero si no usa correctamente la lógica de la variable de respaldo, podría provocar una recursión infinita. Afortunadamente, las expresiones cortas (`=>`) asignan al valor de respaldo automáticamente.

---

<a id="asymmetric-visibility"></a>
## Visibilidad asimétrica de propiedades (`public private(set)`)

En PHP 8.4, ahora puede definir diferentes permisos de visibilidad para leer y escribir propiedades. Esta es una mejora masiva para los DTOs de solo lectura y las raíces de agregados (aggregate roots) en el diseño guiado por el dominio (DDD).

```php
// app/Models/User.php
final class User
{
    // Leíble en cualquier lugar, pero solo modificable desde dentro de esta clase
    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]
> **¿Sabía que?**
> La visibilidad asimétrica requiere que la operación de escritura (`set`) sea **más estricta** que la operación de lectura. Por ejemplo, `private public(set)` no es válido y arrojará un error de sintaxis.

---

<a id="lazy-objects"></a>
## Lazy Objects (Ghosts y Proxies a través de Reflection)

PHP 8.4 introduce soporte nativo para **Lazy Objects**. Esto está dirigido principalmente a autores de frameworks y ORM (como Doctrine o las relaciones Eloquent de Laravel) que necesitan posponer la hidratación costosa de la base de datos o las llamadas a la API hasta que se acceda a una propiedad.

Anteriormente, las bibliotecas tenían que generar clases proxy complejas sobre la marcha. Ahora, Reflection admite esto de forma nativa:

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

$r = new ReflectionClass(LargeService::class);
// Crea una instancia lazy ghost de LargeService
$lazyService = $r->newLazyGhost($initializer);
```

La función inicializadora del objeto solo se ejecuta una vez que intenta acceder a cualquier propiedad en `$lazyService`.

---

<a id="deprecated-attribute"></a>
## Depreciaciones de espacio de usuario con `#[Deprecated]`

Anteriormente, para depreciar funciones o métodos personalizados, los desarrolladores tenían que activar `E_USER_DEPRECATED` manualmente. Ahora puede usar el atributo nativo `#[\Deprecated]`:

```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;
    }
}
```

Cuando alguien llame a este método, PHP activará automáticamente una advertencia de depreciación que muestra su mensaje y versión.

---

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

Durante mucho tiempo, el `DOMDocument` de PHP sufrió de un cumplimiento deficiente de HTML5. PHP 8.4 introduce una API DOM moderna y conforme a WHATWG bajo el espacio de nombres `Dom`.

```php
// app/Services/HtmlParser.php
// Analiza estructuras HTML5 modernas sin rodeos
$dom = Dom\HTMLDocument::createFromString('<main><article>Modern HTML5!</article></main>');
$node = $dom->querySelector('article');

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

Las clases existentes como `DOMDocument` permanecen por compatibilidad con versiones anteriores, pero se alienta a los desarrolladores a migrar a `Dom\HTMLDocument` y `Dom\XMLDocument`.

---

<a id="request-parse-body"></a>
## Análisis multipart que no sean POST: `request_parse_body()`

Si alguna vez ha creado una API REST en PHP donde los usuarios suben archivos a través de solicitudes `PUT` o `PATCH`, conoce el dolor: `$_FILES` y `$_POST` solo se completan para solicitudes `POST`. Los desarrolladores tenían que analizar manualmente los fragmentos de flujo sin procesar.

PHP 8.4 nos ofrece `request_parse_body()`, que analiza solicitudes multipart/form-data RFC1867 para cualquier verbo HTTP:

```php
// public/index.php
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
    [$postData, $files] = request_parse_body();
    
    // $postData contiene campos de formulario
    // $files contiene archivos subidos mapeados de forma idéntica a $_FILES
}
```

---

<a id="new-functions"></a>
## Nuevos asistentes de arrays y matemáticas

PHP 8.4 incluye varias funciones de utilidad para facilitar el desarrollo diario:

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

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

// Buscar el primer usuario administrador
$admin = array_find($users, fn($u) => $u['role'] === 'admin');

// Comprobar si existe algún usuario
$hasUsers = array_any($users, fn($u) => $u['role'] === 'user');
```

### 2. Recorte de cadenas multibyte (`mb_trim`)
El `trim()` nativo no es seguro para multibyte, dejando caracteres de espacio en blanco anchos finales en UTF-8. PHP 8.4 soluciona esto con `mb_trim()`, `mb_ltrim()`, y `mb_rtrim()`.

---

<a id="backward-incompatible"></a>
## Cambios incompatibles con versiones anteriores y depreciaciones

Antes de actualizar los servidores de producción a PHP 8.4, audite su base de código para estos cambios:

1. **Los parámetros implícitamente anulables están depreciados**
   ```php
   // ❌ Depreciado en PHP 8.4
   function save(string $name = null) {}

   // ✅ Enfoque correcto
   function save(?string $name = null) {}
   ```
2. **Comportamiento de `exit()` y `die()`**
   Ahora son funciones en lugar de declaraciones. Esto significa que están sujetos a coerciones de tipo estándar y tipos estrictos. Si pasa un objeto o array no válido, lanzará un `TypeError`.
3. **Migraciones de recursos a objetos**
   Las extensiones como DBA, ODBC y SOAP ahora devuelven objetos en lugar de recursos. Si usa comprobaciones `is_resource()` en estas conexiones, ahora devolverán `false`.

---

<a id="self-check"></a>
## 🧠 Preguntas de autoevaluación

¡Repasemos lo que aprendimos! Intente responder a estas preguntas:
1. **¿Verdadero o falso?** ¿Puede definir un hook `get` y uno `set` para una propiedad virtual?
2. ¿Por qué `cleanString($person->firstName)` lanza un Fatal Error si `$firstName` tiene property hooks?
3. ¿Qué sucede si define una propiedad con visibilidad `private public(set)`?
4. ¿Qué función auxiliar en PHP 8.4 debería usar para verificar si *al menos un* elemento en un array cumple con una devolución de llamada de validación dada?

<details>
<summary><b>Mostrar respuestas</b></summary>

1. **Falso.** Una propiedad virtual no tiene almacenamiento de respaldo, por lo que no puede definir un hook `set` que escriba en ella (a menos que el hook `set` escriba en una propiedad diferente dentro de la clase).
2. Debido a que las propiedades con hooks son valores calculados a los que se llama mediante rutinas de hooks; no tienen una referencia estable en memoria, lo que las hace inelegibles para el paso por referencia.
3. Arroja un error de sintaxis. La visibilidad de escritura (`set`) debe ser tan estricta o más estricta que la visibilidad de lectura (`get`).
4. Debería usar `array_any()`.
</details>