== vs === en PHP: igualdad suelta vs estricta

En PHP, == compara solo el valor (aplicando conversión de tipos), mientras que === compara valor y tipo. Por eso 5 == "5" es true, pero 5 === "5" es false. La regla práctica: usa === por defecto. Evita los bugs sutiles que provoca la conversión automática de tipos.
Este artículo forma parte de la guía de operadores en PHP. Aquí me enfoco en la comparación que más confunde.
== : igualdad con conversión de tipos
El operador == (igual) devuelve true cuando los dos operandos tienen el mismo valor, incluso si son de tipos distintos. Antes de comparar, PHP convierte uno de los operandos para que ambos sean del mismo tipo. A esto se le llama type juggling:
var_dump(5 == "5"); // true — la string "5" se trata como el número 5
var_dump(true == 1); // true — true equivale a 1
var_dump(null == false); // true — ambos son "vacíos"
var_dump(0 == false); // trueEs cómodo, pero peligroso: ese ajuste automático esconde comparaciones que no son las que esperas.
=== : identidad (valor y tipo)
El operador === (idéntico) devuelve true solo si los operandos tienen el mismo valor y el mismo tipo. No hay conversión de por medio:
var_dump(5 === "5"); // false — int vs string
var_dump(true === 1); // false — bool vs int
var_dump(null === false); // false — null vs bool
var_dump(5 === 5); // true — mismo valor, mismo tipoEs la comparación segura y predecible. Lo que ves es lo que se compara.
Tabla de comparación
| Expresión | == |
=== |
|---|---|---|
5 y "5" |
true |
false |
0 y false |
true |
false |
null y false |
true |
false |
"" y null |
true |
false |
"1" y "01" |
true |
false |
"10" y "1e1" |
true |
false |
0 y "foo" (PHP 8) |
false |
false |
5 y 5 |
true |
true |
Fíjate en "1" == "01" y "10" == "1e1": cuando ambos operandos son strings numéricas, PHP las compara como números. Por eso "01" y "1e1" (notación científica de 10) entran en juego. Con === nunca pasa: distinto string, distinto resultado.
El cambio de PHP 8 que rompe código viejo
Hasta PHP 7, comparar un número con una string no numérica convertía la string a 0. Eso hacía que 0 == "foo" fuera true, una de las fuentes de bugs más clásicas del lenguaje. PHP 8 lo cambió: ahora convierte el número a string:
// PHP 7: true | PHP 8: false
var_dump(0 == "foo");
// PHP 7: true | PHP 8: false
var_dump(0 == "");Si migras un proyecto de PHP 7 a 8, este es uno de los cambios de comportamiento que más vale revisar. Y es, otra vez, un argumento a favor de usar ===.
!= vs !== (las versiones negadas)
La misma lógica aplica a la desigualdad:
!=(o su alias<>) es la negación de==:truesi los valores difieren tras la conversión de tipos.!==es la negación de===:truesi difieren en valor o en tipo.
var_dump(5 != "5"); // false — para != son iguales (mismo valor)
var_dump(5 !== "5"); // true — para !== difieren (distinto tipo)Dónde PHP usa == sin que lo veas
El gran problema de == no es cuando lo escribes a propósito, sino cuando PHP lo usa por ti:
switch usa comparación suelta. Esto era una trampa en PHP 7:
$valor = 0;
switch ($valor) {
case "admin": // PHP 7: 0 == "admin" era true → ¡entraba aquí!
echo "es admin";
break;
default:
echo "otro valor";
}En PHP 7 ese switch caía en "admin" por la conversión de tipos. Para evitarlo, usa match (PHP 8+), que compara con ===:
$resultado = match ($valor) {
"admin" => "es admin",
default => "otro valor", // 0 nunca coincide con "admin"
};in_array() y array_search() comparan con == por defecto. Pásales true como tercer argumento para forzar comparación estricta:
$roles = ['admin', 'editor'];
var_dump(in_array(0, $roles)); // PHP 7: true (¡bug!) | PHP 8: false
var_dump(in_array(0, $roles, true)); // false siempre — usa ===Acostúmbrate a pasar el true: es la versión segura.
Comparar arrays y objetos
Con tipos compuestos, la diferencia entre == y === tiene matices propios.
Arrays:
$a = ['x' => 1, 'y' => 2];
$b = ['y' => 2, 'x' => 1]; // mismas claves/valores, distinto orden
var_dump($a == $b); // true — mismos pares clave/valor (el orden no importa)
var_dump($a === $b); // false — el orden de las claves difiereCon ===, dos arrays son idénticos solo si tienen los mismos pares clave/valor, en el mismo orden y del mismo tipo.
Objetos:
class Punto {
public function __construct(public int $x, public int $y) {}
}
$p1 = new Punto(1, 2);
$p2 = new Punto(1, 2);
$p3 = $p1;
var_dump($p1 == $p2); // true — misma clase y mismos atributos
var_dump($p1 === $p2); // false — son dos instancias distintas
var_dump($p1 === $p3); // true — $p3 es la misma instancia que $p1Para objetos, == compara clase y atributos; === exige que sean la misma instancia en memoria.
Cuándo usar cada uno
===por defecto. Es predecible, evita los bugs de conversión y deja la intención clara. En la práctica, es lo que quieres el 95% de las veces.==solo cuando la conversión de tipos es intencional y la entiendes bien (por ejemplo, comparar un valor de formulario que llega como string contra un número). Aun así, casi siempre es más limpio convertir el tipo de forma explícita y usar===.
Preguntas frecuentes
¿Cuál es la diferencia entre == y === en PHP?
== compara solo el valor, convirtiendo los tipos si hace falta (5 == "5" es true). === compara valor y tipo, sin conversión (5 === "5" es false). Usa === por defecto para evitar bugs de conversión de tipos.
¿Por qué 0 == "texto" daba true en PHP 7?
Porque PHP 7 convertía la string no numérica a 0 antes de comparar, así que quedaba 0 == 0. PHP 8 cambió ese comportamiento: ahora convierte el número a string, y 0 == "texto" es false.
¿Es seguro usar in_array()?
Por defecto no, porque compara con ==. Pasa true como tercer argumento (in_array($valor, $array, true)) para que use comparación estricta y evites coincidencias inesperadas.
¿=== es más rápido que ==?
Marginalmente, porque se salta la conversión de tipos, pero la diferencia es insignificante. Elige el operador por corrección, no por velocidad: === gana por ser predecible, no por rendimiento.
Cierre
La diferencia entre == y === se reduce a una sola idea: === también compara el tipo, == no. Como == puede convertir tipos por detrás —y PHP lo usa en switch, in_array() y más—, lo seguro es usar === por defecto y reservar == para los pocos casos donde la conversión es justo lo que buscas. Para repasar el resto de comparadores y operadores del lenguaje, vuelve a la guía de operadores en PHP.