O Que Há de Novo no PHP 8 - Parte 1

27 de julho de 2022
Ronaldo B.

O PHP 8 tem previsão de lançamento para 26 de novembro deste ano e trará muitas novidades que irão nos ajudar e irão elevar essa linguagem para um outro nível.

Através deste post estamos iniciando uma série de artigos que irão falar sobre as novidades desta versão por meio de exemplos práticos. Algumas das novidades são novos recursos, outras são melhorias em recursos já existentes.

Neste artigo falaremos das 8 primeiras novidades.

Método construtor simplificado

Quando trabalhamos com Orientação a Objetos, aprendemos que o método construtor é um dos mais importantes em uma classe. E o PHP 8 irá simplificá-lo, para que não se torne redundante no processo de definição dos atributos no momento em que o objeto é instanciado. Vamos ver um exemplo dessa questão.

Imagine que desejamos criar uma classe Person que irá armazenar as informações de nome, idade e altura de uma pessoa. Atualmente, com a versão 7.4 do PHP, poderíamos criar essa classe com o seguinte código:

class Person {

    public string $name;
    public int $age;
    public float $height;

    public function __construct(
        string $name,
        int $age,
        float $height
    ) {

        $this->name = $name;
        $this->age = $age;
        $this->height = $height;

    }

}

Esse código funcionaria sem erros. Contudo, percebeu como repetimos o nome dos atributos? Fizemos isso três vezes: no momento da criação dos atributos, nos parâmetros do método construtor e na definição deles no corpo do método construtor. Isso é um pouco redundante, não concorda? Na versão 8, poderemos realizar esse mesmo processo com o seguinte código:

class Person {

    public function __construct(
        public string $name,
        public int $age,
        public float $height
    ) {

 

    }

}

Muito mais simples! Note que agora definimos a visibilidade dos atributos no momento em que definimos os parâmetros do método construtor.

É importante lembrar que os atributos que serão criados automaticamente possuirão o mesmo nome que os parâmetros informados. Além disso, essa funcionalidade possui algumas exceções. Esse recurso irá funcionar apenas em métodos construtores de classes não abstratas. Ou seja, não será reconhecido em funções simples, em classes abstratas ou interfaces.

Union Types

O próximo recurso que iremos analisar é chamado Union Types. Seu objetivo é permitir que o PHP suporte vários tipos de dados em sua estrutura, não apenas um valor único. Isso pode ser aplicado a parâmetros de métodos e também no retorno de métodos em nossas classes. Para definir os vários tipos que uma variável pode receber, basta usar o sinal de | (pipe). Vamos ver um exemplo.

Vamos criar uma classe Product que irá armazenar o valor do preço do produto. Esse valor pode ser do tipo inteiro ou decimal (float). A criação desta classe, já aplicando o novo conceito do método construtor simplificado, ficará assim:

class Product {

    public function __construct(
        public int|float $price
    ) { }

}

Dessa forma, se informarmos um valor inteiro, como 15, ou então um valor decimal, como 18.5, será aceito pelo PHP.

Esse recurso pode ser aplicado também nos parâmetros dos métodos. Por exemplo: poderíamos ter essa mesma lógica aplicada em um método set_price():

public function set_price(int|float $new_price) {
	$this->price = $new_price;
}

Podemos fazer essa definição até mesmo no retorno dos métodos. Veja como ficaria o método get_price():

public function get_price(): int|float {
	return $this->price;
}

Neste exemplo adicionamos apenas dois possíveis tipos para os métodos, mas poderíamos definir uma quantidade maior ainda se desejássemos.

Pode haver algumas situações em que esse recurso poderá nos retornar um erro, principalmente quando definirmos alguma configuração redundante. Por exemplo, não é recomendado repetir um mesmo tipo no mesmo contexto. Imagine definir string|STRING nos parâmetros de um método. Isso retornaria um erro.

Também, há tipos que são parecidos. Exemplo: se definirmos o tipo bool|false|true, também veremos um erro. O recomendado é definir apenas bool neste caso para não haver problemas.

Mudanças na herança de métodos privados

Encapsulamento e visibilidade também são conceitos muito importante na Orientação a Objetos. Sabemos que existem três níveis de visibilidade em um método ou atributo: público, protegido e privado. Os três tipos passam pela mesma verificação quando realizamos a herança de métodos de uma classe para outra. Contudo, os métodos privados não são herdados por classes filhas. Dessa forma, não parece fazer muito sentido realizar a mesma verificação nos métodos deste tipo, não é mesmo? Pensando nisso, a partir do PHP 8 tal verificação não ocorrerá mais nesses métodos.

Melhoria na chamada de Exceptions

Vamos falar agora sobre algo que os programadores estão muito acostumados: erros ?. Gerar Exceptions é um processo que repetimos com uma certa frequência, e a partir da versão 8 veremos uma pequena mudança que deixará nosso código um pouco mais simples. Para ver isso na prática, vamos realizar mais um exemplo.

Iremos criar uma função que irá multiplicar dois números. Se o resultado for menor que 10 uma Exception será retornada. A função ficará desta forma:

function multiply_numbers($number1, $number2) {

    $result = ($number1 * $number2);

    if ($result < 10) {
        throw new Exception();
    }

}

Note que não definimos nenhuma mensagem na chamada da classe Exception. Fizemos isso pois desejamos retornar o erro de forma estática, através de um bloco try/catch. Se estivermos usando o PHP 7.4, esse código ficará assim:

try {
    multiply_numbers(2, 3);
} catch (Exception $error) {
    echo "Erro na multiplicação";
}

Realizamos o echo de uma mensagem de erro. Contudo, foi necessário definir uma variável chamada $error, mesmo que não a usemos em nenhum lugar. Isso não parece fazer muito sentido, mas se não fizéssemos essa definição o código não funcionaria. Em contrapartida, usando o PHP 8 poderíamos criar o mesmo código da seguinte forma:

try {
    multiply_numbers(2, 3);
} catch (Exception) {
    echo "Erro na multiplicação";
}

Um pouco mais simples e menos confuso, não concorda?

Throw Expression

Ainda falando sobre erros, na versão 8 veremos uma evolução na declaração de Exceptions. Como sabemos, podemos definir um throw new Exception em funções e métodos. Contudo, não é possível realizar o mesmo em operadores ternários ou em arrow functions no PHP 7.

A boa notícia é que a partir do PHP 8 isso será possível. Vamos ver alguns exemplos.

Imagine que desejamos criar uma arrow function que exiba a informação de um erro. No PHP 8 poderemos criá-la da seguinte forma:

$show_error = fn($error) => throw new Exception($error);

$show_error("Erro no PHP!!!");

Uma outra situação: imagine que possuímos uma função que realiza uma requisição em alguma API externa. Se dados forem retornados da API, iremos exibi-los. Se isso não ocorrer, iremos retornar uma Exception. Poderíamos usar um if ternário ou o operador null coalescing para isso, da seguinte forma:

$result = call_api();

return $result ?? throw new Exception("Erro na chamada da API");

Nosso código ficou bem simples e fácil de entender, não acha?

Fatal Error na sobrescrita de métodos

Na Orientação a Objetos um erro bem comum que pode ocorrer é quando sobrescrevemos um método já criado. Isso pode ocorrer em dois contextos: quando implementamos uma interface ou quando estendemos uma classe de outra. Vamos ver um exemplo dessa questão. Vamos criar uma interface chamada Automobile e uma classe Car, que irá implementar a Interface:

interface Automobile {

    public function show_details(array $details);

}

class Car implements Automobile {

    public function show_details(string $details) { }

}

new Car();

Note que tanto a interface quanto a classe possuem o método show_details(). Contudo, o tipo do parâmetro esperado está diferente neles. Se estivermos testando esse código no PHP 7.4, veremos o seguinte resultado no navegador:

Erro na interface Automobile e variável Car

Note que um erro fatal foi retornado por causa da sobrescrita do método. Vamos ver agora o que ocorre quando realizamos a mesma sobrescrita por meio de duas classes. O código ficará assim:

class Location {

    public function show_details(array $details) {}

}

class City extends Location {

    public function show_details(string $details) {}

}

new City();

Ao testar o código, veremos o seguinte resultado:

Aviso quanto às classes City e Location

Perceba que agora foi retornado um Warning. Contudo, não concorda que o conceito das duas situações é o mesmo? Então por que retornar duas mensagens diferentes para elas? Pensando nisso, a partir do PHP 8 as duas situações irão retornar um erro fatal, padronizando essa questão.

Arrays com índices negativos

Já pensou em usar arrays com índices negativos? Bem interessante, não é mesmo? A partir da versão 8 o PHP permitirá que iniciemos arrays usando índices negativos. Pense por exemplo na função array_fill(). Ela cria um array para nós e espera três parâmetros: 1) o índice inicial do array, 2) a quantidade de posições que o array irá possuir e 3) o valor que será adicionado repetidamente em cada posição do array. O que ocorreria se informássemos a primeira opção com um índice negativo? Vamos fazer esse teste com o seguinte código:

$cars = array_fill(-5, 7, "Ferrari");

print_r($cars);

Se estivermos usando o PHP 7.4, veremos o seguinte resultado no navegador:

Resultado do código PHP 7.4 no navegador

Note que a primeira posição do array foi o -5, o índice inicial que informamos para a função. Entretanto, os outros índices foram definidos a partir do 0. Não faria mais sentido se eles continuassem a partir do -5? Veja agora o resultado que obtemos quando testamos o mesmo código com o PHP 8:

Resultado do código PHP 8 no navegador

Excelente, ocorreu justamente o que estávamos desejando!

Ajustes na concatenação de strings e números

Iremos falar de um dos primeiros conceitos que aprendemos quando estudamos PHP e também um dos conceitos que pode nos causar muita dor de cabeça: a concatenação. Sabemos que o PHP é uma linguagem fracamente tipada e tenta converter números em strings quando usamos o operador . (ponto) em nosso código. Contudo, isso pode nos causar alguns problemas. Vamos ver um exemplo.

Vamos criar duas variáveis numéricas, realizar a soma delas e então exibir o resultado no navegador. Podemos fazer isso por meio do seguinte código:

$number1 = 10;
$number2 = 15;

echo "Resultado: " . $number1 + $number2;

Quando testamos esse código vemos o seguinte resultado:

Resultado de nosso código PHP no navegador

O resultado está incorreto e um Warning foi retornado. Isso ocorre pois o PHP tentou concatenar a string “Resultado” com o número 10, o que não deu certo. O responsável por fazer com que a conta fosse desconsiderada e a concatenação ocorresse é o que chamamos de precedência de operadores. Ele define a prioridade dos operadores no código. Talvez você lembre desse conceito nas aulas de expressões matemáticas na escola.

Atualmente o operador de concatenação (.) tem uma precedência superior em relação ao operador de soma (+). Para resolver o problema acima, precisaríamos isolar a conta na expressão usando parênteses, da seguinte forma:

echo "Resultado: " . ($number1 + $number2);

Entretanto, pode ser que esqueçamos disso e continuemos tendo o mesmo erro. Pensando em resolver essa pedra no sapato, foi proposto que na versão 8 do PHP o operador de concatenação tenha uma precedência inferior aos operadores aritméticos, e felizmente essa proposta foi aceita. Assim, se executarmos o código inicial usando o PHP 8, veremos o seguinte resultado:

Resultado de nosso código no navegador

Perfeito! A operação foi realizada e a string “Resultado” foi exibida.

Conclusão

Neste artigo vimos algumas das incríveis novidades que o PHP 8 está trazendo. Contudo, ainda iremos falar sobre muitas outras nos próximos artigos desta série. Não esqueça de compartilhar esse conteúdo com outros, isso ajuda bastante nosso trabalho, muito obrigado :)

Até o próximo artigo.

Hcode: Utilizamos cookies para a personalização de anúncios e experiências de navegação dentro de nosso site. Ao continuar navegando, você concorda com as nossas Política de Privacidade.