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.
