Buscar..


¿Qué es el tipo de malabarismo?

PHP es un lenguaje vagamente escrito. Esto significa que, de forma predeterminada, no requiere que los operandos de una expresión sean del mismo tipo (o compatibles). Por ejemplo, puede agregar un número a una cadena y esperar que funcione.

var_dump ("This is example number " . 1);

La salida será:

string (24) "Este es el ejemplo número 1"

PHP logra esto al convertir automáticamente tipos de variables incompatibles en tipos que permiten que se lleve a cabo la operación solicitada. En el caso anterior, convertirá el literal entero 1 en una cadena, lo que significa que se puede concatenar en el literal de la cadena anterior. Esto se conoce como tipo malabarismo. Esta es una característica muy poderosa de PHP, pero también es una característica que puede llevarlo a un montón de tirones si no está al tanto, e incluso puede llevar a problemas de seguridad.

Considera lo siguiente:

if (1 == $variable) {
    // do something
}

La intención parece ser que el programador está comprobando que una variable tiene un valor de 1. ¿Pero qué sucede si $ variable tiene un valor de "1 y medio" en su lugar? La respuesta podría sorprenderte.

$variable = "1 and a half";
var_dump (1 == $variable);

El resultado es:

bool (verdadero)

¿Por qué ha sucedido esto? Es porque PHP se dio cuenta de que la cadena "1 y medio" no es un número entero, pero debe ser para poder compararla con el número entero 1. En lugar de fallar, PHP inicia el tipo de malabarismo e intenta convertir la variable en una entero. Para ello, toma todos los caracteres al principio de la cadena que se pueden convertir en enteros y los emite. Se detiene tan pronto como encuentra un personaje que no puede ser tratado como un número. Por lo tanto, "1 y medio" se convierte en entero 1.

Por supuesto, este es un ejemplo muy artificial, pero sirve para demostrar el problema. Los siguientes ejemplos cubrirán algunos casos en los que me he encontrado con errores causados ​​por los juegos de tipo que ocurrieron en software real.

Leyendo de un archivo

Al leer un archivo, queremos saber cuándo hemos llegado al final de ese archivo. Sabiendo que fgets() devuelve falso al final del archivo, podríamos usar esto como la condición para un bucle. Sin embargo, si los datos devueltos de la última lectura resultan ser algo que se evalúa como booleano false , puede hacer que nuestro bucle de lectura de archivos termine prematuramente.

$handle = fopen ("/path/to/my/file", "r");

if ($handle === false) {
    throw new Exception ("Failed to open file for reading");
}

while ($data = fgets($handle)) {
    echo ("Current file line is $data\n");
}

fclose ($handle);

Si el archivo que contiene leer una línea en blanco, el while de bucle se dará por terminado en ese momento, debido a que la cadena vacía se evalúa como booleano false .

En su lugar, podemos verificar explícitamente el valor false booleano, utilizando operadores de igualdad estricta :

while (($data = fgets($handle)) !== false) {
    echo ("Current file line is $data\n");
}

Tenga en cuenta que este es un ejemplo artificial; En la vida real usaríamos el siguiente bucle:

while (!feof($handle)) {
    $data = fgets($handle);
    echo ("Current file line is $data\n");
}

O reemplazar todo el asunto con:

$filedata = file("/path/to/my/file");
foreach ($filedata as $data) {
    echo ("Current file line is $data\n");
}

Cambiar sorpresas

Las declaraciones de cambio utilizan una comparación no estricta para determinar coincidencias. Esto puede llevar a algunas sorpresas desagradables . Por ejemplo, considere la siguiente declaración:

switch ($name) {
    case 'input 1':
        $mode = 'output_1';
        break;
    case 'input 2':
        $mode = 'output_2';
        break;
    default:
        $mode = 'unknown';
        break;
}

Esta es una declaración muy simple, y funciona como se esperaba cuando $name es una cadena, pero puede causar problemas de lo contrario. Por ejemplo, si $name es el entero 0 , entonces el tipo de malabarismo ocurrirá durante la comparación. Sin embargo, es el valor literal en la declaración de caso que se hace malabarismo, no la condición en la instrucción de cambio. La cadena "input 1" se convierte a entero 0 que coincide con el valor de entrada de entero 0 . El resultado de esto es que si proporciona un valor de entero 0 , el primer caso siempre se ejecuta.

Hay algunas soluciones a este problema:

Casting explícito

El valor se puede encasillar en una cadena antes de la comparación:

switch ((string)$name) {
...
}

O también se puede usar una función conocida para devolver una cadena:

switch (strval($name)) {
...
}

Ambos métodos aseguran que el valor sea del mismo tipo que el valor en las declaraciones de case .

Evitar el switch

El uso de una declaración if nos permitirá controlar cómo se realiza la comparación, lo que nos permitirá utilizar operadores de comparación estrictos :

if ($name === "input 1") {
    $mode = "output_1";
} elseif ($name === "input 2") {
    $mode = "output_2";
} else {
    $mode = "unknown";
}

Tipificación estricta

Desde PHP 7.0, algunos de los efectos dañinos de los juegos malabares pueden mitigarse con una escritura estricta . Al incluir esta declare declaración como la primera línea del archivo, PHP aplicará las declaraciones de tipo de parámetro y devolverá las declaraciones de tipo lanzando una excepción TypeError .

declare(strict_types=1);

Por ejemplo, este código, utilizando definiciones de tipo de parámetro, lanzará una excepción detectable de tipo TypeError cuando se ejecute:

<?php
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

echo sum("1", 2);

Asimismo, este código utiliza una declaración de tipo de retorno; también lanzará una excepción si intenta devolver algo que no sea un entero:

<?php
declare(strict_types=1);

function returner($a): int {
    return $a;
}

returner("this is a string");


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow