El operador nullsafe ?-> en PHP 8 y otras mejoras: str_contains, throw como expresión

PHP 8.0 no solo trajo match y los union types: también llegaron el operador nullsafe ?-> para encadenar llamadas sobre objetos que pueden ser null, las funciones de cadena str_contains(), str_starts_with() y str_ends_with(), y la posibilidad de usar throw como expresión dentro de ternarios, coalescing y arrow functions.

El operador nullsafe ?->

Antes de PHP 8, encadenar llamadas sobre objetos opcionales obligaba a comprobar manualmente cada eslabón con if o ?? null. El operador nullsafe ?-> devuelve null en cuanto encuentra un valor null en la cadena, sin lanzar excepción:

<?php
class Pais
{
    public function __construct(public string $nombre) {}
}

class Ciudad
{
    public function __construct(
        public string $nombre,
        public ?Pais  $pais = null,
    ) {}
}

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

    public function getCiudad(): ?Ciudad
    {
        return $this->ciudad;
    }
}

$usuario = new Usuario('Ana', new Ciudad('Madrid', new Pais('España')));
$sinCiudad = new Usuario('Bot');

// PHP 7: varios if anidados
$pais = null;
if ($usuario->getCiudad() !== null && $usuario->getCiudad()->pais !== null) {
    $pais = $usuario->getCiudad()->pais->nombre;
}

// PHP 8: una sola línea con nullsafe
$pais1 = $usuario?->getCiudad()?->pais?->nombre;
$pais2 = $sinCiudad?->getCiudad()?->pais?->nombre;

echo $pais1; // España
var_dump($pais2); // NULL — sin excepción
?>

str_contains(), str_starts_with() y str_ends_with()

<?php
$url = 'https://www.ejemplo.com/productos/teclado-mecanico?color=negro';

// Antes: strpos() con === false, confuso
if (strpos($url, 'productos') !== false) {
    echo "Es URL de producton";
}

// PHP 8: mucho más legible
if (str_contains($url, 'productos')) {
    echo "Es URL de producton";
}

if (str_starts_with($url, 'https://')) {
    echo "URL seguran";
}

if (str_ends_with($url, 'color=negro')) {
    echo "Filtrado por color negron";
}

// Ejemplos con cadenas vacías (comportamiento definido)
var_dump(str_contains('hola', ''));      // true (toda cadena contiene la vacía)
var_dump(str_starts_with('hola', ''));   // true
?>

throw como expresión

<?php
// Antes: throw solo era una instrucción, no una expresión
// Ahora puede usarse en ternarios, null coalescing, arrow functions...

// En operador ternario
function getEnv(string $nombre): string
{
    return getenv($nombre)
        ?: throw new RuntimeException("Variable de entorno '$nombre' no definida");
}

// En null coalescing
function getConfig(array $config, string $clave): mixed
{
    return $config[$clave]
        ?? throw new InvalidArgumentException("Clave '$clave' requerida en config");
}

// En arrow function
$validar = fn(int $n) => $n > 0
    ? $n
    : throw new RangeException("Debe ser positivo, se recibió: $n");

echo $validar(5);  // 5
$validar(-1);      // RangeException
?>

Nullsafe con métodos que devuelven objetos anidados

<?php
class Repositorio
{
    private array $datos = [
        1 => ['nombre' => 'Ana', 'pais_id' => 1],
        2 => ['nombre' => 'Luis', 'pais_id' => null],
    ];

    public function find(int $id): ?object
    {
        $d = $this->datos[$id] ?? null;
        return $d ? (object)$d : null;
    }
}

class PaisRepositorio
{
    public function find(?int $id): ?object
    {
        if ($id === null) return null;
        return (object)['nombre' => 'España'];
    }
}

$repo     = new Repositorio();
$paisRepo = new PaisRepositorio();

// Encadenamiento nullsafe con resultado de métodos
$usuario = $repo->find(2);
$pais    = $paisRepo->find($usuario?->pais_id);

var_dump($pais); // NULL — Luis no tiene pais_id
?>

La documentación oficial del operador nullsafe y las funciones str_contains(), str_starts_with() detallan los casos límite con cadenas vacías y el comportamiento del short-circuit del operador nullsafe.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP