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.

https://www.php.net/images/php8/php_8_released.png

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 voidnunca pode ser parte de um union type, pois indica "nenhum valor de retorno". Além disso, unions do tipo nullablepodem 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 syntaxAttributecostumava ser chamada PhpAttributena 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: matchpode 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, staticnã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 mixedtype 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. mixedem si significa um destes tipos:

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

Observe que mixedtambé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 mixedjá 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 reservadathrowdeixa 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 functiontambé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. WeakMapconté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 ::classem 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 catchvazio. Se quiser capturar todas as exceções e erros, você pode usar Throwablecomo 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 Stringableinterface 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, -INFou 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\Barretornaria 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 classePhpTokencom 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: Errorexception em vez de notice
  • Undefined array index: warning em vez de notice
  • Division by zero: DivisionByZeroErrorexception em vez de warning
  • Tentativa de incrementar / decrementar a propriedade ‘%s’ de um não objeto: Errorexception em vez de warning
  • Tentativa de modificar a propriedade ‘%s’ do não objeto: Errorexception em vez de warning
  • Tentativa de atribuir a propriedade ‘%s’ de não objeto: Errorexception em vez de warning
  • Criando objeto padrão a partir de valor vazio: Errorexception 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: Errorexception em vez de warning
  • Não é possível remover o deslocamento em uma variável sem matriz: Errorexception em vez de warning
  • Não é possível usar um valor escalar como array: Errorexception em vez de warning
  • Apenas arrays e Traversablespodem ser descompactados: TypeErrorexception em vez de warning
  • Argumento inválido fornecido para foreach (): TypeErrorexception em vez de warning
  • Illegal offset type: TypeErrorexception em vez de warning
  • Illegal offset type em isset ou empty: TypeErrorexception em vez de warning
  • Illegal offset type em unset: TypeErrorexception 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: Errorexception em vez de warning
  • O resource fornecido não é um stream resource válido: TypeErrorexception 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_ALLincorpora tambémE_NOTICEe 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 ReflectionTypepara 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 deReflectionNamedTypeassim:

$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.

Referência

Thanks Brent Roose!

--

--

Um eterno aprendiz que acredita fielmente que o desenvolvimento de software e pessoal são habilidades que devem ser realizadas com Paixão!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
João Roberto da Paixão

Eterno aprendiz que compartilha aquilo que aprende. Apaixonado por tecnologia, desenvolvimento pessoal e investimentos. Pai de família, um cara tranquilo =) !