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

27 de julho de 2022
Ronaldo B.

Este é o segundo artigo da nossa série sobre as novidades do PHP 8. No primeiro artigo falamos sobre 8 novidades desta versão. Você pode acessar esse artigo clicando aqui.

Neste artigo iremos falar sobre mais 8 novidades da versão. Está preparado? Então vamos para mais novidades!

Novo Operador Nullsafe

As últimas versões do PHP têm adicionado vários operadores, que nos ajudaram em várias situações. Na versão 8 teremos a implementação de mais um operador que também será muito útil. Estamos falando do operador Nullsafe.

Um excelente hábito que desenvolvemos quando começamos a amadurecer como programadores é sempre executar algum código depois de verificar que sua expressão não é nula. Geralmente fazemos isso usando um if() verificando se essa variável é diferente de NULL. Isso é ótimo; mas se as verificações forem muito extensas, nosso código pode ficar verboso e repetitivo. Vamos ver um exemplo disso.

Imagine que possuímos uma classe de cidade. Ela possuirá o seguinte conteúdo:

class City {

    public function __construct(
        public string $name,
        public string $state
    ) {

    }

    public function set_name($new_name) {
        $this->name = $new_name;
        return $this;
    }

    public function get_name() {
        return $this;
    }

    public function get_uppercase() {
        return strtoupper($this->name);
    }

}

Vamos instanciar uma nova instância desta classe com o seguinte código:

$city = new City("Sao Bernardo do Campo", "SP");

Agora imagine que desejamos  recuperar o valor do nome da cidade, definir um novo nome e então exibir esse novo nome com letras garrafais, mas sempre fazendo a verificação de as propriedades não serem nulas. Poderíamos fazer isso com o seguinte código:

if ($city !== NULL) {

    $name = $city->get_name();

    if ($name !== NULL) {

        $city->set_name("Sao Paulo");

        $new_name = $city->get_uppercase();

        if ($new_name !== NULL) {

            echo $new_name;

        }

    }

}

Esse código funcionaria. Entretanto, ele ficou um pouco repetitivo, não é mesmo? Nesta situação o novo operador Nullsafe pode nos ajudar. Ele nos permite verificar se o resultado dos métodos ou propriedades é nulo, apenas adicionando um ponto de interrogação (?) antes do sinal “->”. Isso significa que poderíamos reescrever o código acima com a seguinte linha:

echo $city?->get_name()?->set_name('Sao Paulo')?->get_uppercase();

Uau, bem mais simples, não é mesmo?

Note que esse operador será usado principalmente em chamadas de métodos em cadeia. Assim, se desejar utilizá-lo, talvez seja necessário adaptar suas classes e objetos para que funcionem desta forma.

O operador Nullsafe também cancela a execução dos métodos se algum deles retornar um valor nulo. Isso pode nos ajudar na questão da performance de nosso servidor. Imagine que algum método em nosso projeto consome muito processamento ou memória do servidor. Se algum outro método antes dele retornar NULL este método não será executado, o que vai poupar os recursos do servidor. Esse conceito é chamado de “short circuiting” (curto-circuito).

Novo Tipo de Retorno -  static

Para falar sobre essa novidade, precisamos relembrar o conceito de duas palavras-chave muito usadas quando trabalhamos com Orientações a Objetos no PHP: static e self. As duas são usadas para fazer referência para métodos e atributos já declarados nas classes, mas com uma pequena diferença. Para conseguir visualizar essa diferença, é necessário aplicar o conceito de herança na Orientação a Objetos. Vamos ver um exemplo disso.

Vamos criar duas classes, uma chamada First e a outra chamada Second. Elas terão o seguinte código:

class First {

    public static function class_name() {
        return __CLASS__;
    }

    public static function show() {
        return self::class_name();
    }

}


class Second extends First {
 
    public static function class_name() {
        return __CLASS__;
    }

}

Explicando: a segunda classe estende da primeira. As duas possuem o mesmo método chamado class_name(). Ele retorna o nome da classe em que foi criado. Também temos o método show(), que é o responsável por invocar o método class_name(). É neste método que iremos concentrar nossa atenção. Note que estamos usando a palavra self para nos referir ao método class_name().

Vamos fazer um teste. Vamos fazer a chamada do método show() na classe Second, desta forma:

echo Second::show();

Esse método foi herdado da classe First. Agora ficamos com a dúvida: “O nome de qual classe será exibida no navegador?” Talvez pensemos: “Da segunda classe. Afinal, chamamos o método show() a partir dela”. Quando acessamos o navegador vemos o seguinte resultado:

Resultado da chamada show() no navegador

Perceba que a classe exibida foi a primeira! Isso ocorre devido ao self. Ele faz referência para os métodos da classe “original”, ou seja, a classe onde o método foi criado inicialmente. Se desejarmos exibir o nome da segunda classe, precisaríamos trocar o self por static no método show():

public static function show() {
    return static::class_name();
}

O static faz referência ao método da classe que foi chamada. Ele faz essa referência em tempo de execução. Assim, ao acessar o navegador novamente, veremos o seguinte resultado:

Resultado do static no método show()

Esse conceito é chamado de “Late Static Bindings” (LSB) ou “Ligações Estáticas Atrasadas”.

Tudo bem, mas por que falamos de todo esse conceito? Fizemos isso pois a novidade que estamos falando agora envolve a aplicação do conceito do LSB, mas agora voltado para o retorno dos métodos. Dessa forma, se possuirmos algum método que deverá retornar a instância da classe, poderemos informar o static, não apenas o self. Vamos adaptar o último exemplo para ver a aplicação dessa novidade na prática. Nossas classes agora ficarão assim:

class First {
    public function show($class_name): self {
        return $class_name;
    }
}

class Second extends First {
    public function show($class_name): self {
        return $class_name;
    }
}

class Third extends Second {}

Note que criamos uma terceira classe, chamada Third, que irá estender de Second. Além disso, note que o método show() nas classes First e Second irá retornar o nome da classe que receber como parâmetro, mas esse retorno deverá ser a palavra self, ou seja, a classe onde o método foi criado.

Vamos instanciar a classe Third e informar como parâmetro a classe Second para o método show():

$third = new Third();

$third->show(new Second);

Esse código irá funcionar, pois usamos o self no método show(). Vamos agora adicionar a palavra static ao método na classe Second, desta forma:

class Second extends First {
    public function show($class_name): static {
        return $class_name;
    }
}

Agora, ao executar o mesmo código, veremos o seguinte resultado:

Erro retornado em nosso navegador

Vemos que um erro foi retornado no navegador, informando que o retorno deveria ser uma instância da classe Third. Logo, para que o código volte a funcionar, precisamos deixá-lo assim:

$third->show(new Third);

Talvez nos perguntemos: “Qual é a vantagem dessa novidade? Por que ela foi implementada?” Ela garante que o retorno dos métodos criados em nossas classes seja do tipo que desejamos, o que nos dá um maior controle sobre nosso código.

Esse é um conceito complexo, que pode nos confundir quando o ouvimos pela primeira vez, mas conforme o vemos na prática ele vai se tornando mais natural para nós. ?

Mixed Type

No último artigo nós falamos sobre a novidade “Union Types” que será implementada no PHP 8 e permitirá que definamos mais de um tipo para uma variável ou retorno de métodos em nossos projetos.

Usando o conceito desse novo recurso, também será adicionado ao PHP 8 o recurso de Mixed Type, ou Tipos Mistos. Usando a palavra reservada mixed poderemos nos referir a uma série de tipos. Isso já existia em várias IDEs ou e também no PHPDoc, mas não existia de forma nativa no PHP.

Mixed engloba os tipos array, bool, callable, int, float, null, object, resource e string, todos resumidos em uma única palavra. Vamos ver um exemplo desse recurso.

Vamos criar uma nova classe, que possuirá a seguinte estrutura:

class Person {

    public mixed $info;

    public function set_info(mixed $info) {

        $this->info = $info;

    }

}

Com o código acima podemos informar qualquer tipo de dado para o método set_info(). Por exemplo:

$glaucio = new Person();

$glaucio->set_info('Glaucio Daniel');

Neste código informamos uma string, definindo o nome da pessoa. Mas se desejarmos informar a idade dela, também é possível:

$glaucio->set_info(25);

Coloquei a idade de 25 para o Glaucio ficar feliz ?.

Esse é um recurso bem interessante, mas é necessário citar alguns pontos importantes sobre ele.

Por exemplo, o Mixed Type só pode ser usado sozinho. Afinal de contas, ele engloba todos os tipos. Isso significa que o código abaixo não iria funcionar:

function hcode(mixed | string $name) {}

Ele não pode ser usado em conjunto com outro tipo.

O Mixed Type também engloba o tipo null. Dessa forma, não podemos usá-lo de maneira anulável:

function hcode(?mixed $name) {}

Esse código também não funcionaria.

Podemos usar mixed também como retorno de métodos. Contudo, ao informá-lo como retorno, seremos obrigados a retornar alguma informação. Veja por exemplo o código abaixo:

function sum($num1, $num2): mixed {

    echo $num1 + $num2;

}

Note que a função sum() irá exibir o resultado da conta no navegador. Contudo, ela não está retornando esse valor. Logo, ao chamar essa função, veremos o seguinte resultado:

Erro retornado no navegador

Um erro fatal é retornado. O correto é que a função esteja assim:

function sum($num1, $num2): mixed {

    return $num1 + $num2;

}

Agora, ao chamar o método novamente, veremos o resultado correto!

Algo interessante sobre o Mixed Type é que os métodos irão adotá-lo por padrão nos parâmetros que não informarmos nenhum tipo específico. A grande vantagem desse novo recurso é deixar claro o que estamos esperando de retorno nos métodos ou então o que esperamos dos argumentos que iremos informar para os métodos. Mais uma vez vemos um avanço na madureza do PHP com esse novo recurso.

Weak Maps

A próxima novidade é uma nova classe, chamada WeakMap. Essa classe permitirá que criemos uma referência para um objeto, definindo que quando este objeto for removido, sua referência também será removida. Vamos ver isso na prática!

Vamos criar um array chamado $years e adicionar um valor nele, informando a sua chave através de uma variável. O código ficará assim:

$years = [];

$key = "year";

$years[$key] = 1968;

var_dump($years);

Ao acessar o navegador veremos o seguinte resultado:

Array retornado em nosso navegador

Muito bom! O array foi corretamente retornado.

A chave “year” está definida por meio da variável $key. O que acontece se a removermos? Vamos fazer esse teste adicionando o seguinte código:

echo "<br/>";

unset($key);

var_dump($years);

Ao acessar o navegador novamente, vemos o seguinte:

Arrays sendo retornados no navegador

O array foi exibido mais uma vez. Perceba que a variável foi removida, mas sua referência, ou mapeamento, continua igual. Vamos agora aplicar a mesma lógica usando a nova classe WeakMap. O código ficará assim:

$years = new WeakMap;

$key = (object) "year";

$years[$key] = 1968;

var_dump($years);
 
echo "<br/>";

unset($key);

var_dump($years);

Note que a variável $years agora armazena uma nova instância da classe WeakMap. Além disso, foi necessário converter a string na variável $key para um objeto, pois apenas esse tipo de dado é válido para a classe WeakMap. Agora, ao testar o mesmo código no navegador, veremos este resultado:

Novo resultado de nosso código no navegador

Note que agora o objeto foi retornado apenas no primeiro var_dump(). No momento em que removemos a variável $key, sua referência também foi apagada. Esse recurso nos ajudará a poupar a memória de nosso servidor, o que melhorará a performance de nossas aplicações.

Usando ::class em Objetos

A palavra reservada ::class pode ser usada dentro das classes, para se referir ao nome delas. Entretanto, muitos programadores esperavam poder fazer uso dela também nos objetos que instanciam as classes. Essa espera acabou. A partir da versão 8 do PHP poderemos usar essa palavra também fora das classes. Vamos criar um exemplo!

Vamos criar uma classe Company, com o seguinte código:

class Company {

    public function index() {

        return 'Hcode Treinamentos';

    }

}

$hcode = new Company();

Até o PHP 7, se desejássemos ver o nome da classe, precisaríamos usar o seguinte código:

var_dump(get_class($hcode));

Agora, a partir da versão 8, poderemos fazer o mesmo com o seguinte código:

var_dump($hcode::class);

Dessa forma é bem mais simples. Essa é uma excelente novidade, não concorda?

Melhorias nos Métodos Abstratos de Traits

Trait é um recurso que foi adicionado no PHP na versão 5.4 e nos permite isolar partes de nosso código e reutilizá-lo em quantas classes desejarmos. Ele é um recurso muito útil, mas foi identificado um pequeno “problema” há algum tempo, relacionado com os métodos abstratos criados dentro de um Trait. Vamos criar um exemplo para ver esse problema.

Iremos criar um Trait chamado Computer e uma classe chamada Notebook, que irá fazer uso dele. Nosso código ficará assim:

trait Computer {

    abstract public function set_processor($processor): int;

}

class Notebook {
 
    use Computer;

    public function set_processor($processor): string {

 

    }

}

Percebeu algo estranho? Note que o método set_processor() foi redefinido na classe Notebook, mas informando um outro tipo de retorno. Se estivéssemos trabalhando com uma interface esse código não funcionaria, mas com o Trait funciona. Pensando nessa questão, a partir do PHP 8 a assinatura de um Trait está um pouco mais específica e agora a validação desse recurso também envolve o retorno dos métodos. Dessa forma, se testarmos o código acima com o PHP 8, veremos o seguinte resultado:

Nova mensagem de erro ao trabalhar com Traits

Vírgula na Última Posição na Passagem de Parâmetros

Para falar sobre a próxima novidade, preciso te fazer uma pergunta: “Você adiciona uma vírgula após o último parâmetro informado para uma função ou método?” Muitos programadores fazem isso. O PHP já permite que informemos essa vírgula final quando estamos definindo os argumentos para alguma classe já criada. Por exemplo:

new Address(
    "Avenida Paulista",
    260,
    "São Paulo",
    "SP",
    "Brasil",
);

Note que após o argumento “Brasil” há uma vírgula. Esse código funciona sem problemas. Contudo, imagine que queremos fazer o mesmo ao criar a classe, desta forma:

class Address {

    public function __construct(
        public string $street,
        public string $number,
        public string $city,
        public string $state,
        public string $country,
    ) {

    }

}

Note que também adicionamos uma vírgula após o parâmetro $country. Entretanto, atualmente esse código não funcionaria, até mesmo o Editor de Texto iria retornar um erro. Isso é um pouco estranho, não acha? É provável que você já tenha se deparado com algum problema neste contexto.

A boa notícia é que, visando a uniformidade do PHP, a partir da versão 8 essa sintaxe irá funcionar também. Logo, teremos uma dor de cabeça a menos a partir de agora ?.

Nova Interface - Stringable

Essa nova interface irá usar o novo recurso de Union Types para permitir o uso de uma string ou então de um objeto Stringable no método mágico __toString() em nossas classes. Segundo Nicolas Grekas, autor desse novo recurso e engenheiro no framework Symfony, um dos objetivos é permitir uma atualização direta do PHP 7 para o 8.

Conclusão

Neste artigo conferimos mais 8 novidades do PHP 8. É possível perceber como o PHP está evoluindo e amadurecendo cada vez mais.

Enquanto aguardamos o lançamento desta versão, que provavelmente ocorrerá no dia 26 de novembro, não deixe de aprender o core desta incrível linguagem em nosso Curso Completo de PHP 7.

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.