PHP 8.0: Notícias em tipos de dados (2/4)
A versão 8.0 do PHP acaba de ser lançada. Está cheio de novas funcionalidades, como nenhuma versão antes. Sua introdução merece quatro artigos separados. Na segunda parte, vamos dar uma olhada nos tipos de dados.
Vamos voltar à história. A introdução de dicas do tipo escalar foi um
avanço significativo no PHP 7. Isso quase não aconteceu. Andreu Faulds, o autor da engenhosa solução,
totalmente compatível com o passado e opcional graças a
declare(strict_types=1)
, foi duramente rejeitada pela comunidade.
Felizmente, Anthony Ferrara a defendeu
e a proposta da época, lançou uma campanha e o RFC passou muito perto. Whew.
A maioria das notícias no PHP 8 são cortesia do legendário Nikita Popov e todos passaram a votação
como uma faca através da manteiga. O mundo está mudando para melhor.
O PHP 8 leva os tipos à perfeição. A grande maioria das anotações do
phpDoc como @param
, @return
e @var
serão
substituídas por notações nativas. Mas o mais importante, os tipos serão
verificados pelo mecanismo PHP. Somente descrições de estruturas como
string[]
ou anotações mais complexas para o PHPStan
permanecerão nos comentários.
Tipos de União
Os tipos de união são uma enumeração de dois ou mais tipos que uma variável pode aceitar:
class Button
{
private string|object $caption;
public function setCaption(string|object $caption)
{
$this->caption = $caption;
}
}
Certos tipos de sindicatos já foram introduzidos ao PHP antes. Os tipos
nulas, por exemplo. Tais como ?string
, que é equivalente ao tipo
de sindicato string|null
. A notação do ponto de interrogação
pode ser considerada uma abreviação. Claro, também funciona no PHP 8, mas
não se pode combiná-lo com barras verticais. Portanto, ao invés de
?string|object
, você tem que escrever
string|object|null
. Além disso, iterable
sempre foi
equivalente a array|Traversable
. Você pode se surpreender que
float
também seja um tipo de sindicato, pois aceita ambos
int|float
, mas dá o valor para float
.
Você não pode usar pseudotipos void
e mixed
em
sindicatos porque não faria sentido.
Nette está pronta para os tipos de união. Em Schema, Expect::from()
aceita-os, e os apresentadores também os aceitam. Você pode usá-los, por
exemplo, nos métodos de renderização e de ação:
public function renderDetail(int|array $id)
{
...
}
Por outro lado, a fiação automática em Nette DI rejeita os tipos de união. Até agora, não há nenhum caso de uso em que faria sentido para o construtor aceitar um ou outro objeto. Naturalmente, se tal caso de uso aparecer, será possível ajustar o comportamento do recipiente de acordo.
Os métodos getParameterType()
, getReturnType()
e
getPropertyType()
em Nette\Utils\Reflection
lançam
uma exceção no caso do tipo de união (isto é, na versão 3.1; na versão
anterior 3.0, estes métodos retornam para manter a compatibilidade nula).
mixed
O pseudotipo mixed
aceita qualquer valor.
No caso de parâmetros e propriedades, isso resulta no mesmo comportamento
como se não especificássemos nenhum tipo. Então, qual é a sua finalidade?
Distinguir quando falta apenas um tipo e quando este é intencionalmente
mixed
.
No caso de valores de retorno de função e método, não especificar o tipo
difere do uso do mixed
. Isso significa o oposto de
void
, pois requer a função para retornar algo. Um retorno ausente
produz então um erro fatal.
Na prática, você raramente deve usá-lo, pois graças aos tipos de sindicatos, você pode especificar o valor com precisão. Portanto, ele só é adequado em situações únicas:
function dump(mixed $var): mixed
{
// print variable
return $var;
}
false
Ao contrário de mixed
, você pode usar o novo pseudotipo
false
exclusivamente em tipos de sindicatos. Ele surgiu da
necessidade de descrever o tipo de retorno das funções nativas, que
historicamente retornam falsas em caso de falha:
function strpos(string $haystack, string $needle): int|false
{
}
Portanto, não há nenhum tipo true
. Não se pode usar somente
false
, nem false|null
, nem
bool|false
.
static
Pseudotipo static
só pode ser usado como um tipo de método de
retorno. Ele diz que o método retorna um objeto do mesmo tipo que o próprio
objeto (enquanto que self
diz que retorna a classe na qual
o método é definido). É excelente para descrever interfaces fluentes:
class Item
{
public function setValue($val): static
{
$this->value = $val;
return $this;
}
}
class ItemChild extends Item
{
public function childMethod()
{
}
}
$child = new ItemChild;
$child->setValue(10)
->childMethod();
resource
Este tipo não existe no PHP 8 e não será introduzido no futuro. Os
recursos são uma relíquia da época em que o PHP não tinha objetos.
Gradualmente, os recursos serão substituídos por objetos. Eventualmente, eles
desaparecerão completamente. Por exemplo, o PHP 8.0 substitui o recurso de
imagem pelo objeto GdImage
, e o recurso de ondulação pelo objeto
CurlHandle
.
Stringable
É uma interface que é implementada automaticamente por cada objeto com um
método mágico __toString()
.
class Email
{
public function __toString(): string
{
return $this->value;
}
}
function print(Stringable|string $s)
{
}
print('abc');
print(new Email);
É possível afirmar explicitamente
class Email implements Stringable
na definição de classe, mas
não é necessário.
Nette\Utils\Html
também reflete este esquema de nomenclatura,
implementando a interface Nette\HtmlStringable
em vez da anterior
IHtmlString
. Objetos deste tipo, por exemplo, não são escapados
por Latte.
Tipo variância, contravariância, covariância
O Princípio de Substituição Liskov (LSP) afirma que as classes de extensão e implementações de interface nunca devem exigir mais e fornecer menos do que o pai. Ou seja, o método infantil não deve exigir mais argumentos ou aceitar uma gama mais restrita de tipos para parâmetros do que o pai, e vice-versa, não deve retornar uma gama mais ampla de tipos. Mas ele pode retornar menos. Por quê? Porque, caso contrário, a herança se quebraria. Uma função aceitaria um objeto de um tipo específico, mas não teria idéia de quais parâmetros pode passar para seus métodos e que tipos retornariam. Qualquer criança poderia quebrá-lo.
Assim, no OOP, a classe infantil pode:
- aceitar uma gama mais ampla de tipos nos parâmetros (isso é chamado de contravariação)
- retornar uma gama mais restrita de tipos (covariância)
- e as propriedades não podem mudar de tipo (são invariantes)
O PHP tem sido capaz de fazer isso desde a versão 7.4, e todos os tipos recentemente introduzidos no PHP 8.0 também suportam contravariância e covariância.
No caso do mixed
, a criança pode reduzir o valor de retorno
para qualquer tipo, mas não void
, pois não representa um valor,
mas sim sua ausência. A criança também não pode omitir uma declaração de
tipo, pois isto também permite uma ausência de valor.
class A
{
public function foo(mixed $foo): mixed
{}
}
class B extends A
{
public function foo($foo): string
{}
}
Os tipos de união também podem ser estendidos em parâmetros e reduzidos em valores de retorno:
class A
{
public function foo(string|int $foo): string|int
{}
}
class B extends A
{
public function foo(string|int|float $foo): string
{}
}
Além disso, false
pode ser estendido para bool
no
parâmetro ou vice versa bool
para false
no valor de
retorno.
Todas as ofensas contra a covariância/contravariância levam a um erro fatal no PHP 8.0.
- Nas próximas partes desta série, mostraremos quais são os atributos, que novas funções e classes apareceram no PHP e apresentaremos o Compilador Just in Time.*
Para enviar um comentário, faça o login