O que há de novo no PHP 8
Essa é uma tradução do artigo "What’s new in PHP 8", devidamente autorizada pelo seu autor Brent Roose.
PHP 8 chegou! Foi lançado em 26 de novembro de 2020. Você pode baixá-lo aqui . É uma nova versão principal, ou seja, essa versão introduz algumas mudanças importantes, bem como muitos novos recursos e melhorias de desempenho.
Por causa das mudanças significativas, há uma chance maior de você precisar fazer algumas mudanças em seu código para executá-lo no PHP 8. Se você se manteve atualizado com os lançamentos mais recentes, a atualização não deve ser muito difícil, uma vez que a maioria das mudanças significativas foram descontinuadas antes nas versões 7. *. E não se preocupe, todas essas depreciações estão listadas nesta postagem.
Além das mudanças significativas, o PHP 8 também traz um bom conjunto de novos recursos, como o compilador JIT , union types , attributes e muito mais.
Novas funcionalidades
Vamos começar com todos os novos recursos, é uma lista e tanto!
Union Types — (rfc)
Dada a natureza de tipagem dinâmica do PHP, existem muitos casos em que os union types podem ser úteis. Os union types são uma coleção de dois ou mais tipos que indicam que qualquer um deles pode ser usado.
public function foo(Foo|Bar $input): int|float;
Observe que void
nunca pode ser parte de um union type, pois indica "nenhum valor de retorno". Além disso, unions do tipo nullable
podem ser escritas usando |null
, ou usando a notação?
já existente :
public function foo(Foo|null $foo): void; public function bar(?Bar $bar): void;
JIT — (rfc)
O compilador JIT — just in time — promete melhorias significativas de desempenho, embora nem sempre dentro do contexto de solicitações da web. Fiz meus próprios benchmarks em aplicativos da web da vida real e parece que o JIT não faz muita diferença, se houver, nesses tipos de projetos PHP.
Se você quiser saber mais sobre o que o JIT pode fazer pelo PHP, pode ler outro post que escrevi sobre ele aqui .
Nullsafe operator— (rfc)
Se você está familiarizado com o coalescing operator , já está ciente das suas deficiências: ele não funciona em chamadas de método. Em vez disso, você precisa de verificações intermediárias ou depende de um helper optional
fornecido por algum framework:
$startDate = $booking->getStartDate(); $dateAsString = $startDate ? $startDate->asDateTimeString() : null;
Com a adição do nullsafe operator, agora podemos ter um comportamento semelhante ao operador null coalescing nos métodos!
$dateAsString = $booking->getStartDate()?->asDateTimeString();
Você pode ler tudo sobre o nullsafe operator aqui .
Argumentos nomeados — (rfc)
Argumentos nomeados permitem que você passe valores para uma função, especificando o nome do valor, para que você não tenha que levar sua ordem em consideração e você também pode pular parâmetros opcionais!
function foo(string $a, string $b, ?string $c = null, ?string $d = null) { /* … */ } foo(
b: 'value b',
a: 'value a',
d: 'value d',
);
Você pode ler mais sobre eles neste post .
Attributes — (rfc)
Attributes , comumente conhecidos como annotation em outras linguagens, oferecem uma maneira de adicionar metadados às classes, sem ter que analisar docBlocks.
Para uma rápida espiada, aqui está um exemplo do RFC de como os atributos se parecem:
use App\Attributes\ExampleAttribute; #[ExampleAttribute]
class Foo
{
#[ExampleAttribute]
public const FOO = 'foo'; #[ExampleAttribute]
public $x; #[ExampleAttribute]
public function foo(#[ExampleAttribute] $bar) { }
}#[Attribute]
class ExampleAttribute
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}
Observe que a syntaxAttribute
costumava ser chamada PhpAttribute
na RFC original, mas foi alterada posteriormente por outra RFC . Se você quiser se aprofundar em como funcionam os atributos e como pode construir os seus próprios; você pode ler sobre os atributos em profundidade neste blog.
Match expression — (rfc)
Você poderia chamá-lo de irmão mais velho da expressão switch
: match
pode retornar valores, não requer declarações break
, pode combinar condições, usa comparações de tipo estritas e não faz nenhuma coerção de tipo.
Se parece com isso:
$result = match($input) {
0 => "hello",
'1', '2', '3' => "world",
};
Você pode ler sobre a expresãomatch
em detalhes aqui .
Constructor property promotion — (rfc)
Este RFC adiciona syntactic sugar para criar objetos de valor ou objetos de transferência de dados. Em vez de especificar propriedades de classe e um construtor para elas, o PHP agora pode combiná-los em um.
Em vez de fazer isso:
class Money
{
public Currency $currency; public int $amount; public function __construct(
Currency $currency,
int $amount,
) {
$this->currency = $currency;
$this->amount = $amount;
}
}
Agora você pode fazer isso:
class Money
{
public function __construct(
public Currency $currency,
public int $amount,
) {}
}
Há muito mais coisas sobre property promotion, você pode ler mais neste post dedicado .
New static
return type — (rfc)
Embora já fosse possível retornar self
, static
não era um tipo de retorno válido até o PHP 8. Dada a natureza de tipagem dinâmica do PHP, é um recurso que será útil para muitos desenvolvedores.
class Foo
{
public function test(): static
{
return new static();
}
}
New mixed
type — (rfc)
Alguns podem chamá-lo de um mal necessário: o mixed
type faz com que muitos tenham sentimentos confusos. No entanto, há um bom argumento a ser defendido: um tipo mixed
pode significar muitas coisas em PHP:
- Uma função não retorna nada ou nulo
- Estamos esperando um de vários tipos
- Estamos esperando um tipo que não pode ser sugerido em PHP
Pelas razões acima, é bom que o mixed type
seja adicionado. mixed
em si significa um destes tipos:
array
bool
callable
int
float
null
object
resource
string
Observe que mixed
também pode ser usado como um parâmetro ou tipo de propriedade, não apenas como um tipo de retorno.
Observe também que, como mixed
já inclui null
, não é permitido torná-lo anulável. O seguinte irá desencadear um erro:
// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type. function bar(): ?mixed {}
Throw expression — (rfc)
Nesse RFC a palavra reservadathrow
deixa de ser uma declaração para ser uma expressão, o que torna possível lançar uma exceção em muitos novos lugares:
$triggerError = fn () => throw new MyError(); $foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');
Herança com métodos privados — (rfc)
Anteriormente, o PHP costumava aplicar as mesmas verificações de herança em métodos públicos, protegidos e privados. Em outras palavras: os métodos privados devem seguir as mesmas regras de assinatura de método dos métodos protegidos e públicos. Isso não faz sentido, uma vez que os métodos privados não serão acessíveis por classes filhas.
Essa RFC mudou esse comportamento, de forma que essas verificações de herança não sejam mais realizadas em métodos privados. Além disso, o uso de final private function
também não fazia sentido, então fazer isso agora irá disparar um aviso:
Warning: Private methods cannot be final as they are never overridden by other classes
Weak maps — (rfc)
Construída sobre a weakrefs RFC que foi adicionada no PHP 7.4, a implementaçãoWeakMap
foi adicionada no PHP 8. WeakMap
contém referências a objetos, o que não impede que esses objetos sejam coletados como lixo
Tome o exemplo dos ORMs, eles geralmente implementam caches que contêm referências a classes de entidade para melhorar o desempenho das relações entre as entidades. Esses objetos de entidade não podem ser coletados como lixo, desde que esse cache tenha uma referência a eles, mesmo que o cache seja a única coisa que os faz referência.
Se esta camada de cache usar referências e mapas fracos, o PHP irá coletar como lixo esses objetos quando nada mais fizer referência a eles. Especialmente no caso de ORMs, que podem gerenciar várias centenas, senão milhares de entidades em uma solicitação; weak maps podem oferecer uma maneira melhor e mais amigável de lidar com esses objetos.
Aqui está a aparência de weak maps, um exemplo da RFC:
class Foo
{
private WeakMap $cache;
public function getSomethingWithCaching(object $obj): object
{
return $this->cache[$obj]
??= $this->computeSomethingExpensive($obj);
}
}
Permitindo ::class
em objetos — (rfc)
Um novo e pequeno recurso, mas útil: agora é possível usar ::class
em objetos, ao invés de ter que usar get_class()
neles. Funciona da mesma forma que get_class()
.
$foo = new Foo(); var_dump($foo::class);
Non-capturing catches — (rfc)
Sempre que você quisesse capturar uma exceção antes do PHP 8, você tinha que armazená-la em uma variável, independentemente de você ter usado essa variável ou não. Com non-capturing catches, você pode omitir a variável, então, em vez disso:
try {
// Something goes wrong
} catch (MySpecialException $exception) {
Log::error("Something went wrong");
}
Você pode fazer isso:
try {
// Something goes wrong
} catch (MySpecialException) {
Log::error("Something went wrong");
}
Observe que é obrigatório sempre especificar o tipo, você não tem permissão para ter um catch
vazio. Se quiser capturar todas as exceções e erros, você pode usar Throwable
como tipo de captura.
Vírgula no final das listas de parâmetros — (rfc)
Já era possível ao chamar uma função; o suporte à vírgula no final ainda estava faltando nas listas de parâmetros, isso agora é permitido no PHP 8, o que significa que você pode fazer o seguinte:
public function(
string $parameterA,
int $parameterB,
Foo $objectfoo,
) {
// …
}
Nota: as vírgulas no final também são suportadas na expresão use
(das clousures), isso foi um descuido e agora adicionado por meio de um RFC separado .
Criar objetos DateTime
da interface
Você já pode criar um objetoDateTime
a partir de um objeto usandoDateTimeImmutable
, mas o contrário era complicado. Agora há uma maneira generalizada para converter esses objetos uns aos outros. DateTime::createFromImmutable($immutableDateTime) DateTime::createFromInterface() DatetimeImmutable::createFromInterface() DateTimeDateTimeImmutable
DateTime::createFromInterface(DateTimeInterface $other);DateTimeImmutable::createFromInterface(DateTimeInterface $other);
Nova interface Stringable — (
rfc)
A Stringable
interface pode ser usada para adicionar hint a qualquer coisa que implemente __toString()
. Sempre que uma classe implementa __toString()
, ela implementa automaticamente a interface nos bastidores e não há necessidade de implementá-la manualmente.
class Foo
{
public function __toString(): string
{
return 'foo';
}
} function bar(string|Stringable $stringable) { /* … */ } bar(new Foo());
bar('abc');
Nova função str_contains() — (
rfc)
Alguns podem dizer que isso está muito atrasado, mas finalmente não precisamos mais depender de strpos()
para saber se uma string contém outra string.
Em vez de fazer isso:
if (strpos('string with lots of words', 'words') !== false) { }
Agora você pode fazer isso:
if (str_contains('string with lots of words', 'words')) { }
Novos funções str_starts_with()
e str_ends_with()— (
rfc)
Duas outras funções muito atrasadas… Essas duas funções agora foram adicionadas ao core:
str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true
Nova função fdiv()
— (pr)
A nova funçãofdiv()
faz algo semelhante às funções fmod()
e intdiv()
, o que permite a divisão por 0. Em vez de erros, você obterá INF
, -INF
ou NAN
, dependendo do caso.
Nova função get_debug_type() — (
rfc)
get_debug_type()
retorna o tipo de uma variável. Parece que algo gettype()
faria? get_debug_type()
retorna uma saída mais útil para matrizes, strings, classes anônimas e objetos.
Por exemplo, chamar gettype()
numa classe \Foo\Bar
retornaria object
. Usar get_debug_type()
retornará o nome da classe.
Uma lista completa das diferenças entre get_debug_type()
e gettype()
pode ser encontrada no RFC.
Nova função get_resource_id()
— (pr)
Resource são variáveis especiais em PHP, referindo-se a recursos externos. Um exemplo é uma conexão MySQL, outro um identificador de arquivo.
Cada um desses resources recebe um ID, embora anteriormente a única maneira de saber esse id fosse fazer o cast do resource para int
:
$resourceId = (int) $resource;
PHP 8 adicionou a funçãoget_resource_id()
, tornando esta operação de tipo mais óbvia e segura:
$resourceId = get_resource_id($resource);
Melhorias em métodos abstratos de traits — (rfc)
Traits podem especificar métodos abstratos que devem ser implementados pelas classes que os utilizam. Porém, há uma ressalva: antes do PHP 8, a assinatura dessas implementações de método não era validada. O seguinte era válido:
trait Test
{
abstract public function test(int $input): int;
} class UsesTrait
{
use Test; public function test($input)
{
return $input;
}
}
O PHP 8 realizará validação de assinatura de método apropriada ao usar uma característica e implementar seus métodos abstratos. Isso significa que você precisará escrever isto:
class UsesTrait
{
use Test; public function test(int $input): int
{
return $input;
}
}
token_get_all() implementação de objeto — (rfc)
A funçãotoken_get_all()
retorna um array de valores. Este RFC adiciona uma classePhpToken
com um método. Esta implementação funciona com objetos em vez de valores simples. Ele consome menos memória e é mais fácil de ler.PhpToken::tokenize()
.
Variable syntax tweaks — (rfc)
Do RFC: “o RFC de Uniform Variable Syntax resolveu uma série de inconsistências na sintaxe de variável do PHP. Este RFC pretende abordar um pequeno de casos que foram negligenciados.”
Type annotations para funções internas—( externals)
Muitas pessoas contribuíram para adicionar type annotations adequados para todas as funções internas. Este era um problema antigo e finalmente resolvido com todas as mudanças feitas no PHP nas versões anteriores. Isso significa que funções e métodos internos terão informações de tipo completas na api de reflexão (reflection).
ext-json
sempre disponível — (rfc)
Anteriormente, era possível compilar o PHP sem a extensão JSON habilitada, isso não é mais possível. Como o JSON é amplamente usado, é melhor que os desenvolvedores sempre possam confiar que ele esteja lá, em vez de ter que garantir que a extensão exista primeiro.
Breaking changes
Como mencionado antes: esta é uma atualização importante e, portanto, haverá mudanças significativas. A melhor coisa a fazer é dar uma olhada na lista completa de mudanças importantes no documento UPGRADING .
Muitas dessas mudanças significativas foram descontinuadas nas versões 7. * anteriores, então, se você tem se mantido atualizado ao longo dos anos, não deve ser tão difícil atualizar para o PHP 8.
Erros de tipo consistente — (rfc)
As funções definidas pelo usuário no PHP irão lançar TypeError
, mas as funções internas não, elas emitem avisos e retornam null
. A partir do PHP 8, o comportamento das funções internas tornou-se consistente.
Reclassificação: engine warnings — (rfc)
Muitos erros que antes acionavam apenas warnings ou notices foram convertidos em erros adequados. Os seguintes avisos foram alterados.
- Undefined variable:
Error
exception em vez de notice - Undefined array index: warning em vez de notice
- Division by zero:
DivisionByZeroError
exception em vez de warning - Tentativa de incrementar / decrementar a propriedade ‘%s’ de um não objeto:
Error
exception em vez de warning - Tentativa de modificar a propriedade ‘%s’ do não objeto:
Error
exception em vez de warning - Tentativa de atribuir a propriedade ‘%s’ de não objeto:
Error
exception em vez de warning - Criando objeto padrão a partir de valor vazio:
Error
exception em vez de warning - Tentando obter a propriedade ‘%s’ de um não objeto: warning em vez de notice
- Propriedade indefinida:% s :: $% s: warning em vez de notice
- Não é possível adicionar elemento a um array porque o próximo elemento já está ocupado:
Error
exception em vez de warning - Não é possível remover o deslocamento em uma variável sem matriz:
Error
exception em vez de warning - Não é possível usar um valor escalar como array:
Error
exception em vez de warning - Apenas arrays e
Traversables
podem ser descompactados:TypeError
exception em vez de warning - Argumento inválido fornecido para foreach ():
TypeError
exception em vez de warning - Illegal offset type:
TypeError
exception em vez de warning - Illegal offset type em isset ou empty:
TypeError
exception em vez de warning - Illegal offset type em unset:
TypeError
exception em vez de warning - Conversão de array em string: warning em vez de notice
- Resource ID#%d usado como offset, fazendo cast para integer (%d): warning em vez de notice
- String offset cast occurred: warning em vez de notice
- Uninitialized string offset %d: warning em vez de notice
- Não é possível atribuir uma string vazia a um string offset:
Error
exception em vez de warning - O resource fornecido não é um stream resource válido:
TypeError
exception em vez de warning
O operador @ não silencia mais os erros fatais
É possível que essa mudança revele erros que novamente estavam ocultos antes do PHP 8. Certifique-se de configurá-los em seus servidores de produção!display_errors=Off
Default error reporting level
Agora E_ALL
incorpora tambémE_NOTICE
e E_DEPRECATED
. Isso significa que muitos erros podem aparecer, os quais foram silenciosamente ignorados, embora provavelmente já existissem antes do PHP 8.
Modo de erro padrão do PDO — (rfc)
Do RFC: "O modo de erro padrão atual para PDO é silencioso. Isso significa que, quando ocorre um erro de SQL, nenhum erro ou aviso pode ser emitido e nenhuma exceção lançada, a menos que o desenvolvedor implemente seu próprio tratamento de erro explícito."
Este RFC muda o erro padrão no PHP 8.PDO::ERRMODE_EXCEPTION
Precedência de concatenação—( rfc)
Embora já obsoleto no PHP 7.4, esta mudança agora entrou em vigor. Se você escrever algo assim:
echo "sum: " . $a + $b;
O PHP o interpretaria anteriormente assim:
echo ("sum: " . $a) + $b;
PHP 8 fará com que seja interpretado assim:
echo "sum: " . ($a + $b);
Verificações de tipo mais rígidas para operadores aritméticos e bit a bit — (rfc)
Antes do PHP 8, era possível aplicar operadores aritméticos ou bit a bit em arrays, resources ou objetos. Isso não é mais possível e vai lançar um TypeError
:
[] % [42];
$object + 4;
Nomes reservados agora podem ser usados em namespaces — (rfc)
PHP usava uma sequência de tokens para interpretar cada parte de um namespace (separado por uma barra invertida \
). Este RFC mudou esse comportamento, o que significa que nomes reservados agora podem ser usados em namespaces.
Strings numéricas mais sãs —(rfc)
O sistema de tipos do PHP tenta fazer muitas coisas inteligentes quando encontra números em strings. Este RFC torna esse comportamento mais consistente e claro.
Reflection changes
Alguns reflection methods foram descontinuados:
ReflectionFunction::isDisabled()
ReflectionParameter::getClass()
ReflectionParameter::isCallable()
Agora você deve usar ReflectionType
para obter informações sobre o tipo de um parâmetro:
$reflectionParameter->getType()->allowsNull();
Se o tipo for um único tipo, ReflectionParameter::getType()
retorna uma instância de ReflectionNamedType
, do qual você pode obter seu nome e se ele está integrado:
$reflectionParameter->getType()->getName();
$reflectionParameter->getType()->isBuiltin();
Se o tipo for um union type, no entanto, você obterá uma instância de ReflectionUnionType
, que pode fornecer uma matriz deReflectionNamedType
assim:
$reflectionParameter->getType()->getTypes();
Verificar se um tipo é uma union type ou não pode ser feito com um instanceof
:
if ($reflectionParameter->getType() instanceof ReflectionNamedType) {
// It's a single type
}
if ($reflectionParameter->getType() instanceof ReflectionUnionType) {
// It's a union type
}
A seguir, veja três assinaturas de métodos da classe reflection que foram alteradas:
ReflectionClass::newInstance($args);
ReflectionFunction::invoke($args);
ReflectionMethod::invoke($object, $args);
Tornaram-se:
ReflectionClass::newInstance(...$args);
ReflectionFunction::invoke(...$args);
ReflectionMethod::invoke($object, ...$args);
O guia de atualização especifica que se você estender essas classes e ainda quiser oferecer suporte a PHP 7 e PHP 8, as seguintes assinaturas são permitidas:
ReflectionClass::newInstance($arg = null, ...$args);
ReflectionFunction::invoke($arg = null, ...$args);
ReflectionMethod::invoke($object, $arg = null, ...$args);
Algoritmos de classificação estáveis — (rfc)
Antes do PHP 8, os algoritmos de classificação eram instáveis. Isso significa que a ordem dos elementos iguais não foi garantida. O PHP 8 muda o comportamento de todas as funções de classificação para classificação estável.
Erro fatal para assinaturas de método incompatíveis — (rfc)
Do RFC: "Erros de herança devido a assinaturas de método incompatíveis atualmente geram um erro fatal ou um aviso, dependendo da causa do erro e da hierarquia de herança."
Outras depreciações e mudanças
Durante o desenvolvimento do PHP 7. *, várias depreciações foram adicionadas, agora finalizadas no PHP 8.
- Depreciações no PHP 7.2
- Depreciações no PHP 7.3
- Depreciações em PHP 7.4
- float to string cast
Referência
Thanks Brent Roose!