Digite variáveis de conversão em PHP, qual é a razão prática para fazer isso?

43

O PHP, como a maioria de nós sabe, tem digitação fraca . Para aqueles que não o fazem, o PHP.net diz:

PHP does not require (or support) explicit type definition in variable declaration; a variable's type is determined by the context in which the variable is used.

Ame ou odeie, o PHP re-lança variáveis on-the-fly. Então, o seguinte código é válido:

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

O PHP também permite que você explique explicitamente uma variável, assim:

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

Isso é tudo legal ... mas, para a vida de mim, não posso conceber uma razão prática para fazer isso.

Eu não tenho problema com digitação strong em linguagens que o suportam, como o Java. Tudo bem, e eu entendo isso completamente. Além disso, estou ciente - e compreendo perfeitamente a utilidade de - insinuações de tipos em parâmetros de função .

O problema que tenho com o tipo de conversão é explicado pela citação acima. Se o PHP pode trocar tipos à vontade , pode fazê-lo mesmo depois que você forçar um tipo; e isso pode ser feito imediatamente quando você precisar de um determinado tipo em uma operação. Isso torna o seguinte válido:

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

Então, qual é o objetivo?

Veja este exemplo teórico de um mundo em que o tipo de conversão definido pelo usuário faz sentido no PHP :

  1. Você força a variável de transmissão $foo como int(int)$foo .
  2. Você tenta armazenar um valor de string na variável $foo .
  3. PHP lança uma exceção !! ← Isso faria sentido. De repente, a razão para o tipo de conversão definido pelo usuário existe!

O fato de que o PHP irá mudar as coisas conforme necessário faz com que o ponto de definição do tipo definido pelo usuário seja vago. Por exemplo, os dois exemplos de código a seguir são 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;

Um ano depois de originalmente fazer esta pergunta, adivinha quem se viu usando typecasting em um ambiente prático? Seu verdadeiramente.

O requisito era exibir valores monetários em um site para um menu de restaurante. O design do site exigia que os zeros à direita fossem aparados, de modo que a exibição se parecesse com o seguinte:

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

A melhor maneira que encontrei para fazer isso foi lançar a variável como float:

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

O PHP apara os zeros à direita do float e, em seguida, reformula o float como uma string para concatenação.

    
por Stephen 07.12.2010 / 14:44
fonte

7 respostas

30

Em uma linguagem fracamente tipada, existe um tipo de conversão para remover a ambigüidade em operações tipadas, quando de outra forma o compilador / interpretador usaria a ordem ou outras regras para fazer uma suposição de qual operação usar.

Normalmente, eu diria que o PHP segue esse padrão, mas, dos casos que verifiquei, o PHP se comportou de maneira contraintuitiva em cada um deles.

Aqui estão esses casos, usando JavaScript como um idioma de comparação.

Concatenação de Cordas

Obviamente, isso não é um problema no PHP porque existem operadores separados de concatenação de string ( . ) e de adição ( + ).

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

Comparação de sequências

Muitas vezes, em sistemas de computador, "5" é maior que "10" porque não o interpreta como um número. Não é assim no PHP, que, mesmo que ambos sejam strings, perceba que são números e elimina a necessidade de um elenco):

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

Digitação de assinatura de função

O PHP implementa uma verificação de tipos simples em assinaturas de função, mas infelizmente é tão falho que provavelmente raramente é utilizável.

Pensei que poderia estar fazendo algo errado, mas um comentário nos documentos confirma que tipos internos diferentes de matriz não podem ser usados em assinaturas de função PHP - embora a mensagem de erro seja enganosa.

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?

E diferentemente de qualquer outra linguagem que conheço, mesmo se você usar um tipo que entenda, null não poderá mais ser passado para esse argumento ( must be an instance of array, null given ). Que estupidez.

Interpretação booleana

[ Editar ]: este é novo. Eu pensei em outro caso, e novamente a lógica é invertida do 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.

Então, em conclusão, o único caso útil em que consigo pensar é ... (drumroll)

Tipo de truncagem

Em outras palavras, quando você tem um valor de um tipo (digamos, string) e deseja interpretá-lo como outro tipo (int) e deseja forçá-lo a se tornar um dos conjuntos de valores válidos nesse 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

Não consigo ver o que o upcasting (para um tipo que engloba mais valores) realmente ganharia você.

Então, embora eu não concorde com o uso proposto da digitação (você está essencialmente propondo a digitação estática , mas com o ambiguidade que somente se fosse força-cast em um tipo seria um erro - o que causaria confusão), eu acho que é uma boa pergunta, porque aparentemente o elenco tem muito pouca finalidade em PHP.

    
por 07.12.2010 / 22:03
fonte
15

Você está misturando os conceitos de tipo fraco / strong e dinâmico / estático.

O PHP é fraco e dinâmico, mas o seu problema é com o conceito de tipo dinâmico. Isso significa que as variáveis não têm um tipo, valores fazem.

Um 'tipo casting' é uma expressão que produz um novo valor de um tipo diferente do original; não faz nada para a variável (se estiver envolvido).

A única situação em que eu digito regularmente os valores de conversão é em parâmetros SQL numéricos. Você deve higienizar / excluir qualquer valor de entrada inserido nas instruções SQL ou (muito melhor) usar consultas parametrizadas. Mas, se você quiser algum valor que DEVE ser um inteiro, é muito mais fácil apenas lançá-lo.

Considere:

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

se eu deixasse de fora a primeira linha, $id seria um vetor fácil para injeção de SQL. O elenco garante que seja um inteiro inofensivo; qualquer tentativa de inserir algum SQL simplesmente resultaria em uma consulta para id=0

    
por 07.12.2010 / 15:23
fonte
4

Um uso para conversão de tipos em PHP que encontrei:

Estou desenvolvendo um aplicativo para Android que faz solicitações HTTP para scripts PHP em um servidor para recuperar dados de um banco de dados. O script armazena dados na forma de um objeto PHP (ou array associativo) e é retornado como um objeto JSON para o aplicativo. Sem o tipo casting eu receberia algo assim:

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

Mas, usando o tipo de PHP castando (int) no id do usuário ao armazenar o objeto PHP, recebo isso retornado ao aplicativo:

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

Então, quando o objeto JSON é analisado no aplicativo, ele me impede de ter que analisar o id para um Integer!

Veja, muito útil.

    
por 02.12.2013 / 21:42
fonte
3

Um exemplo são objetos com um método __toString: $str = $obj->__toString(); vs %código%. Há muito menos digitação no segundo, e o material extra é pontuação, o que leva mais tempo para ser digitado. Eu também acho que é mais legível, embora outros possam discordar.

Outra é criar uma matriz de elemento único: $str = (string) $obj; vs array($item); . Isto irá colocar qualquer tipo escalar (inteiro, recurso, etc) dentro de uma matriz.
Altenatively, se (array) $item; for um objeto, suas propriedades se tornarão chaves para seus valores. No entanto, acho que a conversão de array objeto e > é um pouco estranha: propriedades privadas e protegidas fazem parte do array e são renomeadas. Para citar a documentação do PHP : as variáveis privadas têm o nome da classe prefixado ao nome da variável; Variáveis protegidas têm um '*' prefixado ao nome da variável.

Outro uso é converter dados GET / POST em tipos apropriados para um banco de dados. O MySQL pode lidar com isso sozinho, mas acho que os servidores mais compatíveis com ANSI podem rejeitar os dados. A razão de eu mencionar apenas os bancos de dados é que na maioria dos outros casos, os dados terão uma operação executada de acordo com seu tipo em algum momento (ou seja, int / floats normalmente terá cálculos executados neles, etc.).

    
por 07.12.2010 / 15:45
fonte
0

Este script:

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

será executado corretamente para script.php?tags[]=one , mas falhará para script.php?tags=one , porque _GET['tags'] retornará uma matriz no primeiro caso, mas não no segundo. Como o script é escrito para esperar uma matriz (e você tem menos controle sobre a string de consulta enviada ao script), o problema pode ser resolvido apropriadamente convertendo o resultado de _GET :

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

Ele também pode ser usado como um método rápido e sujo para garantir que dados não confiáveis não quebrem algo, por exemplo, se estiver usando um serviço remoto que tenha validação de lixo e só deve aceitar números.

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

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

Obviamente, isso é falho e também tratado implicitamente pelo operador de comparação na instrução if, mas é útil para garantir que você saiba exatamente o que seu código está fazendo.

    
por 07.12.2010 / 17:12
fonte
-2

Um "uso" do PHP re-casting de variáveis on-the-fly que eu vejo em uso, muitas vezes é ao recuperar dados de fontes externas (entrada do usuário ou banco de dados). Ele permite que os codificadores (note que eu não disse desenvolvedores) ignoram (ou nem aprendem) os diferentes tipos de dados disponíveis de diferentes fontes.

Um codificador (note que eu não disse desenvolvedor) cujo código eu herdei e ainda mantenho parece não saber que existe existe uma diferença entre a string "20" que é retornou na variável $_GET super, para entre a operação inteira 20 + 20 quando ela o adiciona ao valor no banco de dados. Ela só tem sorte que o PHP use . para concatenação de strings e não + como todas as outras linguagens, porque vi o código dela "adicionar" duas strings (um varcahr do MySQL e um valor de $_GET ) e obtenha um int.

Este é um exemplo prático? Apenas no sentido de permitir que os programadores não saibam com que tipos de dados estão trabalhando. Eu pessoalmente odeio isso.

    
por 22.03.2014 / 16:41
fonte