---
title: 'PHP 7.4 from 7.3: Typed Properties, Arrow Functions, FFI & Migration | DevSense'
description: 'Guía de actualización de PHP 7.4: propiedades tipadas, funciones de flecha, varianza, ??=, spread en arrays, WeakReference, __serialize/__unserialize, pre-carga de OPcache, FFI—y los cambios incompatibles.'
faq:
    - { question: '¿Qué son las propiedades tipadas en PHP 7.4?', answer: 'PHP 7.4 permite declaraciones de tipo para las propiedades de las clases. Acceder a una propiedad tipada no inicializada antes de asignarle un valor lanza un TypeError.' }
    - { question: '¿Cómo funcionan las funciones de flecha en PHP 7.4?', answer: "Las funciones de flecha usan la palabra clave 'fn' y capturan automáticamente las variables del ámbito padre por valor. Están limitadas a una sola expresión que se devuelve implícitamente." }
    - { question: '¿Qué es la pre-carga de OPcache?', answer: 'La pre-carga permite cargar archivos PHP en memoria al arrancar, haciéndolos disponibles de forma permanente para todas las solicitudes sin sobrecoste de análisis, pero requiere un reinicio del servidor para actualizarse.' }
    - { question: '¿Cuál es el nuevo mecanismo de serialización personalizada en PHP 7.4?', answer: "PHP 7.4 introduce los métodos mágicos '__serialize' y '__unserialize', que reemplazan a la antigua interfaz 'Serializable' y proporcionan una forma más robusta de personalizar la serialización." }
published: '2026-05-31'
---
# PHP 7.4: Características Principales

Imagine actualizar su versión de PHP solo para que su producción falle porque una propiedad de clase no se inicializó. PHP 7.4 trae la tan esperada seguridad de tipos a las propiedades de clase, pero con este poder viene el requisito estricto de que deben inicializarse antes de acceder a ellas. Actúa como el puente definitivo hacia el PHP 8 moderno, introduciendo propiedades tipadas y funciones de flecha que requieren disciplina por parte del desarrollador.

## Índice
* [Propiedades tipadas](#typed-properties)
* [Funciones de flecha (Arrow functions)](#arrow-functions)
* [Covarianza y contravarianza](#variance)
* [Asignación de fusión nula (`??=`)](#null-coalesce-assign)
* [Operador Spread en arrays](#array-spread)
* [Separador de literales numéricos](#numeric-separator)
* [Referencias débiles (Weak references)](#weak-references)
* [`__toString()` ahora puede lanzar excepciones](#tostring-exceptions)
* [Serialización: `__serialize` / `__unserialize`](#custom-serialize)
* [Extensiones destacadas y stdlib](#extensions-stdlib)
* [Recetas prácticas](#practical-recipes)
* [Errores Comunes](#common-mistakes)
* [Limitaciones](#limitations)
* [Cambios incompatibles con versiones anteriores (notas de migración)](#backward-incompatible)
* [Depreciaciones (corregir pronto)](#deprecations)
* [Otros cambios y operaciones (compilación, INI, rendimiento)](#other-changes)
* [Quiz interactivo](#interactive-quiz)
* [Reflexiones finales](#closing-thoughts)

---

<a id="typed-properties"></a>
## Propiedades tipadas

Las propiedades de clase pueden declarar tipos. Las propiedades tipadas no inicializadas **no deben leerse** antes de su asignación; de lo contrario, obtendrá **`Typed property ... must not be accessed before initialization`**.

```php
// src/User.php
final class User
{
    public int $id;
    public string $name;
    public ?string $nickname = null;
}
```

Notas de código real:

* Utilice **`?Tipo`** o un valor por defecto si \"sin valor\" es un estado válido.
* **`callable`** **no** está permitido como tipo de propiedad (depende del contexto).
* No puede establecer una propiedad predeterminada en `new SomeClass()` en el momento de la declaración de la misma manera que con los escalares; las propiedades con tipo de objeto están limitadas.

> [!NOTE]
> ¿Sabía que? En PHP 7.4, las propiedades tipadas están implementadas a nivel del motor con seguridad de \"inicialización diferida\" (lazy initialization). Una propiedad existe en un estado \"no inicializado\", que es distinto de `null`. La comprobación `isset($user->id)` devolverá `false` si no se ha asignado, incluso si no admite valores nulos.

---

<a id="arrow-functions"></a>
## Funciones de flecha (Arrow functions)

Las funciones de flecha (`fn`) capturan variables **por valor** del ámbito contenedor y devuelven una única expresión; ideal para devoluciones de llamada (callbacks) cortas.

```php
// src/MathHelper.php
$factor = 10;
$nums = array_map(fn($n) => $n * $factor, [1, 2, 3]);
```

**`fn` es una palabra reservada**: no puede nombrar una función o clase `fn` (los nombres de métodos/constantes siguen estando permitidos).

> [!NOTE]
> ¿Sabía que? A diferencia de las funciones de flecha de JavaScript, las funciones de flecha de PHP capturan variables únicamente por valor. No puede modificar una variable en el ámbito externo desde dentro de una función de flecha.

---

<a id="variance"></a>
## Covarianza y contravarianza

Los tipos de retorno pueden ser **covariantes** y los tipos de parámetros **contravariantes** en la herencia, con límites. La varianza completa funciona mejor con el **autoloading**; en un solo archivo, solo son posibles las referencias **no cíclicas** porque las clases deben conocerse antes de su uso.

```php
// src/Repository.php
interface Repository {
    public function get(int $id): object;
}

class UserRepository implements Repository {
    public function get(int $id): User { // Tipo de retorno covariante
        return new User();
    }
}
```

---

<a id="null-coalesce-assign"></a>
## Asignación de fusión nula (`??=`)

Asigna solo si el lado izquierdo no está definido o es nulo:

```php
// src/Config.php
$config['debug'] ??= false;
```

---

<a id="array-spread"></a>
## Operador Spread en arrays

Desempaquete iterables dentro de literales de arrays:

```php
// src/ArrayMerge.php
$base = [1, 2];
$all = [0, ...$base, 3]; // [0, 1, 2, 3]
```

Combina bien con `array_merge(...$arrays)` ahora que `array_merge()` se puede llamar **sin argumentos** (devuelve `[]`).

---

<a id="numeric-separator"></a>
## Separador de literales numéricos

Los guiones bajos mejoran la legibilidad y no cambian el valor:

```php
// src/Constants.php
$million = 1_000_000;
$hex = 0xFF_FF_FF;
```

---

<a id="weak-references"></a>
## Referencias débiles (Weak references)

`WeakReference` le permite mantener una referencia que **no** mantiene vivo un objeto, lo cual resulta útil para cachés y grafos sin tener fugas de memoria.

```php
// src/Cache.php
$obj = new stdClass();
$weakRef = WeakReference::create($obj);
```

---

<a id="tostring-exceptions"></a>
## `__toString()` ahora puede lanzar excepciones

Ahora está permitido lanzar excepciones desde `__toString()` (antes era fatal). Algunos fallos fatales recuperables anteriores se convirtieron en excepciones de tipo **`Error`**; audite las rutas de conversión a cadenas de texto.

```php
// src/Stringy.php
class Stringy {
    public function __toString(): string {
        throw new Exception("Fallo al convertir a cadena de texto");
    }
}
```

---

<a id="custom-serialize"></a>
## Serialización: `__serialize` / `__unserialize`

Nuevos ganchos (hooks) reemplazan los patrones ad hoc y superan a `Serializable` a largo plazo:

```php
// src/Point.php
final class Point
{
    private int $x;
    private int $y;

    public function __serialize(): array
    {
        return ['x' => $this->x, 'y' => $this->y];
    }

    public function __unserialize(array $data): void
    {
        $this->x = $data['x'];
        $this->y = $data['y'];
    }
}
```

Los tipos SPL como `ArrayObject` pueden emitir **nuevas** cargas útiles legibles en 7.4+ pero no en PHP más antiguo.

---

<a id="extensions-stdlib"></a>
## Extensiones destacadas y stdlib

* **FFI**: llame a bibliotecas de C desde PHP (potente; requiere sandboxing y revisión de seguridad).
* **Pre-carga de OPcache**: cargue un conjunto de archivos una vez al arrancar para reducir el coste de la carga automática; necesita `opcache.preload` y, a menudo, `opcache.preload_user`.
* **`mb_str_split()`**: como `str_split()` pero en **puntos de código** (code points).
* **`proc_open()`**: comando como **array** (sin shell), además de descriptores `redirect` / `null`.
* **`strip_tags()`**: etiquetas permitidas como un **array** de nombres.
* **PDO**: usuario/contraseña en DSN para más controladores; **`??`** en SQL escapa un `?` literal (por ejemplo, el JSON de PostgreSQL `?`).
* **PCRE**: `preg_replace_callback*` acepta `flags` (`PREG_OFFSET_CAPTURE`, `PREG_UNMATCHED_AS_NULL`).
* **Password**: Argon2 a través de **sodium** cuando se compila sin libargon.

---

<a id="practical-recipes"></a>
## Recetas prácticas

### Valores por defecto seguros con `??=`

```php
// src/recipes.php
$options['timeout'] ??= 30;
```

### Función de flecha en una ordenación

```php
// src/recipes.php
usort($rows, fn($a, $b) => $a['score'] <=> $b['score']);
```

### Fusión por desempaquetado de listas dinámicas

```php
// src/recipes.php
$merged = array_merge(...$chunks);
```

---

<a id="common-mistakes"></a>
## ⚠️ Errores Comunes

Aquí tiene algunos problemas típicos que los desarrolladores enfrentan al hacer la transición a PHP 7.4:

### 1. Leer propiedades tipadas no inicializadas
```php
// src/BadPropertyAccess.php
// Malo: Leer la propiedad antes de asignarle un valor lanza un error fatal
class BadUser {
    public int $id;
}
$user = new BadUser();
echo $user->id; // Fatal Error: Typed property BadUser::$id must not be accessed before initialization
```
```php
// src/GoodPropertyAccess.php
// Bueno: Proporcione un valor por defecto o inicialice en el constructor
class GoodUser {
    public int $id = 0; // valor por defecto
    // O bien:
    // public function __construct(int $id) { $this->id = $id; }
}
```

### 2. Modificar variables del ámbito externo en funciones de flecha
```php
// src/BadArrowScope.php
// Malo: Creer que las funciones de flecha capturan variables de ámbito por referencia
$count = 0;
$increment = fn() => $count++;
$increment();
echo $count; // Resultado: 0 (¡La variable externa no se ve afectada!)
```
```php
// src/GoodArrowScope.php
// Bueno: Use una función anónima tradicional con enlace de referencia explícito
$count = 0;
$increment = function() use (&$count) {
    $count++;
};
$increment();
echo $count; // Resultado: 1
```

---

<a id="limitations"></a>
## Limitaciones

* **Funciones de flecha**: Son estrictamente de una sola expresión. No puede tener lógica multilínea ni declaraciones de retorno (return) dentro de una función de flecha.
* **Pre-carga de OPcache**: Los archivos pre-cargados se almacenan en memoria permanentemente. Cualquier cambio en el código base requiere un reinicio completo de PHP-FPM o del servidor web para surtir efecto.
* **FFI**: Ofrece un rendimiento increíble pero pasa por alto las capas de seguridad de PHP. Es altamente vulnerable a la corrupción de memoria (fallos de segmentación) y no debe usarse en entornos de alojamiento compartido no confiables.

---

<a id="backward-incompatible"></a>
## Cambios incompatibles con versiones anteriores (notas de migración)

### Núcleo

* **Acceso a arrays en no-arrays**: usar `null`, `bool`, `int`, `float` o `resource` como `$x['k']` emite un **Notice**.
* **`get_declared_classes()`**: ya no lista clases **anónimas** hasta que se instancian.
* **`fn`**: palabra clave reservada para nombres de clases/funciones.
* **`<?php` al final del archivo** sin salto de línea: ahora se analiza como una **etiqueta abierta** (antes era ambiguo con `short_open_tag`).
* **Envolturas de flujo (Stream wrappers)**: `include`/`require` en flujos pueden llamar a `stream_set_option(STREAM_OPTION_READ_BUFFER)`—impleméntelo o devuelva `false` para evitar advertencias.
* **Serialización**: **formato `o` eliminado** (solo importaba para cargas útiles manipuladas).
* **Constantes de contraseña**: los identificadores `PASSWORD_*` son **strings** (`'2y'`, `'argon2id'`, …), no enteros—el código que compara con `1`/`2`/`3` fallará.
* **`htmlentities()`**: **Notice** cuando la codificación recurre al comportamiento de `htmlspecialchars`.
* **`fread`/`fwrite`**: devuelven **`false`** en caso de fallo (no `''`/`0`); el fallo puede emitir un **Notice**.
* **BCMath**: advierte sobre cadenas numéricas mal formadas (siguen tratándose como cero).
* **CURL**: la serialización de **`CURLFile`** lanza excepciones antes; `curl_version($nonDefault)` advierte/ignora valores distintos de `CURLVERSION_NOW`.
* **Date**: volcar `DateTime*` ya no deja propiedades huérfanas; la **comparación de `DateInterval`** advierte y siempre es `false`.
* **Intl**: la variante predeterminada de `idn_to_ascii` / `idn_to_utf8` es **`INTL_IDNA_VARIANT_UTS46`**.
* **MySQLi**: se eliminó el servidor embebido; se eliminó la propiedad no documentada `$mysqli->stat`—use **`mysqli::stat()`**.
* **OpenSSL**: `openssl_random_pseudo_bytes` lanza excepciones como **`random_bytes`** en caso de fallo; `$crypto_strong` es `true` cuando no se lanza ninguna excepción.
* **PCRE**: con **`PREG_UNMATCHED_AS_NULL`**, los grupos no coincidentes al final son **`null`** (tamaño de `$matches` estable).
* **PDO**: serializar **`PDO`/`PDOStatement`** lanza una **`Exception`** (no una `PDOException`).
* **Reflection**: serializar objetos de reflexión **lanza excepciones**; cambiaron algunos **valores numéricos de constantes modificadoras**.
* **SPL / `ArrayObject`**: cambió el comportamiento de **`get_object_vars`** a menos que se use `STD_PROP_LIST`; `SplPriorityQueue::setExtractFlags(0)` lanza excepciones de inmediato.
* **Tokenizer**: los bytes inesperados se convierten en **`T_BAD_CHARACTER`** en lugar de generar huecos.
* **Cookies (desde 7.4.11)**: los **nombres** de las cookies ya no se decodifican por URL.

---

<a id="deprecations"></a>
## Depreciaciones (corregir pronto)

Elementos destacados de alta señal:

* **Operadores ternarios anidados** sin paréntesis explícitos (excepto la forma no ambigua de operando medio).
* **Desplazamientos con llaves** `$str{0}` / `$arr{0}` → use `[]`.
* **`(real)`** / **`is_real()`** → **`(float)`** / **`is_float()`**.
* **`array_key_exists()` en objetos** → **`isset()`** / **`property_exists()`**.
* **`implode($parts, $glue)`** en orden inverso → **`implode($glue, $parts)`**.
* **`ReflectionType::__toString()`** y **`Reflection*::export()`** → use APIs como **`ReflectionNamedType::getName()`** y la conversión de objetos de reflexión a cadenas.
* **`allow_url_include`**, **`FILTER_SANITIZE_MAGIC_QUOTES`**, ayudantes paginados de LDAP heredados y muchas funciones pequeñas (`money_format()`, `hebrevc()`, …)—consulte [migration74 deprecated](https://www.php.net/manual/en/migration74.deprecated.php).

---

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

Ponga a prueba sus conocimientos sobre las características de PHP 7.4:

1. **¿Cuál es el resultado de leer una propiedad tipada no inicializada en PHP 7.4?**
   * A) Devuelve `null`
   * B) Genera un aviso (Notice) y devuelve `null`
   * C) Lanza una excepción de tipo Error
   * D) Devuelve `false`

   <details>
   <summary>Haga clic para ver la respuesta</summary>
   
   **Respuesta correcta: C**  
   PHP 7.4 prohíbe estrictamente la lectura de una propiedad tipada antes de que sea inicializada. Intentar hacerlo lanzará un `Error` (específicamente `Error: Typed property ... must not be accessed before initialization`).
   </details>

2. **¿Puede modificar una variable en el ámbito externo desde dentro de una función de flecha?**
   * A) Sí, las variables se capturan por referencia
   * B) No, las variables se capturan por valor
   * C) Sí, pero solo si usa la palabra clave `use`
   * D) Sí, pero solo para objetos

   <details>
   <summary>Haga clic para ver la respuesta</summary>
   
   **Respuesta correcta: B**  
   Las funciones de flecha de PHP capturan variables por valor automáticamente. Cualquier cambio realizado en las variables del ámbito externo dentro de la función de flecha es local y no afecta al ámbito externo.
   </details>

3. **¿Qué tipo de propiedad NO está permitido en PHP 7.4?**
   * A) `object`
   * B) `callable`
   * C) `iterable`
   * D) `?string`

   <details>
   <summary>Haga clic para ver la respuesta</summary>
   
   **Respuesta correcta: B**  
   El tipo `callable` está explícitamente no permitido como tipo de propiedad porque su comportamiento depende en gran medida del contexto de la llamada.
   </details>

---

<a id="closing-thoughts"></a>
## Reflexiones finales

Trate a PHP 7.4 como **la versión puente**: adopte las propiedades tipadas y la serialización moderna **antes** de los errores más estrictos de 8.0. En paralelo, busque **algoritmos de contraseña enteros**, **identificadores `fn`**, la sintaxis **`$var{idx}`** y patrones de **ArrayObject**—y vuelva a probar los **flujos (streams)**, el generador aleatorio de **OpenSSL** y la serialización de **PDO/Reflection** en sus pruebas de integración.