PHP 8.3.4 Released!

Declarações de tipo

As declarações de tipo podem ser adicionadas a argumentos de função, valores de retorno e, a partir do PHP 7.4.0, propriedades de classe. Eles garantem que o valor seja do tipo especificado no momento da chamada, caso contrário, um TypeError é lançado.

Cada tipo que o PHP suporta, com exceção de resource, pode ser usado dentro de uma declaração de tipo de usuário. Esta página contém um changelog de disponibilidade dos diferentes tipos e documentação sobre o uso deles em declarações de tipo.

Nota:

Quando uma classe implementa um método de interface ou reimplementa um método já definido por uma classe pai, ela deve ser compatível com a definição acima. Um método é compatível se segue as regras de variância.

Registro de Alterações

Versão Descrição
8.3.0 Suporte à constantes em classes, interfaces, traits e enums foi adicionado.
8.2.0 O suporte para tipos DNF foi adicionado.
8.2.0 O suporte para o tipo literal true foi adicionado.
8.2.0 Os tipos null e false agora podem ser usados de forma independente.
8.1.0 O suporte para tipos de interseção foi adicionado.
8.1.0 Retornar por referência de uma função void agora foi descontinuado.
8.1.0 O suporte para o tipo somente retorno never foi adicionado.
8.0.0 O suporte para mixed foi adicionado.
8.0.0 O suporte para o tipo de retorno apenas static foi adicionado.
8.0.0 O suporte para tipos de união foi adicionado.
7.4.0 Suporte para propriedades tipadas foi adicionado.
7.2.0 Suporte para object foi adicionado.
7.1.0 Suporte para iterable foi adicionado.
7.1.0 Suporte para void foi adicionado.
7.1.0 O suporte para tipos anuláveis foi adicionado.

Notas de uso em tipos atômicos

Os tipos atômicos têm um comportamento direto com algumas ressalvas menores que são descritas nesta seção.

Tipos escalares

Aviso

Aliases de nome para tipos escalares (bool, int, float, string) não são suportados. Em vez disso, eles são tratados como nomes de classe ou interface. Por exemplo, usar boolean como uma declaração de tipo exigirá que o valor seja uma instanceof da classe ou interface boolean, em vez do tipo bool:

<?php
function test(boolean $param) {}
test(true);
?>

Saída do exemplo acima no PHP 8:

Warning: "boolean" will be interpreted as a class name. Did you mean "bool"? Write "\boolean" to suppress this warning in /in/9YrUX on line 2

Fatal error: Uncaught TypeError: test(): Argument #1 ($param) must be of type boolean, bool given, called in - on line 3 and defined in -:2
Stack trace:
#0 -(3): test(true)
#1 {main}
  thrown in - on line 2

void

Nota:

Retornar por referência de uma função void foi descontinuado a partir do PHP 8.1.0, porque tal função é contraditória. Anteriormente, ele já emitia o seguinte E_NOTICE quando chamado: Only variable references should be returned by reference.

<?php
function &test(): void {}
?>

Tipos que podem ser chamados

Este tipo não pode ser usado como uma declaração de tipo de propriedade de classe.

Nota: Não é possível especificar a assinatura da função.

Declarações de tipo em parâmetros de passagem por referência

Se um parâmetro de passagem por referência tiver uma declaração de tipo, o tipo da variável é somente verificado na entrada da função, no início da chamada, mas não quando a função retorna. Isso significa que uma função pode alterar o tipo de referência de variável.

Exemplo #1 Parâmetros de passagem por referência digitados

<?php
function array_baz(array &$param)
{
$param = 1;
}
$var = [];
array_baz($var);
var_dump($var);
array_baz($var);
?>

O exemplo acima produzirá algo semelhante a:

int(1)

Fatal error: Uncaught TypeError: array_baz(): Argument #1 ($param) must be of type array, int given, called in - on line 9 and defined in -:2
Stack trace:
#0 -(9): array_baz(1)
#1 {main}
  thrown in - on line 2

Notas de Uso de Tipos Compostos

As declarações de tipo composto estão sujeitas a algumas restrições e realizarão uma verificação de redundância no tempo de compilação para evitar erros simples.

Cuidado

Antes do PHP 8.2.0 e da introdução dos tipos DNF, não era possível combinar tipos de interseção com tipos de união.

Uniao de tipos

Aviso

Não é possível combinar os dois valores false e true juntos em um tipo de união. Em vez disso, use bool.

Cuidado

Antes do PHP 8.2.0, como false e null não podiam ser usados como tipos autônomos, um tipo de união composto apenas por esses tipos não era permitido. Isso inclui os seguintes tipos: false, false|null, e ?false.

Açúcar sintático de tipo anulável

Uma única declaração de tipo base pode ser marcada como anulável prefixando o tipo com um ponto de interrogação (?). Assim, ?T e T|null são idênticos.

Nota: Esta sintaxe é suportada a partir do PHP 7.1.0, e é anterior ao suporte generalizado de tipos de união.

Nota:

Também é possível obter argumentos anuláveis tornando null o valor padrão. Isso não é recomendado, pois se o valor padrão for alterado em uma classe filha, uma violação de compatibilidade de tipo será gerada, pois o tipo null precisará ser adicionado à declaração de tipo.

Exemplo #2 Maneira antiga de tornar os argumentos anuláveis

<?php
class C {}

function
f(C $c = null) {
var_dump($c);
}

f(new C);
f(null);
?>

O exemplo acima produzirá:

object(C)#1 (0) {
}
NULL

Tipos duplicados e redundantes

Para capturar bugs simples em declarações de tipo composto, tipos redundantes que podem ser detectados sem executar o carregamento de classe resultarão em um erro de tempo de compilação. Isso inclui:

  • Cada tipo resolvido por nome pode ocorrer apenas uma vez. Tipos como int|string|INT ou Countable&Traversable&COUNTABLE resultam em erro.
  • O uso mixed resulta em erro.
  • Para tipos de união:
    • Se bool for usado, false ou true não pode ser usado adicionalmente.
    • Se object for usado, os tipos de classe não podem ser usados adicionalmente.
    • Se iterable for usado, array e Traversable não podem ser usados adicionalmente.
  • Para interseção de tipos:
    • Usar um tipo que não é um tipo de classe resulta em erro.
    • O uso de self, parent ou static resulta em erro.
  • Para tipos DNF:
    • Se um tipo mais genérico for usado, o mais restritivo será redundante.
    • Usando dois tipos de interseção idênticos.

Nota: Isso não garante que o tipo seja “mínimo”, porque isso exigiria o carregamento de todos os tipos de classe usados.

Por exemplo, se A e B são aliases de classe, A|B continua sendo um tipo de união legal, mesmo que possa ser reduzido a A ou B. Da mesma forma, se a classe B extends A {}, A|B também é um tipo de união legal, embora pudesse ser reduzida a apenas A.

<?php
function foo(): int|INT {} // Proibido
function foo(): bool|false {} // Proibido
function foo(): int&Traversable {} // Proibido
function foo(): self&Traversable {} // Proibido

use A as B;
function
foo(): A|B {} // Não permitido ("use" faz parte da resolução de nomes)
function foo(): A&B {} // Não permitido ("use" faz parte da resolução de nomes)

class_alias('X', 'Y');
function
foo(): X|Y {} // Permitido (a redundância só é conhecida em tempo de execução)
function foo(): X&Y {} // Permitido (a redundância só é conhecida em tempo de execução)
?>

Exemplos

Exemplo #3 Declaração de tipo de classe básica

<?php
class C {}
class
D extends C {}

// Isso não estende C.
class E {}

function
f(C $c) {
echo
get_class($c)."\n";
}

f(new C);
f(new D);
f(new E);
?>

Saída do exemplo acima no PHP 8:

C
D

Fatal error: Uncaught TypeError: f(): Argument #1 ($c) must be of type C, E given, called in /in/gLonb on line 14 and defined in /in/gLonb:8
Stack trace:
#0 -(14): f(Object(E))
#1 {main}
  thrown in - on line 8

Exemplo #4 Declaração de tipo de interface básica

<?php
interface I { public function f(); }
class
C implements I { public function f() {} }

// Isso não implementa I.
class E {}

function
f(I $i) {
echo
get_class($i)."\n";
}

f(new C);
f(new E);
?>

Saída do exemplo acima no PHP 8:

C

Fatal error: Uncaught TypeError: f(): Argument #1 ($i) must be of type I, E given, called in - on line 13 and defined in -:8
Stack trace:
#0 -(13): f(Object(E))
#1 {main}
  thrown in - on line 8

Exemplo #5 Declaração de tipo de retorno básico

<?php
function sum($a, $b): float {
return
$a + $b;
}

// Observe que um float será retornado.
var_dump(sum(1, 2));
?>

O exemplo acima produzirá:

float(3)

Exemplo #6 Retornando um objeto

<?php
class C {}

function
getC(): C {
return new
C;
}

var_dump(getC());
?>

O exemplo acima produzirá:

object(C)#1 (0) {
}

Exemplo #7 Declaração de tipo de argumento anulável

<?php
class C {}

function
f(?C $c) {
var_dump($c);
}

f(new C);
f(null);
?>

O exemplo acima produzirá:

object(C)#1 (0) {
}
NULL

Exemplo #8 Declaração de tipo de retorno anulável

<?php
function get_item(): ?string {
if (isset(
$_GET['item'])) {
return
$_GET['item'];
} else {
return
null;
}
}
?>

Exemplo #9 Declaração de tipo de propriedade

<?php
class User {
public static
string $foo = 'foo';

public
int $id;
public
string $username;

public function
__construct(int $id, string $username) {
$this->id = $id;
$this->username = $username;
}
}
?>

Tipagem estrita

Por padrão, o PHP forçará valores do tipo errado na declaração de tipo escalar esperada, se possível. Por exemplo, uma função que recebe um int para um parâmetro que espera uma string obterá uma variável do tipo string.

É possível ativar o modo estrito por arquivo. No modo estrito, apenas um valor correspondente exatamente à declaração do tipo será aceito, caso contrário, um TypeError será lançado. A única exceção a essa regra é que um valor int passará por uma declaração do tipo float.

Aviso

Chamadas de função dentro de funções internas não serão afetadas pela declaração strict_types.

Para habilitar o modo estrito, a declare é usada com a declaração strict_types:

Nota:

A digitação estrita se aplica a chamadas de função feitas de dentro do arquivo com tipagem estrita habilitada, não para as funções declaradas dentro desse arquivo. Se um arquivo sem tipagem estrita habilitada fizer uma chamada para uma função que foi definida em um arquivo com tipagem estrita, a preferência do chamador (tipagem coercitiva) será respeitada e o valor será convertido.

Nota:

A tipagem estrita é definida apenas para declarações de tipo escalar.

Exemplo #10 Tipagem estrita para valores de argumentos

<?php
declare(strict_types=1);

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

var_dump(sum(1, 2));
var_dump(sum(1.5, 2.5));
?>

Saída do exemplo acima no PHP 8:

int(3)

Fatal error: Uncaught TypeError: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 9 and defined in -:4
Stack trace:
#0 -(9): sum(1.5, 2.5)
#1 {main}
  thrown in - on line 4

Exemplo #11 Tipagem coercitiva para valores de argumento

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

var_dump(sum(1, 2));

// Estes serão convertidos em números inteiros: observe a saída abaixo!
var_dump(sum(1.5, 2.5));
?>

O exemplo acima produzirá:

int(3)
int(3)

Exemplo #12 Tipagem estrita para valores de retorno

<?php
declare(strict_types=1);

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

var_dump(sum(1, 2));
var_dump(sum(1, 2.5));
?>

O exemplo acima produzirá:

int(3)

Fatal error: Uncaught TypeError: sum(): Return value must be of type int, float returned in -:5
Stack trace:
#0 -(9): sum(1, 2.5)
#1 {main}
  thrown in - on line 5
add a note

User Contributed Notes 2 notes

up
18
toinenkayt (ta at ta) [iwonderr] gmail d
2 years ago
While waiting for native support for typed arrays, here are a couple of alternative ways to ensure strong typing of arrays by abusing variadic functions. The performance of these methods is a mystery to the writer and so the responsibility of benchmarking them falls unto the reader.

PHP 5.6 added the splat operator (...) which is used to unpack arrays to be used as function arguments. PHP 7.0 added scalar type hints. Latter versions of PHP have further improved the type system. With these additions and improvements, it is possible to have a decent support for typed arrays.

<?php
declare (strict_types=1);

function
typeArrayNullInt(?int ...$arg): void {
}

function
doSomething(array $ints): void {
(function (?
int ...$arg) {})(...$ints);
// Alternatively,
(fn (?int ...$arg) => $arg)(...$ints);
// Or to avoid cluttering memory with too many closures
typeArrayNullInt(...$ints);

/* ... */
}

function
doSomethingElse(?int ...$ints): void {
/* ... */
}

$ints = [1,2,3,4,null];
doSomething ($ints);
doSomethingElse (...$ints);
?>

Both methods work with all type declarations. The key idea here is to have the functions throw a runtime error if they encounter a typing violation. The typing method used in doSomethingElse is cleaner of the two but it disallows having any other parameters after the variadic parameter. It also requires the call site to be aware of this typing implementation and unpack the array. The method used in doSomething is messier but it does not require the call site to be aware of the typing method as the unpacking is performed within the function. It is also less ambiguous as the doSomethingElse would also accept n individual parameters where as doSomething only accepts an array. doSomething's method is also easier to strip away if native typed array support is ever added to PHP. Both of these methods only work for input parameters. An array return value type check would need to take place at the call site.

If strict_types is not enabled, it may be desirable to return the coerced scalar values from the type check function (e.g. floats and strings become integers) to ensure proper typing.
up
12
crash
2 years ago
The documentation lacks the information, that it's possible to change the return type of a method defined in an interface when the interface's methods return type is defined as `mixed`.

From the RFC:

"The mixed return type could be narrowed in a subclass as this is covariant and is allowed in LSP." (https://wiki.php.net/rfc/mixed_type_v2)

This means the following code is valid in PHP 8.0:

<?php

interface ITest
{
public function
apfel(): mixed; // valid as of 8.0
}

class
Test implements ITest
{
public function
apfel(): array // more explicit
{
return [];
}
}

var_dump((new Test())->apfel());
?>

You can see the result here: https://3v4l.org/PXDB6
To Top