En resumen: PHP es de tipado dinámico: el tipo de un valor solo se fija en tiempo de ejecución y, sin más indicaciones, PHP convierte los valores automáticamente cuando hace falta. Desde PHP 7, sin embargo, los parámetros, los valores devueltos y las propiedades pueden llevar tipos, y declare(strict_types=1) hace estricta la comprobación. Quien conoce las reglas y usa comparaciones estrictas con === evita toda una clase de fallos lógicos y de seguridad.
De tipado dinámico, pero no sin tipos
En PHP no hace falta indicar ningún tipo en $preis = 19.90;. PHP reconoce que se trata de un número de coma flotante. Si más adelante el valor se combina con una cadena de texto, PHP lo convierte. Este comportamiento se llama type juggling, es decir, conversión automática de tipos. Facilita los primeros pasos, pero puede dar resultados sorprendentes.
Para quienes toman decisiones es relevante porque muchas aplicaciones PHP antiguas se escribieron sin indicaciones de tipo. Ese código es más difícil de revisar y de modernizar. Los proyectos más recientes usan los tipos de forma sistemática, lo que hace visibles los fallos antes y permite que el análisis estático con herramientas como PHPStan o Psalm resulte realmente eficaz.
Los tipos de PHP de un vistazo
El manual de PHP menciona nueve tipos internos que puede tener cualquier expresión. Habitualmente se agrupan en tres categorías:
| Grupo | Tipos | Ejemplo |
|---|---|---|
| Tipos escalares | bool, int, float, string |
true, 42, 3.5, "Text" |
| Tipos compuestos | array, object, callable |
[1, 2], new DateTime() |
| Tipos especiales | null, resource |
null, un archivo abierto |
Para comprobar tipos, el manual recomienda las funciones is_*(), como is_int(). Para inspeccionar sirve get_debug_type(), que desde PHP 8.0.0 devuelve los nombres de tipo habituales, mientras que la antigua gettype() devuelve, por ejemplo, "integer" y "double".
Además de la conversión automática existe la explícita: con un cast como (int) $wert o con settype(), el propio código decide qué tipo debe tener un valor. Es habitual con los datos de formularios, porque todo lo que llega por $_GET o $_POST es, en principio, una cadena o un array. Pero un cast no comprueba si el valor tenía sentido: "abc" se convierte en silencio en 0. Para las entradas, una comprobación real con filter_var() suele ser, por tanto, la mejor opción.
Las clases y las interfaces también son tipos en PHP. Una función puede exigir, pues, que un parámetro sea un objeto de una clase determinada o que cumpla una interfaz concreta. Cómo se combinan clases, interfaces y enums se explica en la página Programación orientada a objetos en PHP.
Declaraciones de tipo: de PHP 7.1 a 8.3
Las declaraciones de tipo establecen qué valores acepta y devuelve una función. El sistema de tipos ha ido creciendo a lo largo de muchas versiones. Según el registro de cambios del manual, se añadieron, entre otras, estas posibilidades:
| Versión | Novedad |
|---|---|
| 7.1.0 | tipos que admiten null (?int), void, iterable |
| 7.2.0 | object |
| 7.4.0 | propiedades de clase tipadas |
| 8.0.0 | tipos unión (varios tipos permitidos en una sola indicación), mixed, tipo de retorno static |
| 8.1.0 | tipos intersección (A&B), tipo de retorno never |
| 8.2.0 | tipos DNF, tipo literal true, null y false como tipos independientes |
| 8.3.0 | constantes tipadas de clases, interfaces, traits y enums |
DNF significa forma normal disyuntiva y permite combinaciones como (A&B)|null. Quien planifica versiones de PHP ve también en esta tabla qué recursos del lenguaje exige una aplicación.
strict_types: conversión o TypeError
Sin más indicaciones, PHP trabaja en modo de conversión. Una función con parámetros int acepta entonces también 1.5 y lo convierte en 1; desde PHP 8.1, esta conversión con pérdida está marcada como obsoleta. Con declare(strict_types=1) al comienzo del archivo, PHP lanza en su lugar un TypeError.
<?php
declare(strict_types=1);
function summe(int $a, int $b): int
{
return $a + $b;
}
echo summe(1, 2); // 3
echo summe(1.5, 2.5); // TypeError, en lugar de un 3 silencioso
Hay dos matices importantes. Primero, strict_types se aplica a las llamadas realizadas desde el archivo donde figura, no a las funciones definidas en él. Si un archivo sin strict_types llama a esa misma función, se convierte el valor. Segundo, un entero donde se espera un número de coma flotante está permitido también en el modo estricto.
Comparaciones laxas y estrictas
El operador == compara después de convertir los tipos; === compara valor y tipo sin conversión. En la mayoría de los casos, el manual recomienda === y !==, porque las reglas de conversión no siempre son evidentes.
<?php
var_dump(0 == "a"); // PHP 7: true, PHP 8: false
var_dump("1" == "01"); // true, ambas son cadenas numéricas
var_dump("1" === "01");// false, mismo tipo, pero otra cadena
var_dump(1 == true); // true
var_dump(1 === true); // false
Con PHP 8.0, las comparaciones entre números y cadenas no numéricas se volvieron más razonables. Las aplicaciones antiguas pueden comportarse de otro modo tras la actualización, por ejemplo en bloques switch. La expresión match, introducida también con PHP 8.0, compara siempre de forma estricta. Cómo las comparaciones laxas llevan a vulnerabilidades, por ejemplo en las comprobaciones de tokens o contraseñas, lo describe el artículo Type juggling: las comparaciones laxas como riesgo de seguridad.
Tipos, actualizaciones y PHP2Shell
Un sistema de tipos estricto hace más robusto el código de la aplicación, pero no protege frente a fallos del propio intérprete. Las vulnerabilidades de PHP2Shell, CVE-2026-109618 y CVE-2026-109619, están en la gestión de memoria y de peticiones de PHP. Están corregidas en PHP 8.5.12, 8.4.27, 8.3.36 y 8.2.35; después deben reiniciarse todos los procesos de PHP.
El sistema de tipos desempeña, aun así, un papel: en la actualización. Las aplicaciones que siguen ancladas en PHP 8.1 o anteriores ya no reciben actualizaciones oficiales del proyecto PHP. Un código bien tipado se puede llevar mucho más fácilmente a una versión con soporte, porque las herramientas detectan pronto las incompatibilidades. En proyectos más antiguos le ayuda nuestra migración a una versión de PHP con soporte.
Active strict_types de forma gradual en los archivos nuevos y en los revisados. Así crece la seguridad de tipos sin que el código antiguo deje de funcionar de golpe.
¿Cómo ayuda PHP SOS en concreto?
- Comprobamos qué versión mínima de PHP necesita su aplicación y la contrastamos con las versiones corregidas.
- Ejecutamos el análisis estático sobre el código y señalamos los puntos con comparaciones laxas en comprobaciones relevantes para la seguridad.
- Planificamos las actualizaciones de modo que los cambios en las reglas de comparación se prueben antes.
- Aplicamos la actualización de PHP, reiniciamos todos los workers y documentamos el estado.
Preguntas frecuentes
¿PHP es de tipado fuerte o débil?
PHP es de tipado dinámico: el tipo de una variable solo se fija en tiempo de ejecución. Sin strict_types, PHP convierte los valores cuando hace falta. Con declaraciones de tipo y strict_types, el código puede hacerse muy estricto.
¿Qué efecto tiene declare(strict_types=1)?
Desactiva la conversión automática de valores escalares en las llamadas realizadas desde ese archivo. Si un valor no encaja con el tipo declarado, PHP lanza un TypeError. La única excepción es un entero allí donde se espera un número de coma flotante.
¿Por qué 0 == "a" es falso en PHP 8 y verdadero en PHP 7?
Hasta PHP 7, al comparar una cadena no numérica con un número, la cadena se convertía en número: "a" pasaba a ser 0. Desde PHP 8.0, en este caso PHP ya no compara numéricamente y el resultado es false.
¿Protegen los tipos frente a vulnerabilidades del propio PHP?
No. Los tipos evitan fallos en el código de su aplicación. Las vulnerabilidades del intérprete, como PHP2Shell, solo se cierran con una actualización de PHP. Aun así, las funciones de tipos modernas exigen versiones actuales de PHP y son, por tanto, un buen motivo para actualizar.