Novidades do PHP 7.1

Artigo que apresenta as novidades do PHP 7.1.

Especificação de tipos nulos e retornos nulos

No PHP 7, foi introduzido o recurso de declaração de tipos escalares e declaração de retornos de função, porém, ainda não havia suporte para especificar parâmetros obrigatórios que aceitassem valores nulos (null), ou para especificar retornos de funções que poderiam ser nulos, ou para especificar que funções que não retornassem nada (void). Já no PHP 7.1 foram disponibilizadas novas sintaxes para estas necessidades.

A declaração de parâmetros de funções que aceitam valores nulos só era permitida em parâmetros opcionais até a versão 7, conforme exemplo:

function exemplo(int $p1, int $p2 = null): int {
    ...
}

No exemplo acima, a função recebe dois parâmetros inteiros e retorna um inteiro, mas o segundo parâmetro, além de ser opcional, também pode ter valor nulo.

Na versão 7.1 passa a ser possível especificar um parâmetro obrigatório e tipado, mas que pode ter valor nulo. Isso é feito usando o sinal "?" antes do nome do tipo, conforme exemplo:

function exemplo(int $p1, ?int $p2): int {
    ...
}

No exemplo acima, a função recebe dois parâmetros obrigatórios. O primeiro precisa ser sempre inteiro, mas o segundo pode ser um inteiro ou nulo.

Uma observação é que continua sendo possível especificar um tipo de um parâmetro com valor default null, inclusive em conjunto com a nova sintaxe, conforme exemplo:

function exemplo(int $p1, ?int $p2 = null): int {
    ...
}

O sinal "?" também pode ser usado para especificar o tipo de retorno de função que pode ser nulo, conforme exemplo:

function exemplo(): ?int {
    ...
}

E para especificar uma função que não retorna nada (ou seja, sempre retorna null), basta utilizar a palavra void, conforme exemplo:

function exemplo(): void {
    ...
}

Ao especificar que uma função tem retorno nulo, ela passa a não poder retornar nenhum valor que não seja null. Isso garante maior legibilidade do código e segurança de que nenhum retorno da função será utilizado.

Melhorias na construção list

A construção da linguagem list ganhou duas melhorias: a possibilidade de extrair arrays com dados associativos e uma sintaxe alternativa simplificada.

Para quem não conhece o list, ele é capaz de atribuir valores de um array diretamente para várias variáveis, conforme exemplo:

$array = array(123, 456, 789);

list($a, $b, $c) = $array;

echo $a; // Mostra 123
echo $b; // Mostra 456
echo $c; // Mostra 789

Ou seja, a posição "0" do array é atribuída para variável $a, a posição "1" do array é atribuída para variável $b e a posição "2" do array é atribuída para variável $c. Também era possível omitir a variável da construção do list, caso ela não tenha importância:

$array = array(123, 456, 789);

list($a, , $c) = $array;

echo $a; // Mostra 123
echo $c; // Mostra 789

A primeira melhoria do list no PHP 7.1 é que ele passa a aceitar arrays associativos e, para isso, é preciso especificar a chave que será obtida em cada variável do list, conforme exemplo:

$array = array('x' => 123, 'y' => 456, 'z' => 789);

list('z' => $a, 'x' => $b) = $array;

echo $a; // Mostra 789
echo $b; // Mostra 123

Observe que no caso de arrays associativos, apenas as chaves desejadas do array são especificadas no list, e que a ordem com que são capturados não é necessariamente a ordem em que se encontram os dados no array.

A segunda melhoria do list no PHP 7.1 é a sua sintaxe alternativa, utilizando apenas colchetes ao invés de list(). O PHP passa a reconhecer colchetes do lado esquerdo de uma atribuição como sendo uma chamada ao list, conforme exemplo:

$array = array(123, 456, 789);

[$a, $b, $c] = $array;

echo $a; // Mostra 123
echo $b; // Mostra 456
echo $c; // Mostra 789

Esta notação simplificada também segue as mesmas regras do list convencional, ou seja, também é possível omitir variáveis para ignorá-las e é possível utilizar as chaves para obter dados de arrays associativos.

Acesso a índices negatívos de strings

Algumas funções que manipulam strings utilizam índices negativos para se referir ao índice contado a partir do final da string. Por exemplo, substr($a, -1) devolve o valor do último caractere da string (ou false caso seja uma string vazia). Porém, existem várias situações em que seria útil utilizar o índice negativo, mas o PHP não provia esse comportamento.

Porém, no PHP 7.1 foram expandidos os pontos da linguagem em que se pode utilizar os índices negativos (e que faça sentido utilizá-lo).

O caso mais simples é o acesso a um caractere da string, conforme exemplo:

$string = 'abcd';

$ultimo    = $string[-1]; // obtem o ultimo caractere da string (d)
$penultimo = $string[-2]; // obtem o penultimo caractere da string (c)

Veja como seriam estes exemplos antes do PHP 7.1:

$string = 'abcd';

$ultimo    = $string[strlen($string) - 1];
$penultimo = $string[strlen($string) - 2];

// OU assim
$ultimo    = substr($string, -1, 1);
$penultimo = substr($string, -2, 1);

Além deste caso, as seguintes funções foram ajustadas para aceitarem índices negativos: strpos, stripos, substr_count, iconv_strpos, file_get_contents, mb_strimwidth, mb_strpos, mb_stripos, entre outras.

Visibilidade de constantes

Constantes de classes fazem parte do PHP há muito tempo, porém, sempre que elas eram definidas tinham a visibilidade pública. A partir do PHP 7.1 passa a ser possível especificar a visibilidade de cada constante (public, protected ou private) e, caso seja omitida, a visibilidade é public. Veja o exemplo:

class Exemplo {
    public    const TESTE  = 1;
    protected const OUTRO  = 2;
    private   const ULTIMO = 3;
}

Constantes privadas caracteriza um importante instrumento para melhorar a legibilidade interna de uma classe, mas que não tem utilidade fora dela.

Multiplo catch

No artigo Try Catch Finally em PHP foi apresentado o conceito e utilização de exceptions no PHP. Nele, vimos que um catch especificava apenas um tipo específico de exception.

Porém, é comum encontrar código PHP em que dois ou mais tipos de exception vão ser tratados de forma idêntica e era necessário replicar o mesmo código em cada catch desta forma:

try {
    ...
} catch (RuntimeException $e) {
    gerar_log($e->getMessage());
} catch (PDOException $e) {
    gerar_log($e->getMessage());
} catch (Exception $e) {
    ...
}

Note que tanto o catch do tipo RuntimeException quanto do tipo PDOException fazem o mesmo tratamento (código duplicado).

A partir do PHP 7.1 passa a ser possível agrupar vários tipos de exception em um único bloco catch utilizando o símbolo "|", conforme o exemplo:

try {
    ...
} catch (RuntimeException | PDOException $e) {
    gerar_log($e->getMessage());
} catch (Exception $e) {
    ...
}

Observe que agora um único bloco de catch é capaz de capturar exceções dos tipos RuntimeException, PDOException e seus tipos derivados. Porém, foi mantido um catch separado pois ele realizava outra operação.

Type hint iterable

Antes do PHP 7.1, era possível usar o type hint array ou o type hint Traversable. Porém, quando era especificado array, não era possível passar uma variável Traversable e quando era especificado Traversable, não era possível passar uma variável array. Isso gerava um certo inconveniente, já que existem situações em que uma função precisa apenas de uma variável em que possa iterar com um foreach, independente se é um array ou objeto iterável.

Então no PHP 7.1 foi criado o type hint especial iterable, que permite informar que um parâmetro ou um retorno de função será algo iterável, podendo ser tanto um array como um objeto iterável.

Ajustes na variável $this

Basicamente o ajuste no PHP 7.1 foi tornar a variável $this inambíqua e segura. A partir desta versão:

  • não é permitido que funções tenham parâmetros chamados $this.
  • não é permitido criar atributos estáticos chamados $this.
  • não é permitido importar o valor de $this em um escopo pelo comando global.
  • não é permitido usar a variável $this como sendo o valor capturado da iteração de um foreach.
  • não é permitido atribuir novos valores para a variável $this via variável com nome variável.
  • não é permitido atribuir novos valores para a variável $this a partir de uma variável com referência para $this.
  • não é permitido criar variáveis chamadas $this a partir de funções que criam variáveis no escopo local, tais como export ou parse_str.
  • a função get_defined_vars nunca retornará o valor da variável $this.
  • o método mágico __call passa a exibir o valor da variável $this corretamente.
  • não é permitido criar variáveis chamadas $this fora de escopos de classes.

Criação de Closure a partir de callable

Uma das limitações de um callback é que para se usar um método de uma classe como callback em um escopo fora da classe, o método precisa ser público. Porém, isso não ocorre se for construida uma Closure com o mesmo propósito.

No PHP 7.1, foi criado o método estático Closure::fromCallable, que recebe uma variável callable e retorna uma Closure correspondente.

Além disso, utilizar Closure é muito mais rápido que um parâmetro callable, pois o PHP tem um custo alto para determinar que o parâmetro é, de fato, "chamável". Por outro lado, uma variável Closure é por natureza chamável.

Float com mais precisão

No PHP 7.1, a diretiva de configuração serialize_precision passa a ter o valor default -1, indicando que será utilizado o "modo zero" para arredondamento de valores float, que é um algorítimo mais preciso para o propósito. Além disso, a função json_encode passa a usar o tipo de precisão PG (ao invés do EG), garantindo maior precisão de casas decimais para representação de valores float. Basicamente ele passa de 14 para 17 casas decimais.

Melhorias no módulo Curl

Foram introduzidas duas melhorias no módulo Curl.

A primeira é a criação das funções curl_multi_errno, curl_share_errno e curl_share_strerror, para que cada tipo de resource criado pelo módulo curl (via curl_init, curl_multi_init e curl_share_init) tenham suas próprias funções para obter o código do último erro e a última mensagem de erro.

A segunda melhoria é o suporte ao Server Push do HTTP/2. Porém, isso é assunto para outro artigo.

8 comentários

Rubens Takiguti Ribeiro (autor do blog) disse...

Oi, Robert
Eu acredito que essa funcionalidade não deva vir. Pois o "use" não é a mesma coisa que um "require" ou "include". Imagina que você coloca dois "use" assim no seu código:

use Algum\Name\Space\*;
use Outro\Name\Space\*;

Daí lá no meio do código você instancia uma classe através de seu nome relativo "Teste". O PHP ainda não carregou o arquivo PHP que contém a classe, então ele vai recorrer ao autoload, passando o nome completo da classe. Daí que ele se perde, pois ele não sabe se você quis instanciar "Algum\Name\Space\Teste" ou "Outro\Name\Space\Teste".

Faria algum sentido uma funcionalidade de caractere coringa em include/require, mas não faz sentido para "use".