Leccion 42 de 75 6 min de lectura

Nullsafe Operator

El operador nullsafe (?->) permite encadenar llamadas a metodos o propiedades de forma segura. Si alguna parte de la cadena es null, toda la expresion retorna null.

El problema: verificaciones anidadas

Antes de PHP 8, acceder a propiedades de objetos que podian ser null requeria multiples verificaciones:

PHP Antes de PHP 8
<?php

declare(strict_types=1);

// Sin nullsafe: codigo verboso
$pais = null;
if ($usuario !== null) {
    $direccion = $usuario->getDireccion();
    if ($direccion !== null) {
        $pais = $direccion->getPais();
    }
}

// O con operador ternario (sigue siendo largo)
$pais = $usuario !== null
    ? ($usuario->getDireccion() !== null
        ? $usuario->getDireccion()->getPais()
        : null)
    : null;

La solucion: operador nullsafe

Con ?->, PHP evalua la cadena y retorna null si encuentra un valor null en cualquier punto:

PHP PHP 8+
<?php

declare(strict_types=1);

class Direccion
{
    public function __construct(
        public string $calle,
        public string $pais
    ) {}

    public function getPais(): string
    {
        return $this->pais;
    }
}

class Usuario
{
    public function __construct(
        public string $nombre,
        public ?Direccion $direccion = null
    ) {}

    public function getDireccion(): ?Direccion
    {
        return $this->direccion;
    }
}

$usuario1 = new Usuario('Ana', new Direccion('Gran Via', 'Espana'));
$usuario2 = new Usuario('Luis'); // Sin direccion

// Nullsafe: una linea, seguro
$pais1 = $usuario1?->getDireccion()?->getPais(); // 'Espana'
$pais2 = $usuario2?->getDireccion()?->getPais(); // null (no hay error)

Encadenar multiples llamadas

PHP
<?php

declare(strict_types=1);

class Empresa
{
    public function __construct(
        public string $nombre,
        public ?Usuario $ceo = null
    ) {}
}

$empresa = new Empresa('TechCorp');

// Cadena larga: si cualquier parte es null, retorna null
$paisCeo = $empresa?->ceo?->getDireccion()?->getPais();

// Equivale a:
// if ($empresa !== null && $empresa->ceo !== null
//     && $empresa->ceo->getDireccion() !== null) { ... }

Con propiedades y métodos

PHP
<?php

declare(strict_types=1);

class Pedido
{
    public function __construct(
        public string $id,
        public ?Usuario $cliente = null
    ) {}
}

$pedido = new Pedido('PED-001');

// Acceso a propiedad
$nombreCliente = $pedido->cliente?->nombre; // null

// Llamada a metodo
$direccionCliente = $pedido->cliente?->getDireccion(); // null

// Combinando con null coalescing para valor por defecto
$pais = $pedido->cliente?->getDireccion()?->getPais() ?? 'Desconocido';
echo $pais; // 'Desconocido'

Caso practico: APIs y datos externos

PHP
<?php

declare(strict_types=1);

class ApiResponse
{
    public function __construct(
        public ?array $data = null,
        public ?string $error = null
    ) {}

    public function getUser(): ?object
    {
        return isset($this->data['user'])
            ? (object) $this->data['user']
            : null;
    }
}

function fetchApi(): ?ApiResponse
{
    // Simula respuesta de API que puede fallar
    return random_int(0, 1) === 1
        ? new ApiResponse(['user' => ['name' => 'Ana', 'email' => 'ana@example.com']])
        : null;
}

$response = fetchApi();

// Sin nullsafe: muchas verificaciones
// Con nullsafe: limpio y seguro
$userName = $response?->getUser()?->name ?? 'Invitado';
echo "Hola, $userName";
Importante

El operador nullsafe solo funciona para lectura. No puedes usarlo para asignar valores: $obj?->prop = 'valor' genera un error.

Ejercicios

Ejercicio 1: Obtener email de forma segura

Dado un array de pedidos donde cada uno puede o no tener cliente, obtener los emails de los clientes usando nullsafe.

Ver solucion
PHP
<?php

declare(strict_types=1);

class Cliente
{
    public function __construct(
        public string $nombre,
        public string $email
    ) {}
}

class Pedido
{
    public function __construct(
        public string $id,
        public ?Cliente $cliente = null
    ) {}
}

$pedidos = [
    new Pedido('P1', new Cliente('Ana', 'ana@example.com')),
    new Pedido('P2'), // Sin cliente
    new Pedido('P3', new Cliente('Luis', 'luis@example.com')),
];

$emails = [];
foreach ($pedidos as $pedido) {
    $email = $pedido->cliente?->email;
    if ($email !== null) {
        $emails[] = $email;
    }
}

print_r($emails); // ['ana@example.com', 'luis@example.com']

Ejercicio 2: Configuracion anidada

Crea una estructura de configuracion anidada y usa nullsafe para acceder a valores profundos con un valor por defecto.

Ver solucion
PHP
<?php

declare(strict_types=1);

class DatabaseConfig
{
    public function __construct(
        public string $host = 'localhost',
        public int $port = 3306
    ) {}
}

class AppConfig
{
    public function __construct(
        public ?DatabaseConfig $database = null
    ) {}
}

class Config
{
    public function __construct(
        public ?AppConfig $app = null
    ) {}
}

$config = new Config(); // Sin configuracion

$dbHost = $config->app?->database?->host ?? 'localhost';
$dbPort = $config->app?->database?->port ?? 3306;

echo "Conectando a $dbHost:$dbPort";

¿Te está gustando el curso?

Cursos premium con proyectos reales y soporte.

Descubrir cursos premium