O PHP 8.3 foi lançado no prazo previsto, em 23 de novembro, e inclui muitos recursos novos e melhorias desde o lançamento do PHP 8.2. Embora seja oficialmente considerada uma versão secundária, algumas das mudanças na versão 8.3 podem afetar diretamente seu trabalho com PHP, inclusive ajudando você a codificar mais rapidamente e com menos bugs.

Vamos nos aprofundar e analisar as grandes — ou não tão grandes — mudanças que vêm com essa última versão.

Novos recursos e melhorias no PHP 8.3

Vamos começar explorando os recursos do PHP 8.3 que mais chamam a atenção.

Constantes de classe tipadas

A capacidade de declarar tipos para propriedades de classes tem estado disponível desde o PHP 7.4. No entanto, apesar de vários ajustes na tipagem do PHP ao longo dos anos, isso não se estendia às constantes — até agora.

Constantes de classes — e isso também inclui constantes de interfaces, traits e enums — podem ser tipadas no PHP 8.3, tornando menos provável que os desenvolvedores se desviem da intenção por trás da declaração inicial de uma constante.

Aqui está um exemplo básico usando uma interface:

// Legal:
interface ConstTest {
    // Declared type and value are both strings
    const string VERSION = "PHP 8.3";
}

// Illegal:
interface ConstTest {
    // Type and value mismatch in this initial declaration
    const float VERSION = "PHP 8.3";
}

O verdadeiro valor dessas constantes de classe tipadas é revelado quando trabalhamos em classes derivadas das declarações base. Embora uma classe filha possa frequentemente atribuir um novo valor a uma constante, o PHP 8.3 pode ajudar a prevenir alterações acidentais no seu tipo de modo que ela se torne incompatível com a declaração inicial:

class ConstTest {
    const string VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // It's OK to change the value of VERSION here
    const string VERSION = "PHP 8.3";

    // Illegal:
    // Type must be declared if it was specified in the base class
    const VERSION = "PHP 8.3";

    // Illegal:
    // In this case, we can't change the type declared in the 
    // base class, even if the new type and its value are compatible.
    const float VERSION = 8.3;
}

Lembre-se de que o tipo atribuído a uma constante de classe pode variar ao “restringir” múltiplos tipos ou ao usar um tipo de outra forma compatível:

class ConstTest {
    const string|float VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // Here, it's OK to narrow the type declaration to string or float
    const string VERSION = "PHP 8.3";
    const float VERSION = 8.3;

    // Legal:
    // Value could be an int, but it's compatible with float 
    const float VERSION = 8;

    // Illegal:
    // We can't widen the type options here to include int
    const string|float|int VERSION = 8;
}

Dois tipos suportados para outras propriedades ao validar valores de retorno — void e never — não são suportados como tipos de constantes de classe.

Uma nova função json_validate()

Ao trabalhar com dados codificados em JSON, é melhor saber se o payload é sintaticamente válido antes de tentar fazer algo com ele.

Em versões anteriores do PHP, os desenvolvedores usavam a função json_decode() e verificavam se havia erros quando essa função tentava transformar dados JSON em arrays associativos ou objetos. A nova função json_validate() do PHP 8.3 faz a verificação de erros sem usar toda a memória necessária para criar essas estruturas de arrays ou objetos.

Portanto, no passado, você poderia validar um payload JSON mais ou menos assim:

$obj = json_decode($maybeJSON);

if (json_last_error() === JSON_ERROR_NONE) {
    // Probably do something with $obj   
}

Se você não for fazer algo imediatamente com $obj no exemplo acima, são muitos recursos usados apenas para confirmar a validade do payload JSON original. No PHP 8.3, você pode poupar um pouco de memória fazendo algo assim:

if (json_validate($maybeJSON)) {
    // Do something with $maybeJSON   
}

Observação: Não faz muito sentido usar json_validate() e, em seguida, executar imediatamente os dados em json_decode(), ainda usando os recursos de memória do decode. Provavelmente você irá preferir usar a nova função para validar o JSON antes de armazená-lo em algum lugar ou entregá-lo como resposta a uma solicitação.

Clonagem profunda de propriedades readonly

A capacidade de declarar propriedades de classe individuais como readonly apareceu no PHP 8.1. O PHP 8.2 introduziu a capacidade de designar esse atributo a uma classe inteira. No entanto, muitos desenvolvedores sentiram que as restrições impostas ao trabalhar com classes que continham tais propriedades dificultavam realizar uma programação mais útil.

Uma RFC para modificar o comportamento do readonly fez duas propostas:

  1. Permitir que classes que não são readonly estendam classes que são
  2. Permitir que propriedades readonly sejam reinicializadas ao clonar

A segunda proposta foi incluída no PHP 8.3. A nova abordagem permite que instâncias de uma classe com propriedades readonly sejam reinicializadas no método mágico __clone (inclusive por meio de funções invocadas desde __clone).

Este exemplo de código da RFC mostra como isso funciona:

class Foo {
    public function __construct(
        public readonly DateTime $bar,
        public readonly DateTime $baz
    ) {}
 
    public function __clone() {
        // $bar will get a new DateTime when clone is invoked
        $this->bar = clone $this->bar; 

        // And this function will be called
        $this->cloneBaz();
    }
 
    private function cloneBaz() {
       // This is legal when called from within __clone
        unset($this->baz); 
    }
}
 
$foo = new Foo(new DateTime(), new DateTime());
$foo2 = clone $foo;

Novo atributo #[\Override]

Ao implementar interfaces no PHP, os programadores fornecem funcionalidades detalhadas para os métodos nomeados nessas interfaces. Ao criar uma instância de uma classe, os programadores podem substituir um método principal criando uma versão alternativa com o mesmo nome e uma assinatura compatível na classe filha.

Um problema é que os programadores podem pensar que estão implementando um método de interface ou substituindo um método principal quando não estão. Eles podem estar criando uma entidade totalmente separada devido a um erro de digitação no nome do método da classe filha ou porque os métodos foram removidos ou renomeados no código principal.

O PHP 8.3 introduziu o atributo #[\Override] para ajudar os programadores a deixar claro que um método deve ter alguma linhagem dentro do código.

Aqui está um exemplo básico:

class A {
    protected function ovrTest(): void {}
}

// This will work because ovrTest() 
// can be found in the parent class
class B extends A {
    #[\Override]
    public function ovrTest(): void {}
}

// This will fail because ovrBest() 
// (probably a typo) is not in the parent
class C extends A {
    #[\Override]
    public function ovrBest(): void {}
}

Busca dinâmica de constantes de classe e membros de Enum

Diferentemente de outras propriedades no código PHP, a busca de constantes de classe e membros de Enum com nomes de variáveis tem sido um pouco complicada. Antes do PHP 8.3, você talvez fizesse isso usando a função constant() desta forma:

class MyClass {
    public const THE_CONST = 9;
}

enum MyEnum int {
    case FirstMember = 9;
    case SecondMember = 9;
}

$constantName = 'THE_CONST';
$memberName = 'FirstMember';

echo constant('MyClass::' . $constantName);
echo constant('MyEnum::' . $memberName)->value;

Agora, usando as mesmas definições de classe e de Enum acima, você pode obter o mesmo resultado no PHP 8.3 com a busca dinâmica de constantes desta forma:

$constantName = 'THE_CONST';
$memberName = 'FirstMember';

echo MyClass::{$constantName};
echo MyEnum::{$memberName}->value;

Novo método getBytesFromString()

Você já quis gerar strings aleatórias usando uma coleção pré-aprovada de caracteres? Agora você pode fazer isso facilmente com o método getBytesFromString(), que foi adicionado à extensão Random do PHP 8.3.

Esse novo método é simples: você passa a ele uma string de caracteres como material de origem e especifica quantos deles deseja usar. Em seguida, o método selecionará bytes da string aleatoriamente até atingir o comprimento especificado.

Um exemplo simples:

$rando = new Random\Randomizer();
$alpha = 'ABCDEFGHJKMNPQRSTVWXYZ';

$rando->getBytesFromString($alpha, 6); //  "MBXGWL"
$rando->getBytesFromString($alpha, 6); //  "LESPMG"
$rando->getBytesFromString($alpha, 6); //  "NVHWXC"

É possível que o comprimento solicitado da saída aleatória tenha mais bytes do que a string de entrada:

$rando = new Random\Randomizer();
$nums = '123456';

$rando->getBytesFromString($nums, 10); //  "2526341615"

Com uma string de entrada de caracteres exclusivos, cada um tem a mesma chance de ser selecionado para o resultado aleatório. No entanto, os caracteres podem ser ponderados se aparecerem com mais frequência do que outros na entrada. Por exemplo:

$rando = new Random\Randomizer();
$weighted = 'AAAAA12345';

$rando->getBytesFromString($weighted, 5); //  "1AA53"
$rando->getBytesFromString($weighted, 10); //  "42A5A1AA3A"

Novos métodos getFloat() e nextFloat()

Também expandindo a extensão Random, o PHP 8.3 apresenta dois novos métodos para gerar valores aleatórios de float: getFloat() e nextFloat().

Por exemplo:

$rando = new Random\Randomizer();

// Generate a float value between a minimum 
//  value of 0 and a maximum value of 5
$rando->getFloat(0,5); // 2.3937446906217

O método getFloat() também aceita um terceiro parâmetro após os valores mínimo e máximo. Usando um Enum Random\IntervalBoundary, você pode determinar se os próprios valores mínimo e máximo podem ser retornados pela função.

Estas são as regras:

  • IntervalBoundary::ClosedOpen: o mínimo pode ser retornado, mas não o máximo
  • IntervalBoundary::ClosedClosed: tanto o mínimo quanto o máximo podem ser retornados
  • IntervalBoundary::OpenClosed: o mínimo não pode ser retornado, mas o máximo pode
  • IntervalBoundary::OpenOpen: nem o mínimo nem o máximo podem ser retornados

Ao usar getFloat() sem especificar o Enum como terceiro parâmetro, o padrão é IntervalBoundary::ClosedOpen.

Um exemplo útil fornecido pela documentação da nova função gera coordenadas aleatórias de longitude e latitude, em que as latitudes podem incluir -90 e 90, mas a longitude não pode incluir -180 e 180 (pois são a mesma):

$rando = new Random\Randomizer();

printf(
    "Lat: %+.6f Long: %+.6f",
    $rando->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed),

    // -180 will not be used 
    $rando->getFloat(-180, 180, \Random\IntervalBoundary::OpenClosed),
);

O novo método nextFloat() é essencialmente o mesmo que usar getFloat() para solicitar um valor aleatório que varia de 0 a menos de 1:

$rando = new Random\Randomizer();

$rando->nextFloat(); // 0.3767414902847

Outras pequenas mudanças no PHP 8.3

O PHP 8.3 também inclui várias outras funções novas e pequenas mudanças. Vamos mencioná-las abaixo com links para recursos adicionais (quando disponíveis):

Depreciações no PHP 8.3

A cada nova versão do PHP, algumas funções e configurações são sinalizadas para eventual remoção. Uma vez depreciados esses recursos, continuar a usá-los não é recomendado, e eles gerarão avisos em muitos registros quando aparecerem na execução do código.

Aqui está uma lista de depreciações no PHP 8.3, com links para informações adicionais:

Resumo

Analisamos as mudanças significativas incluídas no PHP 8.3. Para uma lista detalhada de cada atualização dessa versão, consulte o changelog oficial da versão. Se você planeja mover seu código para uma plataforma que execute o PHP mais recente, o Guia de Migração de 8.2 para 8.3 pode ajudá-lo a não ter problemas.

Se você tem o encargo de instalar o PHP em seus servidores de desenvolvimento ou produção, a versão 8.3 está pronta para download agora.

Se você é um cliente Kinsta, pode confiar em nós para disponibilizar essa versão nos servidores por trás de seus projetos de Hospedagem Gerenciada de WordPress ou Hospedagem de Aplicativos.

Steve Bonisteel Kinsta

Steve Bonisteel is a Technical Editor at Kinsta who began his writing career as a print journalist, chasing ambulances and fire trucks. He has been covering Internet-related technology since the late 1990s.