PHP Conversão de Tipos

Um formulário pergunta a idade da cliente, ela digita 29, e o PHP recebe… a string "29". Tudo o que vem da web chega como texto. Para fazer contas, o texto precisa virar número; para mostrar um número, ele precisa virar texto. Essa troca de tipo se chama conversão. Às vezes o PHP faz sozinho (e às vezes faz o que você não queria); às vezes você pede. Nesta aula você aprende as duas formas, vê o que acontece com textos que quase são números e conhece a chave que liga o "modo rigoroso" do PHP.

Você vai aprender
  • Como o PHP converte os tipos sozinho (type juggling) em contas, textos e decisões.
  • O que acontece com "10" + 5, "10 maçãs" + 5 e "abc" + 1 no PHP 8.
  • Como converter você mesmo: (int), (float), (string), (bool).
  • As funções intval, floatval, strval, boolval e settype.
  • O que é declare(strict_types=1), só para reconhecê-lo.

Os programas desta aula rodaram com o PHP 8.5 na máquina fictícia da Ana, na pasta ~/site. Você já conhece os tipos (Tipos de Dados) e o var_dump (var_dump e print_r), que vamos usar o tempo todo.


A ideia: o tradutor automático e o tradutor contratado

Imagine uma loja com clientes estrangeiros. Há duas formas de entender o que eles dizem:

  • Um tradutor automático, que traduz sozinho sempre que acha necessário. É rápido, mas às vezes traduz errado sem avisar.
  • Um tradutor contratado, a quem você pede explicitamente: "traduza isto para português". Dá mais trabalho, mas você sabe exatamente o que foi feito.

O PHP tem os dois. A conversão automática (chamada type juggling, "malabarismo de tipos") acontece sozinha quando o PHP precisa de um tipo e recebe outro. A conversão explícita (o casting) é quando você escreve no código "converta para inteiro".


1. A conversão automática

O PHP olha o operador para decidir o tipo de que precisa. O + precisa de números; o ponto (.) precisa de texto; o if precisa de booleano. Se recebe outro tipo, converte:

<?php
var_dump("10" + 5);
var_dump("10" . 5);
var_dump("1.5" + 1);
var_dump(" 10" + 5);
var_dump("1e2" + 0);
var_dump(true + 1);
var_dump(null + 1);
var_dump("10" * "2");
$ php automatica.php
int(15)                  « "10" virou o número 10 para a soma
string(3) "105"         « o ponto quer texto: o 5 virou "5" e foi colado
float(2.5)               « "1.5" virou float
int(15)                  « espaços no começo (e no fim) são tolerados
float(100)               « "1e2" é notação científica: 100.0
int(2)                   « true vale 1
int(1)                   « null vale 0
int(20)                  « os dois textos viraram números
OndeO PHP precisa deExemploResultado
+ - * / % **número"10" + 515
. e echotexto"10" . 5"105"
if, while, !, &&booleanoif ("0")falso (aula Booleanos)
==, <, >tipos "compatíveis"10 == "10"true (aula Comparação)

Repare na diferença das duas primeiras linhas: o mesmo par "10" e 5 deu 15 com + e "105" com .. Em PHP, o operador decide. (Em JavaScript, "10" + 5 dá "105"; o PHP evita essa confusão tendo um operador separado para juntar texto, o ponto.)


2. Texto que é número, que quase é, e que não é

Para fazer conta com uma string, o PHP classifica o texto em três grupos. Cada grupo tem um comportamento diferente no PHP 8:

GrupoExemplosNuma conta
Numérico: só um número (espaços nas pontas são aceitos)"10", " 10", "1.5", "1e2", "-3"Vira o número, sem aviso.
Começa com número: número seguido de outra coisa"10 maçãs", "12abc", "12,90"Usa o número do começo e dá Warning.
Não numérico"abc", "maçã", ""TypeError: o programa para.
<?php
var_dump("10 maçãs" + 5);
echo "--\n";
var_dump("abc" + 1);
echo "fim\n";
$ php macas.php

Warning: A non-numeric value encountered in /home/ana/site/macas.php on line 2
int(15)                  « usou o 10 do começo, mas reclamou
--

Fatal error: Uncaught TypeError: Unsupported operand types: string + int in /home/ana/site/macas.php:4
Stack trace:
#0 {main}
  thrown in /home/ana/site/macas.php on line 4
$ echo $?
255                      « o "fim" nunca apareceu
Warning: A non-numeric value encountered
"Foi encontrado um valor não numérico". O PHP aproveitou o 10 do começo e jogou fora o resto (" maçãs"), mas avisa: provavelmente não era isso que você queria.
TypeError: Unsupported operand types: string + int
"Erro de tipo: tipos de operandos não suportados: string mais int". Não há nenhum número em "abc": o PHP 8 se recusa a inventar um e para o programa.
Isso mudou no PHP 8

No PHP 7, "abc" + 1 dava só um aviso e o resultado 1 (o texto valia zero). O PHP 8 ficou mais rigoroso: texto sem número numa conta agora é erro. Se você encontrar tutoriais antigos dizendo que "string sem número vale 0 numa soma", eles estão falando do PHP 7.

O caso "12,90" merece atenção especial no Brasil: o PHP usa ponto como separador decimal, então a vírgula é "outra coisa" e tudo depois dela é descartado. "12,90" * 2 dá 24, com Warning. Você vai consertar isso na missão final.

Experimente: troque o texto de $quantidade e veja em qual grupo ele cai: "3", " 3 ", "3.5", "3 unidades", "três", "".

<?php
$quantidade = "3 unidades";
var_dump($quantidade * 2);

3. Converter você mesmo: o casting

Para converter de propósito, escreva o tipo desejado entre parênteses antes do valor. Isso se chama casting (algo como "moldar"):

(int) $x
Converte para inteiro.
(float) $x
Converte para float.
(string) $x
Converte para texto.
(bool) $x
Converte para booleano.
(array) $x
Converte para array (põe o valor dentro de um array).

O casting nunca dá Warning nem erro por texto estranho: você está dizendo "eu sei o que estou fazendo". Por isso, é bom conhecer as regras.

Para int

<?php
var_dump((int) "42");
var_dump((int) "42abc");
var_dump((int) "abc");
var_dump((int) 3.99);
var_dump((int) -3.99);
var_dump((int) "3.99");
var_dump((int) true);
var_dump((int) null);
var_dump((int) "");
var_dump((int) "1e3");
var_dump((int) "0x1A");
var_dump((int) "012");
$ php para-int.php
int(42)
int(42)                  « pega o número do começo, em silêncio
int(0)                   « sem número: 0
int(3)                   « corta a parte decimal (não arredonda!)
int(-3)                  « corta em direção ao zero
int(3)
int(1)
int(0)
int(0)
int(1000)                « entende notação científica
int(0)                   « NÃO entende hexadecimal em texto
int(12)                  « nem octal: o zero à esquerda é ignorado

O mais importante: (int) corta, não arredonda. (int) 3.99 é 3. Para arredondar, use (int) round(3.99) (aula int e float).

Para float, string e bool

<?php
var_dump((float) "3.14");
var_dump((float) "3,14");
var_dump((float) 5);
var_dump((string) 42);
var_dump((string) 3.0);
var_dump((string) true);
var_dump((string) false);
var_dump((string) null);
var_dump((bool) "0");
var_dump((bool) "abc");
$ php outros.php
float(3.14)
float(3)                 « a vírgula interrompe: só o 3 foi lido
float(5)
string(2) "42"
string(1) "3"            « 3.0 vira "3", sem o ".0"
string(1) "1"            « true vira "1"
string(0) ""             « false vira texto vazio
string(0) ""             « null também
bool(false)              « "0" é falso
bool(true)
Valor(int)(float)(string)(bool)
"42"4242.0"42"true
"42abc"4242.0"42abc"true
"abc"00.0"abc"true
"0"00.0"0"false
""00.0""false
3.9933.99"3.99"true
true11.0"1"true
false00.0""false
null00.0""false
Novidade do PHP 8.5: float grande demais para int

Converter para int um float que não cabe num inteiro (ou o NAN) agora dá Warning, porque o resultado é lixo:

$ php grande.php             « var_dump((int) 1e20);

Warning: The float 1.0E+20 is not representable as an int, cast occurred in /home/ana/site/grande.php on line 2
int(7766279631452241920)

Not representable as an int = "não pode ser representado como int".

Missão: a idade veio do formulário como texto. Converta-a para inteiro antes de somar, guardando em $idade, para o var_dump do ano que vem mostrar int(30). Use (int) ou intval.

<?php
$idadeDoFormulario = "29";
$idade = $idadeDoFormulario;
var_dump($idade);
var_dump($idade + 1);
<?php
$idadeDoFormulario = "29";
$idade = (int) $idadeDoFormulario;
var_dump($idade);
var_dump($idade + 1);

4. As funções de conversão: intval, floatval, strval, boolval

Cada casting tem uma função equivalente. intval("42") é o mesmo que (int) "42". A vantagem das funções é poderem ser passadas para outras funções (como array_map("intval", $lista), em Funções de Array) e, no caso do intval, escolher a base do número:

<?php
var_dump(intval("42"));
var_dump(intval("12abc"));
var_dump(intval("0x1A", 16));
var_dump(intval("101", 2));
var_dump(intval("012", 0));
var_dump(floatval("1.5kg"));
var_dump(strval(10));
var_dump(boolval([]));
$ php funcoes.php
int(42)
int(12)
int(26)                  « lido como hexadecimal (base 16)
int(5)                   « lido como binário (base 2)
int(10)                  « base 0: o PHP olha o prefixo; "012" é octal
float(1.5)
string(2) "10"
bool(false)              « array vazio é falso
intval($x, $base)
Converte para int. A base (2, 8, 10, 16…) só vale para textos; o padrão é 10. Com base 0, o PHP decide pelo prefixo (0x, 0, 0b).
floatval($x)
O mesmo que (float).
strval($x)
O mesmo que (string).
boolval($x)
O mesmo que (bool).

5. settype: mudar o tipo da própria variável

O casting e as funções devolvem um valor novo; a variável original continua como estava. O settype ("definir o tipo") é diferente: ele muda a própria variável.

<?php
$x = "123";
settype($x, "integer");
var_dump($x);
$ php settype.php
int(123)                 « a variável $x agora guarda um int

Os nomes aceitos são "int" ou "integer", "float", "string", "bool" ou "boolean", "array" e "null". Ele devolve true quando consegue. É pouco usado: $x = (int) $x; faz o mesmo e é mais claro.

Qual usar?

SituaçãoUse
Converter um valor (o mais comum)(int) $x, (float) $x, (string) $x
Ler um número em outra baseintval($x, 16)
Converter todos os itens de um arrayarray_map("intval", $lista)
Validar o que veio do usuário antes de converteris_numeric($x) ou filter_var (veja Validar Dados)
Mudar o tipo da própria variável$x = (int) $x; (ou settype)
Converter não é validar

(int) "abc" dá 0 sem reclamar. Se a cliente digitou "abc" no campo idade, converter em silêncio esconde o problema. Primeiro confira (is_numeric), depois converta.


6. declare(strict_types=1): o modo rigoroso (só apresentação)

No PHP moderno, você pode dizer o tipo que uma função espera receber (aula Tipos e Retorno). Por padrão, o PHP ainda faz conversão automática nessa entrada: se a função espera int e recebe "5", ele converte:

<?php
function dobro(int $n): int
{
    return $n * 2;
}
var_dump(dobro(5));
var_dump(dobro("5"));
var_dump(dobro("cinco"));
$ php sem-strict.php
int(10)
int(10)                  « "5" foi convertido para 5

Fatal error: Uncaught TypeError: dobro(): Argument #1 ($n) must be of type int, string given, called in /home/ana/site/sem-strict.php on line 8 and defined in /home/ana/site/sem-strict.php:2
Stack trace:
#0 /home/ana/site/sem-strict.php(8): dobro('cinco')
#1 {main}
  thrown in /home/ana/site/sem-strict.php on line 2

Com a linha declare(strict_types=1); no topo do arquivo, o PHP desliga essa conversão nas chamadas de função: o tipo tem que ser exatamente o pedido. Até "5" é recusado:

<?php
declare(strict_types=1);

function dobro(int $n): int
{
    return $n * 2;
}
var_dump(dobro(5));
var_dump(dobro("5"));
$ php com-strict.php
int(10)

Fatal error: Uncaught TypeError: dobro(): Argument #1 ($n) must be of type int, string given, called in /home/ana/site/com-strict.php on line 9 and defined in /home/ana/site/com-strict.php:4
Stack trace:
#0 /home/ana/site/com-strict.php(9): dobro('5')
#1 {main}
  thrown in /home/ana/site/com-strict.php on line 4
declare(…)
"Declarar": dá uma instrução ao PHP sobre como ler este arquivo.
strict_types=1
"Tipos estritos: ligado". Vale só para o arquivo onde está escrito.
must be of type int, string given
"Deve ser do tipo int; foi dada uma string".

Três coisas para saber agora:

  • Ele precisa ser a primeira instrução do arquivo, logo depois do <?php. Senão: Fatal error: strict_types declaration must be the very first statement in the script.
  • Ele vale para chamadas de função (as suas e as do PHP: strlen(123) vira TypeError). Não muda os operadores: "10" + 5 continua dando 15.
  • Muitos projetos modernos e frameworks colocam essa linha em todos os arquivos. Ela pega erros cedo, em vez de deixar o PHP "adivinhar". Você vai usá-la a partir de Tipos e Retorno.

Quando dá errado

Warning: A non-numeric value encountered

O que significa: numa conta, uma string começava com número mas tinha mais coisa depois ("10 maçãs", "12,90", "5 kg"). O PHP usou só o número do começo.

Como consertar: limpe o texto antes. Para vírgula decimal: (float) str_replace(",", ".", $preco). Para unidades, separe o número do texto.

Fatal error: Uncaught TypeError: Unsupported operand types: string + int

O que significa: numa conta, uma string sem nenhum número no começo ("abc", "", "R$ 10").

Como consertar: confira o valor com var_dump. Valide com is_numeric antes de calcular. Repare que "R$ 10" não começa com número: tire o R$ antes.

TypeError: ...(): Argument #1 (...) must be of type int, string given

O que significa: uma função que exige int recebeu uma string que não dá para converter, ou o arquivo tem strict_types=1 e a string não é aceita de jeito nenhum.

Como consertar: converta antes de chamar: dobro((int) $x) (depois de validar).

Fatal error: strict_types declaration must be the very first statement in the script
$ cat fora-do-lugar.php
<?php
echo "oi\n";
declare(strict_types=1);
$ php fora-do-lugar.php

Fatal error: strict_types declaration must be the very first statement in the script in /home/ana/site/fora-do-lugar.php on line 3
Stack trace:
#0 {main}

Como consertar: mova o declare para logo depois do <?php.

A conversão deu 0 ou um número menor que o esperado

O que significa: (int) cortou a parte decimal (3.99 → 3), parou na vírgula ("3,5" → 3) ou o texto não tinha número ("abc" → 0).

Como consertar: arredonde com round, troque a vírgula por ponto e valide antes.


Referência rápida

EscritaFazExemplo → resultado
(int) $x, intval($x)Para inteiro (corta decimais)(int) "3.99" → 3
intval($x, $base)Para inteiro, lendo na base dadaintval("ff", 16) → 255
(float) $x, floatval($x)Para float(float) "1.5kg" → 1.5
(string) $x, strval($x)Para texto(string) false → ""
(bool) $x, boolval($x)Para booleano(bool) "0" → false
(array) $xPara array(array) "Ana" → ["Ana"]
settype($x, "int")Muda o tipo da própria variável
declare(strict_types=1);Desliga a conversão automática nas chamadas de função do arquivo

Exercícios

1. Para inteiro

Complete para converter $texto em inteiro com casting:

$n = $texto;

2. Ponto ou mais?

Qual operador faz "10" ? 5 dar "105"?

var_dump("10" 5);

3. Quanto vale (int) 7.9?

4. O que acontece com "abc" + 1 no PHP 8?

5. Com declare(strict_types=1); no topo, o que dá "10" + 5?

Missão final: o preço com vírgula

Missão: o preço veio do formulário escrito do jeito brasileiro, "12,90", e a conta dá Total: 24 com um Warning. Antes de multiplicar, troque a vírgula por ponto com str_replace(",", ".", $preco) e converta o resultado para float com (float). A saída deve ser Total: 25.8, sem avisos.

<?php
$preco = "12,90";
$total = $preco * 2;
echo "Total: $total\n";
<?php
$preco = "12,90";
$preco = (float) str_replace(",", ".", $preco);
$total = $preco * 2;
echo "Total: $total\n";

Resumo

  • O PHP converte sozinho conforme o operador: + quer número, . quer texto, if quer booleano.
  • Texto numérico ("10") vira número em silêncio; texto que começa com número ("10 maçãs") dá Warning; texto sem número ("abc") dá TypeError no PHP 8.
  • Para converter de propósito: (int), (float), (string), (bool), ou intval, floatval, strval, boolval. (int) corta, não arredonda.
  • settype muda a própria variável. Converter não é validar: confira antes.
  • declare(strict_types=1);, na primeira linha, desliga a conversão automática nas chamadas de função do arquivo.