Escriba las variables de conversión en PHP, ¿cuál es la razón práctica para hacer esto?

43

PHP, como la mayoría de nosotros sabemos, tiene escritura débil . Para aquellos que no lo hacen, PHP.net dice:

  

PHP no requiere (o admite) definición de tipo explícita en la declaración de variable; el tipo de una variable está determinado por el contexto en el que se usa la variable.

Lo amo o lo odio, PHP vuelve a lanzar las variables sobre la marcha. Por lo tanto, el siguiente código es válido:

$var = "10";
$value = 10 + $var;
var_dump($value); // int(20)

PHP también le permite emitir explícitamente una variable, de este modo:

$var = "10";
$value = 10 + $var;
$value = (string)$value;
var_dump($value); // string(2) "20"

Todo es genial ... pero, por mi vida, no puedo concebir una razón práctica para hacer esto.

No tengo problemas con la escritura fuerte en lenguajes compatibles, como Java. Eso está bien, y lo entiendo completamente. Además, soy consciente de y entiendo completamente la utilidad de sugerencias de tipo en los parámetros de función .

El problema que tengo con la conversión de tipos se explica mediante la cita anterior. Si PHP puede intercambiar los tipos a voluntad , puede hacerlo incluso después de que fuerce la conversión de un tipo; y puede hacerlo sobre la marcha cuando necesite un determinado tipo en una operación. Eso hace válido lo siguiente:

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

Entonces, ¿cuál es el punto?

Tome este ejemplo teórico de un mundo donde la conversión de tipos definidos por el usuario tiene sentido en PHP :

  1. Forzas la variable de conversión $foo como int(int)$foo .
  2. Intenta almacenar un valor de cadena en la variable $foo .
  3. PHP lanza una excepción !! ← Eso tendría sentido. De repente, ¡el motivo de la conversión de tipos definidos por el usuario existe!

El hecho de que PHP cambie las cosas según sea necesario hace que el punto de conversión del tipo definido por el usuario sea impreciso. Por ejemplo, los siguientes dos ejemplos de código son equivalentes:

// example 1
$foo = 0;
$foo = (string)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

// example 2
$foo = 0;
$foo = (int)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

Un año después de haber formulado originalmente esta pregunta, ¿adivina quién se encontró usando encasillamiento en un entorno práctico? Atentamente.

El requisito era mostrar los valores monetarios en un sitio web para un menú de restaurante. El diseño del sitio requería que se recortaran los ceros finales, de modo que la pantalla se pareciera a la siguiente:

Menu Item 1 .............. $ 4
Menu Item 2 .............. $ 7.5
Menu Item 3 .............. $ 3

La mejor manera que encontré para hacer eso fue lanzar la variable como un flotador:

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP recorta los ceros finales del flotador, y luego vuelve a ubicar el flotador como una cadena para la concatenación.

    
pregunta Stephen 07.12.2010 - 14:44

7 respuestas

30

En un lenguaje de tipo débil, la conversión de tipos existe para eliminar la ambigüedad en las operaciones con tipo, cuando de otra manera el compilador / intérprete usaría orden u otras reglas para hacer una suposición de qué operación usar.

Normalmente, diría que PHP sigue este patrón, pero en los casos que he comprobado, PHP se ha comportado de forma contrainteligente en cada uno.

Aquí están esos casos, usando JavaScript como lenguaje de comparación.

Concatentación de cadenas

Obviamente, esto no es un problema en PHP porque hay operadores de concatenación de cadenas ( . ) y operadores de adición ( + ) separados.

JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

Comparación de cadenas

A menudo, en los sistemas informáticos "5" es mayor que "10" porque no lo interpreta como un número. No es así en PHP, que, incluso si ambos son cadenas, se da cuenta de que son números y elimina la necesidad de un lanzamiento):

JavaScript
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false";  // false!

Escritura de firma de función

PHP implementa una comprobación de tipos básica en las firmas de funciones, pero desafortunadamente es tan defectuoso que probablemente no sea utilizable.

Pensé que podría estar haciendo algo mal, pero un comenta sobre los documentos confirma que los tipos incorporados distintos a la matriz no se pueden usar en las firmas de funciones de PHP, aunque el mensaje de error es engañoso.

PHP
function testprint(string $a) {
    echo $a;
}

$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
                      //  must be an instance of string, string given" WTF?

Y a diferencia de cualquier otro idioma que conozco, incluso si utiliza un tipo que entiende, el valor nulo ya no se puede pasar a ese argumento ( must be an instance of array, null given ). Qué estúpido.

Interpretación booleana

[ Editar ]: Este es nuevo. Pensé en otro caso, y de nuevo la lógica se invierte desde JavaScript.

JavaScript
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.

En conclusión, el único caso útil que se me ocurre es ... (drumroll)

Tipo de corte

En otras palabras, cuando tiene un valor de un tipo (digamos una cadena) y quiere interpretarlo como otro tipo (int) y quiere forzarlo a convertirse en uno de los conjuntos de valores válidos en ese tipo:

$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties

No puedo ver qué es lo que realmente te ganaría un upcasting (para un tipo que abarca más valores).

Entonces, si bien no estoy de acuerdo con su uso propuesto de escribir (esencialmente, está proponiendo escritura estática , pero con el la ambigüedad de que solo si se forzara en un tipo arrojara un error (lo que causaría confusión), creo que es una buena pregunta, porque aparentemente el casting tiene muy poco propósito en PHP.

    
respondido por el Nicole 07.12.2010 - 22:03
15

Estás mezclando los conceptos de tipo débil / fuerte y dinámico / estático.

PHP es débil y dinámico, pero su problema es con el concepto de tipo dinámico. Eso significa que las variables no tienen un tipo, los valores sí.

Un 'tipo de conversión' es una expresión que produce un nuevo valor de un tipo diferente del original; no le hace nada a la variable (si hay una involucrada).

La única situación en la que regularmente escribo valores de conversión es en parámetros numéricos de SQL. Se supone que debe sanear / escapar cualquier valor de entrada que inserte en sentencias de SQL, o (mucho mejor) usar consultas parametrizadas. Pero, si quieres un valor que DEBE ser un número entero, es mucho más fácil simplemente emitirlo.

Considera:

function get_by_id ($id) {
   $id = (int)$id;
   $q = "SELECT * FROM table WHERE id=$id LIMIT 1";
   ........
}

si omito la primera línea, $id sería un vector fácil para la inyección de SQL. El elenco se asegura de que sea un entero inofensivo; cualquier intento de insertar algún SQL simplemente resultaría en una consulta para id=0

    
respondido por el Javier 07.12.2010 - 15:23
4

Un uso para la conversión de tipos en PHP que he encontrado:

Estoy desarrollando una aplicación para Android que realiza solicitudes http a scripts PHP en un servidor para recuperar datos de una base de datos. El script almacena los datos en forma de un objeto PHP (o una matriz asociativa) y se devuelve como un objeto JSON a la aplicación. Sin el tipo de casting recibiría algo como esto:

{ "user" : { "id" : "1", "name" : "Bob" } }

Pero, al usar el tipo PHP casting (int) en la identificación del usuario al almacenarlo, el objeto PHP se devuelve a la aplicación:

{ "user" : { "id" : 1, "name" : "Bob" } }

Luego, cuando se analiza el objeto JSON en la aplicación, ¡me evita tener que analizar el id a un entero!

Ver, muy útil.

    
respondido por el Ryan 02.12.2013 - 21:42
3

Un ejemplo son los objetos con un método __toString: $str = $obj->__toString(); vs %código%. Hay mucho menos que escribir en el segundo, y la materia adicional es la puntuación, que lleva más tiempo para escribir. También creo que es más legible, aunque otros pueden estar en desacuerdo.

Otro es hacer una matriz de un solo elemento: $str = (string) $obj; vs array($item); . Esto colocará cualquier tipo de escalar (entero, recurso, etc.) dentro de una matriz.
Alternativamente, si (array) $item; es un objeto, sus propiedades se convertirán en claves para sus valores. Sin embargo, sí creo que la conversión de matriz de objetos > es un poco extraña: las propiedades privadas y protegidas son parte de la matriz y se les ha cambiado el nombre. Para citar la documentación de PHP : las variables privadas tienen el nombre de clase precedido al nombre de la variable; las variables protegidas tienen un '*' ante el nombre de la variable.

Otro uso es convertir datos GET / POST en tipos apropiados para una base de datos. MySQL puede manejar esto por sí mismo, pero creo que mientras más servidores compatibles con ANSI rechacen los datos. La razón por la que solo menciono las bases de datos es que, en la mayoría de los casos, los datos tendrán una operación realizada de acuerdo con su tipo en algún momento (es decir, int / floats usualmente tendrán cálculos realizados en ellos, etc.).

    
respondido por el Alan Pearce 07.12.2010 - 15:45
0

Este script:

$tags = _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

se ejecutará bien para script.php?tags[]=one pero fallará para script.php?tags=one , porque _GET['tags'] devuelve una matriz en el primer caso pero no en el segundo. Dado que la secuencia de comandos está escrita para esperar una matriz (y usted tiene menos control sobre la cadena de consulta enviada a la secuencia de comandos), el problema puede resolverse mediante la conversión adecuada del resultado de _GET :

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}
    
respondido por el beldaz 03.01.2011 - 04:20
0

También se puede utilizar como un método rápido y sucio para garantizar que los datos no confiables no vayan a romper algo, por ejemplo, si se usa un servicio remoto que tiene validación de basura y solo debe aceptar números.

$amount = (float) $_POST['amount'];

if( $amount > 0 ){
    $remoteService->doacalculationwithanumber( $amount );    
}

Obviamente, esto es defectuoso y el operador de comparación también lo maneja de manera implícita en la declaración if, pero es útil para asegurarse de que sepa exactamente lo que está haciendo su código.

    
respondido por el Gruffputs 07.12.2010 - 17:12
-2

Un "uso" de las variables de reemisión de PHP sobre la marcha que veo en uso a menudo es cuando se recuperan datos de fuentes externas (entrada de usuario o base de datos). Permite a los programadores (tenga en cuenta que no dije desarrolladores) ignorar (o ni siquiera aprender) los diferentes tipos de datos disponibles de diferentes fuentes.

Un codificador (tenga en cuenta que no dije desarrollador) cuyo código heredé y aún mantengo no parece saber que existe una diferencia entre la cadena "20" que es devuelto en la variable super $_GET , entre la operación entera 20 + 20 cuando la agrega al valor en la base de datos. Solo tiene la suerte de que PHP use . para la concatenación de cadenas y no + como cualquier otro idioma, porque he visto que su código "agrega" dos cadenas (un varcahr de MySQL y un valor de $_GET ) y obtener un int.

¿Es este un ejemplo práctico? Solo en el sentido de que permite a los programadores no saber con qué tipos de datos están trabajando. Yo personalmente lo odio.

    
respondido por el dotancohen 22.03.2014 - 16:41

Lea otras preguntas en las etiquetas