Novidades do PHP 8.3: O que há de novo nessa versão do PHP

A versão final do PHP 8.3 está agendada para ser lançada no dia 23 de novembro de 2023 e traz algumas novidades bem interessantes, contando com novas funções, novas classes e código que passa a ser considerado obsoleto.

Modificações sobre readonly

Uma RFC intitulada Readonly amendments propõe duas mudanças em como a palavra-chave readonly se comporta, porém apenas uma dessas mudanças foi aceita. A partir da versão 8.3 do PHP, é possível reinicializar propriedades readonly ao clonar objetos, ou seja, dentro do método mágico __clone.

Isso torna o seguinte código, que no PHP 8.2 causaria um erro, possível:

<?php

readonly class Usuario
{
    public function __construct(
        public \DateTimeImmutable $nascimento, 
    ) {}

    public function __clone() : void
    {
        $this->nascimento = clone $this->nascimento;
    }
}

Essa funcionalidade, embora pareça pequena, permite que realizemos deep copy de objetos que possuam propriedades readonly.

Se quiser ver mais sobre essa nova funcionalidade, eu tenho um vídeo sobre ela em meu canal do YouTube:

Novidades do PHP 8.3: Modificações sobre readonly | Dias de Dev

Constantes tipadas em classes

A partir do PHP 8.3 é possível definirmos os tipos de constantes em nossas classes. Essa possibilidade nos traz mais segurança de tipos principalmente quando há herança entre classes ou implementação de interfaces que possuem constantes. Até o PHP 8.2, uma classe poderia sobrescrever uma constante herdada adicionando qualquer valor, sem checagem de tipo. Isso poderia trazer problemas como o seguinte:

interface InterfaceTeste {
    const TEST = "Test";
}

class ClasseBase implements InterfaceTeste {
    const TEST = []; // Mudamos o tipo aqui
}

class ClasseDerivada extends ClasseBase {
    const TEST = null; // Mais uma vez, outro tipo
}

Ao acessar a constante TEST de algum objeto do tipo InterfaceTeste, não havia nenhuma garantia que seu tipo sempre seria string. Com o PHP 8.3, o seguinte é possível:

interface InterfaceTeste {
    const string TEST = "Test";
}

class ClasseBase implements InterfaceTeste {
    const array TEST = []; // Isso falharia, pois muda o tipo da constante
}

Retorno nulo em FFI

Ao chamar uma função que não retorna nada, ou seja, void, via FFI, o PHP retornava um tipo específico chamado FFI\CData com o valor void. Agora, assim como em uma função PHP com o retorno void, nós receberemos null dessa função. Exemplo:

$ffi = FFI::cdef(
    "void free(void *ptr);",
    "libc.so.6"
);
// Até o PHP 8.2, isso retornaria `FFI\CData`, agora retorna `null`
$ffi->free(/* ponteiro*/);

Atributo #[\Override] para sobrescrita de métodos

Um novo atributo chega ao PHP 8.3. O atributo #[Override] permite que o código deixe claro que o método anotado com ele está sobrescrevendo um método da classe base, ou seja, caso haja alguma inconsistência na herança (como nome do método escrito errado) um erro de compilação será exibido.

Isso permite encontrar erros como o seguinte:

<?php
class Conta
{
    public function saca(int $valor): void { /* implementação */ }
}

class ContaCorrente
{
    public function sacar(int $valor): void { /* implementação modificada */ }
}

O código acima possui um erro sutil no nome do método. Ao chamar (new ContaCorrente())->saca($valor), embora a intenção seja chamar o método da classe ContaCorrente que sobrescreve o método da classe Conta, nós na verdade estamos chamando o método da classe Conta mesmo. Esse novo atributo nos permite descobrir esse erro com mais facilidade, exibindo uma mensagem de erro bastante clara:

<?php
class Conta
{
    public function saca(int $valor): void { /* implementação */ }
}

class ContaCorrente
{
    #[Override]
    public function sacar(int $valor): void { /* implementação modificada */ }
}

Isso geraria o seguinte erro:

Fatal error: ContaCorrente::sacar() has #[\Override] attribute, but no matching parent method exists

Essa é mais uma novidade do PHP 8.3 sobre a qual eu gravei um vídeo específico em meu canal do YouTube. Aqui você pode conferí-lo:

Novidades do PHP 8.3 - Novo atributo (Override) para sobrescrita de métodos | Dias de Dev

Exceções de DateTime mais adequadas e específicas

Algumas novas classes de exceção são introduzidas no PHP 8.3. Se tratando de DateTime, essas são as novas exceções:

  • DateException
    • DateInvalidOperationException
    • DateInvalidTimezoneException
    • DateMalformedIntervalStringException
    • DateMalformedPeriodStringException
    • DateMalformedStringException
  • DateError
    • DateObjectError
    • DateRangeError

Com isso, algumas exceções lançadas anteriormente ao instanciar objetos do tipo DateTime, DateTimeImmutable, DateInterval e DatePeriod podem ser diferentes agora. Um exemplo:

// Até o PHP 8.2, uma exceção do tipo `Exception` seria lançada
// A partir do 8.3, `DateMalformedStringException` será lançada
new DateTime('data inválida');

Essa é uma RFC simples, mas com um grande número de modificações. Se quiser saber mais sobre cada caso que pode lançar uma das novas exceções, você pode conferir a página da RFC.

Nova função json_validate

Em alguns cenários nós precisamos garantir que uma string é um json válido, mas não necessariamente precisamos realizar a transformação dessa string em um objeto ou array em PHP. Se nós precisamos armazenar no banco de dados um json recebido na requisição, por exemplo, não há necessidade de realizar todo o trabalho de transformar esse json com a função json_decode.

Porém, até o PHP 8.2, a única forma de descobrirmos se uma string é um json válido em PHP era através da função json_decode. Esse desperdício de recursos foi resolvido no PHP 8.3 com a chegada da função json_validate.

Essa nova função garante que o json é válido, mas sem precisar alocar memória para salvar seu conteúdo transformado já que ela simplesmente retorna um booleano.

Eu gravei um vídeo curto no YouTube (no formato de shorts) falando sobre essa nova função. Você pode conferir aqui:

Novidades do PHP 8.3: Nova função json_validate | Dias de Dev #shorts

Comando php -l agora suporta múltiplos arquivos

Um comando muito útil é o php -l que permite verificar se um arquivo possui algum erro de sintaxe. Até a versão 8.2 do PHP apenas um arquivo podia ser analisado por vez. Agora, a partir do PHP 8.3, nós podemos passar diversos arquivos para o php -l analisar. Exemplo:

php -l arquivo1.php arquivo2.php

O comando exibirá, caso nenhum dos arquivos possua erros de sintaxe:

No syntax errors detected in arquivo1.php

No syntax errors detected in arquivo2.php

Funções array_sum e array_product mais consistentes

As funções array_sum e array_product possuiam um comportamento menos previsível quando valores não numéricos eram encontrados nos arrays a serem processados. Essa novidade traz uma maior consistência e previsibilidade para seu comportamento.

Por exemplo:

$input = [true, STDERR, new stdClass(), []];
var_dump(array_sum($input));
var_dump(array_product($input));

Tanto até o PHP 8.2 quanto no PHP 8.3, os resultados serão 4 e 3, porém no PHP 8.3 diversos alertas serão exibidos informando que não é possível realizar operações matemáticas em resource, em stdClass e em array (true vira 1 em operações matemáticas no PHP e isso não muda nessa versão).

Isso faz com que nós sejamos notificados pela aplicação caso operações matemáticas estejam sendo realizadas onde não deveriam.

Há mais pontos de melhoria trazidos por essa RFC e por isso eu também tenho um vídeo completo sobre ela:

Novidades do PHP 8.3 - Funções array_sum e array_product mais consistentes | Dias de Dev

Mais novidades

Você já deve ter reparado que esse post começou a ficar bem grande, não é mesmo? O PHP 8.3 realmente está trazendo algumas novidades bem interessantes e nesse post eu quis destacar algumas delas.

Mas se você quiser saber sobre todas as novidades que o PHP 8.3 traz, pode conferir a página oficial das RFCs ou conferir a seguinte lista:

E se você quiser aprender mais sobre PHP e suas funcionalidades, vou deixar aqui um cupom de desconto para a Alura, onde eu tenho diversos cursos sobre PHP, dentre outros assuntos.