str_contains, str_starts_with y str_ends_with en PHP 8

PHP 8.0 introdujo tres funciones que simplifican una de las operaciones más frecuentes con cadenas: comprobar si una contiene, empieza o termina con una subcadena determinada. Antes de PHP 8, todo esto requería strpos() !== false, un patrón confuso precisamente por el problema del cero que ya vimos con strpos(). Las nuevas funciones devuelven booleanos directos y son más legibles.

str_contains(): ¿la cadena contiene esta subcadena?

<?php
str_contains(string $haystack, string $needle): bool
?>
<?php
$email = '[email protected]';

// PHP 7: propenso a errores
if (strpos($email, '@') !== false) {
    echo "Tiene arroba";
}

// PHP 8: booleano directo
if (str_contains($email, '@')) {
    echo "Tiene arroba";
}
?>

La diferencia es especialmente notable cuando se usa con !. !str_contains($email, '@') es mucho más claro que strpos($email, '@') === false.

str_starts_with(): ¿empieza por...?

<?php
$urls = [
    'https://tienda.ejemplo.com',
    'http://blog.ejemplo.com',
    'ftp://ficheros.ejemplo.com',
];

foreach ($urls as $url) {
    if (str_starts_with($url, 'https://')) {
        echo "? HTTPS: $urln";
    } else {
        echo "? No segura: $urln";
    }
}
// ? HTTPS: https://tienda.ejemplo.com
// ? No segura: http://blog.ejemplo.com
// ? No segura: ftp://ficheros.ejemplo.com
?>

str_ends_with(): ¿termina por...?

<?php
$ficheros = ['imagen.jpg', 'documento.pdf', 'script.php', 'datos.json'];

$imagenes = array_filter($ficheros, fn($f) => str_ends_with($f, '.jpg') || str_ends_with($f, '.png') || str_ends_with($f, '.webp'));

print_r($imagenes);
// Array ( [0] => imagen.jpg )
?>

Comportamiento con cadena vacía

Las tres funciones tienen un comportamiento especial cuando el needle es una cadena vacía: siempre devuelven true. Esto es consistente con la teoría de conjuntos (la cadena vacía es prefijo, sufijo y subconjunto de cualquier cadena), pero puede ser un fuente de bugs si el needle viene de una entrada de usuario sin validar:

<?php
var_dump(str_contains('hola', ''));     // bool(true)
var_dump(str_starts_with('hola', '')); // bool(true)
var_dump(str_ends_with('hola', ''));   // bool(true)

// Valida el needle si viene de fuera
$termino = trim($_GET['q'] ?? '');
if ($termino !== '' && str_contains($descripcion, $termino)) {
    // buscar
}
?>

Ejemplos reales con formularios y rutas

<?php
// Detectar si es una ruta de administración
$ruta = '/admin/usuarios/editar/5';

if (str_starts_with($ruta, '/admin/')) {
    // verificar que el usuario tiene permisos de admin
    requireAdmin();
}

// Validar extensión de fichero subido
$nombreFichero = $_FILES['foto']['name'] ?? '';
$extensionesPermitidas = ['.jpg', '.jpeg', '.png', '.webp'];

$esImagen = false;
foreach ($extensionesPermitidas as $ext) {
    if (str_ends_with(strtolower($nombreFichero), $ext)) {
        $esImagen = true;
        break;
    }
}

// Filtrar etiquetas de un artículo
$etiquetas = ['php', 'backend', 'programacion-funcional', 'php-arrays'];
$etiquetasPhp = array_filter($etiquetas, fn($e) => str_contains($e, 'php'));
// ['php', 'php-arrays']
?>

La documentación oficial de str_contains(), junto a las de str_starts_with() y str_ends_with(), muestran los ejemplos del núcleo de PHP 8.0.

COMPARTE ESTE ARTÍCULO

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