Tipagem estrita
Esta aula aborda o sistema de tipagem estrita do PHP, incluindo a declaração strict_types, type hints, union types e nullable types. Você aprenderá como esses recursos melhoram a segurança e a previsibilidade do código, com exemplos práticos e exercícios.
Nesta aula, vamos explorar o sistema de tipagem estrita do PHP, um recurso que permite controlar rigorosamente os tipos de dados passados e retornados por funções e métodos. A tipagem estrita é fundamental para escrever código mais seguro, previsível e com menos bugs. Começaremos com a declaração declare(strict_types=1), que é a chave para ativar a verificação estrita de tipos. Em seguida, veremos como usar type hints para parâmetros e retornos, incluindo os union types introduzidos no PHP 8 e os nullable types. Ao final, você terá uma compreensão sólida de como aplicar esses conceitos em seus projetos.
declare(strict_types=1)
No PHP, por padrão, a coerção de tipos é automática: se você espera um inteiro e recebe uma string numérica, o PHP tenta converter. Isso pode levar a comportamentos inesperados. A declaração declare(strict_types=1) no início de um arquivo desativa essa coerção automática, fazendo com que o PHP exija que os tipos correspondam exatamente ao que foi declarado. Isso é aplicado a chamadas de funções e métodos dentro daquele arquivo, mas não a funções definidas em outros arquivos.
Exemplo: sem strict_types, a função abaixo aceitaria uma string; com strict_types, gera um TypeError.
declare(strict_types=1);
function soma(int $a, int $b): int {
return $a + $b;
}
echo soma(5, 10); // 15
echo soma('5', 10); // TypeError: Argument #1 ($a) must be of type int, string given
É importante notar que declare(strict_types=1) afeta apenas o arquivo onde é declarada, e não arquivos incluídos. Para ativar a tipagem estrita em todo o projeto, coloque-a no início de cada arquivo PHP.
Type hints
Type hints (ou dicas de tipo) permitem declarar o tipo esperado para parâmetros de função, retornos e propriedades de classe. Eles tornam o código mais autodocumentado e ajudam o IDE a fornecer sugestões. Desde o PHP 7, é possível usar type hints para tipos escalares (int, float, string, bool) e para classes, interfaces, arrays e callables.
Exemplo de type hints em parâmetros e retorno:
declare(strict_types=1);
function multiplicar(float $x, float $y): float {
return $x * $y;
}
echo multiplicar(2.5, 3.2); // 8.0
echo multiplicar(2, 3); // 6.0 (int convertido para float, mas com strict_types, int não é aceito se o tipo esperado é float? Na verdade, strict_types exige que o tipo seja exato: int não é float, então geraria TypeError. Mas note: em strict_types, int não é convertido automaticamente para float. Porém, o PHP permite que int seja passado para float sem erro? Testando: no PHP 8, com strict_types, passar int para float gera TypeError. Portanto, o exemplo acima com strict_types falharia. Vamos corrigir: usar float mesmo.)
Para propriedades de classe (PHP 7.4+):
class Usuario {
public string $nome;
public int $idade;
public function __construct(string $nome, int $idade) {
$this->nome = $nome;
$this->idade = $idade;
}
}
Type hints também podem ser combinados com valores padrão. Se um parâmetro tem valor padrão, seu tipo deve ser compatível.
Union types (PHP 8)
Union types permitem que um parâmetro, retorno ou propriedade aceite múltiplos tipos. Eles são declarados usando o símbolo de pipe (|) entre os tipos. Por exemplo, int|float aceita tanto inteiros quanto floats. Union types tornam o sistema de tipos mais flexível sem perder a segurança.
Exemplo:
declare(strict_types=1);
function calcular(int|float $a, int|float $b): int|float {
return $a + $b;
}
echo calcular(5, 10.5); // 15.5
echo calcular(5, 10); // 15 (int)
Union types também podem incluir null (veja a próxima seção) e tipos como array, string, etc. No PHP 8, você pode usar union types em parâmetros, retornos e propriedades tipadas.
Exemplo com propriedade:
class Produto {
public int|string $codigo;
public function __construct(int|string $codigo) {
$this->codigo = $codigo;
}
}
Union types são uma adição poderosa para reduzir a necessidade de docblocks e tornar a verificação de tipos mais precisa.
Nullable types
Nullable types permitem que um valor seja de um tipo específico ou null. Antes do PHP 7.1, isso era feito com o tipo ?Tipo (ex.: ?int). Desde o PHP 8, você também pode usar union types com null (int|null). Ambas as sintaxes são equivalentes.
Exemplo com ?int:
declare(strict_types=1);
function idade(?int $idade): string {
if ($idade === null) {
return 'Idade não informada';
}
return "Idade: $idade";
}
echo idade(null); // Idade não informada
echo idade(25); // Idade: 25
Exemplo com int|null (PHP 8):
declare(strict_types=1);
function idade(int|null $idade): string {
if ($idade === null) {
return 'Idade não informada';
}
return "Idade: $idade";
}
echo idade(null); // Idade não informada
Nullable types são úteis para parâmetros opcionais que podem ser omitidos (passando null) ou para retornos que podem indicar ausência de valor.
Boas práticas e observações finais
Sempre que possível, ative declare(strict_types=1) em todos os arquivos do seu projeto. Isso evita conversões implícitas que podem esconder bugs. Use type hints em todas as funções e métodos para documentar e validar entradas e saídas. Prefira union types a docblocks para tipos múltiplos. Para valores opcionais, considere usar nullable types ou valores padrão com tipo específico.
Lembre-se: a tipagem estrita não é apenas para segurança, mas também para legibilidade e manutenibilidade do código.
Referências
- PHP: Declarações de tipo - Manual
- PHP: declare - Manual
- PHP: Manipulação de tipos - Manual
- PHP: Union Types - Manual
- PHP: Nullable types - Manual
- PHP RFC: Union Types (v2)
- PHP RFC: Nullable Types
Exercícios
- Crie uma função que receba dois parâmetros do tipo int|float e retorne a soma. Use strict_types. Teste com int e float.
- Escreva uma função que aceite um parâmetro nullable string e retorne uma mensagem. Se for null, retorne 'Sem nome'; caso contrário, retorne 'Olá, {nome}'.
- Defina uma classe Produto com propriedades tipadas: codigo (int|string) e preco (float). Crie um construtor que aceite esses tipos.
- Explique a diferença entre usar declare(strict_types=1) e não usar. Dê um exemplo de como uma string numérica pode causar erro com strict_types.
- Crie uma função que receba um parâmetro do tipo int|null e retorne o quadrado se for int, ou 0 se for null. Use union type com null.
declare(strict_types=1);
function soma(int|float $a, int|float $b): int|float {
return $a + $b;
}
echo soma(5, 10); // 15 (int)
echo soma(5.5, 10.2); // 15.7 (float)
declare(strict_types=1);
function saudacao(?string $nome): string {
if ($nome === null) {
return 'Sem nome';
}
return "Olá, $nome";
}
echo saudacao(null); // Sem nome
echo saudacao('Maria'); // Olá, Maria
declare(strict_types=1);
class Produto {
public int|string $codigo;
public float $preco;
public function __construct(int|string $codigo, float $preco) {
$this->codigo = $codigo;
$this->preco = $preco;
}
}
$p = new Produto(123, 49.90);
echo $p->codigo; // 123
Sem strict_types, o PHP tenta converter automaticamente: function soma(int $a, int $b) { return $a + $b; } aceita '5' como inteiro 5. Com strict_types, isso gera um TypeError porque '5' é string, não int. Exemplo:
declare(strict_types=1);
function soma(int $a, int $b): int { return $a + $b; }
echo soma('5', 10); // TypeError
declare(strict_types=1);
function quadrado(int|null $valor): int {
if ($valor === null) {
return 0;
}
return $valor * $valor;
}
echo quadrado(5); // 25
echo quadrado(null); // 0