---
title: "Null coalescing en PHP: el operador ?? y ??="
excerpt: "Cómo funciona el operador null coalescing (??) y su versión de asignación (??=) en PHP: valores por defecto sin warnings, encadenamiento, y la diferencia clave con el operador Elvis (?:)."
date: "2026-06-16T00:00:00.000Z"
lastModified: "2026-06-16T00:00:00.000Z"
category: "PHP"
author:
  name: "angel cruz"
  picture: "https://angelcruzdevcdn.nyc3.cdn.digitaloceanspaces.com/images/me/angel-cruz.png"
ogImage:
  url: "/images/open-graph/php-opengraph-image.png"
seo_title: "Null coalescing en PHP: el operador ?? y ??= con ejemplos"
seo_description: "El operador null coalescing (??) de PHP devuelve un valor por defecto cuando una variable es null o no existe, sin warnings. Aprende ??, ??= y la diferencia con el Elvis (?:)."
tech_article:
  article_section: "PHP Fundamentals"
  keywords: "null coalescing php, operador ?? php, operador de fusion null php, ??= php, valor por defecto php"
  is_part_of:
    url: "https://www.angelcruz.dev/post/operadores-php"
---

El operador **null coalescing** (`??`), o de fusión de null, devuelve su operando izquierdo si **existe y no es `null`**; de lo contrario, devuelve el derecho. Sirve para dar valores por defecto sin que PHP lance un warning cuando la variable no está definida. Desde PHP 7.4 existe además `??=`, que asigna el valor por defecto solo cuando la variable es `null` o no existe.

Este artículo forma parte de la [guía de operadores en PHP](/post/operadores-php). Aquí me enfoco en `??` y `??=` y en cómo se diferencian del Elvis.

## El problema que resuelve `??`

Leer una clave que puede no existir —de `$_GET`, de un array de configuración, de una respuesta de API— obligaba a verificar con `isset()` para evitar el warning *"Undefined array key"*:

```php
// Antes: verificación manual con isset
$nombre = isset($_GET['nombre']) ? $_GET['nombre'] : 'Invitado';

// Con null coalescing: mismo resultado, sin warning
$nombre = $_GET['nombre'] ?? 'Invitado';
```

`$_GET['nombre'] ?? 'Invitado'` equivale exactamente a `isset($_GET['nombre']) ? $_GET['nombre'] : 'Invitado'`, pero en una expresión limpia. Si la clave no existe o es `null`, devuelve `'Invitado'` sin quejarse.

## Encadenamiento: el primer valor que exista

`??` se puede encadenar. PHP evalúa de izquierda a derecha y devuelve el **primer operando que no sea `null`**:

```php
// Busca el tema en preferencias del usuario, luego en config global, luego un default
$tema = $userConfig['theme'] ?? $appConfig['theme'] ?? 'dark';
```

Es uno de los patrones más útiles de PHP 7+: una sola línea recorre varias fuentes de fallback en orden de prioridad.

## `??=` : asignar solo si está vacío

El operador de **asignación coalescente** (`??=`, PHP 7.4+) asigna el valor de la derecha **solo si** la variable de la izquierda es `null` o no existe. Si ya tiene un valor, lo deja intacto:

```php
$opciones['timeout'] ??= 30;
// Asigna 30 solo si 'timeout' no estaba definido o era null.
// Equivale a: $opciones['timeout'] = $opciones['timeout'] ?? 30;
```

Es ideal para rellenar valores por defecto en un array de configuración sin pisar lo que el usuario ya definió.

## La diferencia clave: `??` vs `?:` (Elvis)

Aquí es donde la mayoría se confunde. El [operador Elvis (`?:`)](/post/el-operador-ternario-php) y el null coalescing (`??`) parecen lo mismo, pero **chequean cosas distintas**:

- **`?:`** evalúa *truthiness*: dispara el fallback con cualquier valor *falsy* (`""`, `0`, `"0"`, `[]`, `false`, `null`).
- **`??`** solo mira si el valor es `null` o no existe. Un `""` o un `0` se consideran valores válidos y **no** disparan el fallback.

```php
$valor = "";

echo $valor ?: 'fallback';  // "fallback" — "" es falsy
echo $valor ?? 'fallback';  // ""         — "" no es null, es un valor válido
```

| Situación | `?:` (Elvis) | `??` (null coalescing) |
|---|---|---|
| Variable no definida | warning + fallback | fallback, **sin** warning |
| Valor `""` o `0` | fallback (es *falsy*) | devuelve `""` o `0` |
| Valor `null` | fallback | fallback |

La regla: en formularios y datos donde `""` o `0` son valores legítimos, usa `??`. Solo usa `?:` cuando de verdad quieres tratar lo *falsy* como "vacío".

## Combinarlo con el operador nullsafe `?->`

Desde PHP 8, el operador nullsafe (`?->`) corta una cadena de propiedades o métodos si algo es `null`, en vez de tirar un error. Combinado con `??`, da una expresión muy expresiva para navegar objetos que pueden no existir:

```php
// Si $usuario, su dirección o su ciudad son null, el resultado es 'desconocida'
$ciudad = $usuario?->direccion?->ciudad ?? 'desconocida';
```

El `?->` evita el error al acceder a propiedades de `null`, y el `??` provee el valor por defecto final.

## Preguntas frecuentes

### ¿Qué hace el operador `??` en PHP?

Devuelve su operando izquierdo si existe y no es `null`; en caso contrario, devuelve el derecho. Sirve para asignar valores por defecto sin generar warnings: `$nombre = $_GET['user'] ?? 'anónimo';`.

### ¿Cuál es la diferencia entre `??` y `?:`?

`?:` (Elvis) dispara el fallback con cualquier valor *falsy* (`""`, `0`, `null`…), mientras que `??` solo lo hace cuando el valor es `null` o no existe. Además, `??` no genera warning si la variable no está definida y `?:` sí.

### ¿Qué es `??=` en PHP?

Es el operador de asignación coalescente (PHP 7.4+). `$a ??= $b` asigna `$b` a `$a` solo si `$a` es `null` o no existe. Equivale a `$a = $a ?? $b` pero sin repetir la variable.

### ¿`??` genera un warning si la variable no existe?

No. Esa es justamente su ventaja sobre `isset()` manual o el Elvis: accede a la variable de forma segura y devuelve el fallback sin emitir *"Undefined variable"* ni *"Undefined array key"*.

## Cierre

El null coalescing es el operador al que recurres cada vez que un valor puede faltar: `??` para leer con un default seguro, `??=` para rellenar sin pisar, y encadenado para recorrer fuentes en orden de prioridad. La clave es no confundirlo con el Elvis: `??` mira `null`, `?:` mira *truthiness*. Para el resto de operadores del lenguaje, vuelve a la [guía de operadores en PHP](/post/operadores-php).

---

## Sitemap

Índice completo del sitio: [/sitemap.md](https://www.angelcruz.dev/sitemap.md)

Canónico HTML: [https://www.angelcruz.dev/post/null-coalescing-php](https://www.angelcruz.dev/post/null-coalescing-php)
