Outras novidades do PHP 7

Lista de algumas outras novidades do PHP 7 um pouco menos relevantes.

Utilização de nomes reservados

Até o PHP 7, algumas palavras reservadas não podiam ser utilizadas para se criar classes, interfaces, traits ou métodos. Apartir do PHP 7, estes nomes passaram a ser permitidos para estes propósitos. Isso é especialmente útil para nomes de métodos. Antes não podíamos criar um método chamado "and" ou "or", por exemplo, que são bacanas para serem usados numa interface fluente de uma classe geradora de SQL.

Há uma exceção quanto à criação de uma constante de classe chamada "class", caso contrário haveria ambiguidade com a sintaxe para resolução do nome absoluto de uma classe, discutido no artigo Namespaces e Resolução de nomes de Classes.

Criação de constantes contendo arrays

No PHP 7 é possível criar constantes valendo arrays:

define('TESTE', [1, 2, 3]);

var_dump(TESTE[0]);

Constante PHP_INT_MIN

Até o PHP 7, existia a constante especial PHP_INT_MAX, que continha o maior número inteiro que o PHP poderia armazenar nativamente na plataforma em que foi consultada. A partir do PHP 7, também foi criada a constante PHP_INT_MIN, que continha o menor número inteiro que o PHP poderia armazenar nativamente na plataforma em que foi consultada. Isso porque o menor valor possível não é exatamente igual ao maior valor possível com sinal trocado.

Strings podem representar caracteres Unicode de forma escapada

No PHP 7 é possível criar strings com caracteres Unicode de forma escapada. Se você não sabe nada sobre Unicode, leia o artigo: Entendendo o Unicode

// Antes do PHP 7: duas formas
$str = "Rubens 滝口 Ribeiro"; // caractere literal
$str = "Rubens \xE6\xBB\x9D\xe5\x8F\xA3 Ribeiro"; // bytes em hexadecimal

// A partir do PHP 7:
$str = "Rubens \u{6edd}\u{53e3} Ribeiro"; // unicode em hexadecimal

Com isso, é possível montar códigos PHP totalmente em ASCII, mas representar símbolos Unicode de forma facilitada. Assim como ocorre no HTML através das HTML entities.

Closures têm o método call

As Closures (funções anônimas) passaram a ter o método call, que recebem por parâmetro o objeto que será a referência para $this e, opcionalmente, a lista de parâmetros esperados pela função. Segue um exemplo:

// Closure qualquer (que usa $this)
$minhaClosure = function($param1, $param2) {
    return $this->atributo + $param1 + $param2;
};

// Objeto de uma classe anonima
$objeto = new class {
    private $atributo = 1;
};

$result = $minhaClosure->call($objeto, 5, 6); // $result = 12

unserialize com filtro

Ao desserializar um dado em PHP de uma origem não confiável, pode-se acabar sofrendo algum tipo de ataque de injection de código PHP indesejado. Para evitar isso, foi feita uma melhoria na função unserialize. Ela passou a ter um segundo parâmetro (opcional) que representa as opções de desserialização, representado na forma de um array assosiativo. No momento só foi incluída a opção "allowed_classes" que indica as classes que podem ser usadas durante a desserialização. Esse chave pode conter o valor true (qualquer classe), false (nenhuma classe) ou um array com os nomes das classes permitidas.

Quando um objeto de uma classe não esperada é desserializado, ele passa a ser da classe especial __PHP_Incomplete_Class ao invés da classe prevista.

$obj = unserialize($str, ['allowed_classes' => ['Usuario', 'Endereco']]);

Classe IntlChar

No PHP 7 foi criada a classe IntlChar para oferecer alguns recursos de "reflexão" sobre determinado caractere. Por exemplo, saber se ele é uma pontuação, se ele é imprimível, se ele é uma letra minúscula/maiúscula, etc. Basicamente a classe oferece vários métodos estáticos que recebem um caractere por parâmetro (este parâmetro pode ser, inclusive Unicode, com mais de 1 byte). Veja alguns exemplos de uso:

// Métodos correspondentes às funções de string
IntlChar::chr(97);
IntlChar::ord('a');
IntlChar::tolower('A');
IntlChar::toupper('a');

// Métodos correspontendes às funções ctype
IntlChar::isalnum('a');
IntlChar::isalpha('a');
IntlChar::isblank('a');
IntlChar::isdigit('a');
IntlChar::isgraph('a');
IntlChar::islower('a');
IntlChar::isprint('a');
IntlChar::ispunct('a');
IntlChar::isspace('a');
IntlChar::isupper('a');

// Método para obter o tipo de caracter (veja a doc do método charType)
IntlChar::charType('a'); 

Agrupamento de classes no use

Até o PHP 7, um arquivo com namespace precisava colocar uma linha para cada use que pretendesse usar no arquivo. A partir do PHP 7, é possível agrupar um conjunto de classes de um mesmo sub-grupo de namespace.

use Core\MVC\{Model, View, Controller};

Também é possível colocar aliases para cada classe declarada desta forma:

use Core\MVC\{Model as M, View as V, Controller as C};

Gerenators têm o método getReturn

No artigo sobre genarators, vimos que o yield é uma espécie de "return especial". Porém, no PHP 7, também é possível utilizar o velho return em uma função generator. O modo de obter o valor do último valor retornado é através do novo método getReturn como no exemplo:

function colecao() {
    yield 'Burnout';
    return 123;
    yield 'Street Fighter';
    return 456;
}

$c = colecao();

foreach ($c as $item) {
    echo "{$item}\n";
    try {
 var_dump($c->getReturn());
    } catch (Exception $e) {
        echo "Nada retornado\n";
    }
}
var_dump($c->getReturn());

No código acima, o foreach só irá imprimir "Burnout" e "Nada retornado", depois será encerrado por causa do return da função. O getReturn dentro do foreach não obtém nenhum valor retornado (portanto emite uma exception) pois no momento que "Burnout" é pego na interação, o return ainda não ocorreu. Só na iteração seguinte é que o generator executa o return e encerra a interação no foreach. Por fim, o getReturn que está após o foreach vai imprimir "123" pois foi o retorno que foi executado de fato. O valor "Street Fighter" nunca será percorrido, assim como o valor 456 nunca será retornado.

Generators de Generators

A partir do PHP 7, é possível que um genarator retorne dados de outro generator através da sintaxe yield from [OUTRO_GENERATOR]. Antes e depois disso, ele pode retornar itens próprios dele, conforme o exemplo:

// Generator convencional
function generatorA() {
    yield 1;
    yield 2;
    yield 3;
}

// Generator que devolve itens proprios e de outros generators
function generatorB() {
    yield 'a';
    yield from generatorA();
    yield 'b';
}

foreach (generatorB() as $item) {
    echo $item;
}

O código acima vai imprimir "a123b".

Divisão de inteiros

Em contas de divisão, normalmente precisamos do resultado inteiro e o resto (separadamente). Até o PHP 7, a forma mais simples de obter o resultado inteiro de uma divisão era fazendo a divisão, depois arredondando o valor para baixo. A partir do PHP 7, existe a função inddiv que retorna justamente o resultado inteiro de uma divisão:

$resultadoFloat = 7 / 2;        // 3.5
$resultadoInt   = intdiv(7, 2); // 3

$resto          = 7 % 2;        // 1

Mudanças em Sessions

Agora a função session_start aceita um array associativo de opções, que sobrescrevem as configurações de sessão convencionais (definidas no arquivo php.ini ou via ini_set, por exemplo).

Além disso, foi criada uma nova diretiva de configuração chamada "session.lazy_write", que fica habilitada por padrão. O que ela faz é realizar a gravação dos dados de sessão apenas se ele sofreu alguma alteração. Isso tende a tornar a execução mais rápida.

Função preg_replace_callback_array

No PHP 7 foi criada uma nova função preg_replace_callback_array, que é similar à função preg_replace_callback, porém recebe um array associativo com as expressões regulares como chave apontando para funções anônimas usadas como callback para a expressão correspondente. Veja o exemplo:

$resultado = preg_replace_callback_array(
    [
        '/([a-z]+)/i' => function($matches) { return strtoupper($matches[1]); },
        '/\s+/' => function($matches) { return ' '; }
    ],
    "Meu     texto"
);

Este código vai converter para maiúsculas as capturas contendo letras, e vai converter para um espaço as ocorrências de vários espaços consecutivos. Isso irá resultar em "MEU TEXTO"

Função get_resources

No PHP 7 foi criada uma nova função get_resources, que devolve um array com todos os resources abertos em determinado momento. Ela também pode receber um parâmetro para obter apenas os resources de determinado tipo.

Melhorias em Reflection

No PHP 7 foram criadas duas novas classes: ReflectionGenerator e ReflectionType. A primeira faz reflexão sobre generators e a segunda faz a reflexão sobre tipos dos parâmetros de funções/métodos (obtida via ReflectionParameter::getType) ou tipos do retorno de funções/métodos (obtida via ReflectionFunctionAbstract::getReturnType).

list aceitando objetos ArrayAccess

No artigo As interfaces Iterator, ArrayAccess e Countable do PHP vimos que é possível criar uma classe que simula os comportamentos de um array convencional do PHP. Porém, implementar a interface ArrayAccess nem sempre era o suficiente para que instruções envolvendo atribuições com list funcionassem com este objeto assim como ocorre com arrays.

A partir do PHP 7, isso foi resolvido. Agora objetos que implementam a interface ArrayAccess estão automaticamente aptos para serem usados em uma atribuição envolvendo list como neste exemplo:

$obj = new ArrayObject([1, 2, 3]);

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

var_dump($a, $b, $c);

Mudanças em algumas funções

No PHP 7, algumas funções passaram por mudanças. As mais relevantes são:

dirname

Agora a função dirname (que devolve o nome de um diretório a partir de um nome de arquivo) também aceita um segundo parâmetro indicando a profundidade de diretórios a subir na árvore de diretórios. Ou seja, ficou mais simples fazer coisas assim>

// Antes do PHP 7
$dir = dirname(dirname(__FILE__));

// A partir do PHP 7
$dir = dirname(__FILE__, 2);

preg_replace

A função preg_replace não aceita mais a opção depreciada \e, por questões de segurança. Agora é preciso usar preg_replace_callback (ou a nova preg_replace_callback_array) no lugar.

setlocale

A função setlocale (discutida no artigo Trabalhando de acordo com a Localidade em PHP) não aceita mais o primeiro parâmetro na forma de string. O primeiro parâmetro é a categoria de localidade que será aplicada (por exemplo LC_NUMERIC para afetar notações de números). Antes o PHP permitia receber o nome da constante LC_... (na forma de string) ou o valor da constante. A partir do PHP 7, só é aceito o valor da constante.

// Funcionava apenas antes do PHP 7
setlocale('LC_NUMERIC', 'pt_BR.utf8');

// Funciona sempre (forma ideal)
setlocale(LC_NUMERIC, 'pt_BR.utf8');

substr

Quando a função substr recebe o parâmetro $start maior ou igual ao tamanho da string ela retornava false. A partir do PHP 7, ela passa a retornar uma string vazia.

$resultado = substr('abc', 3); // retorna false antes do PHP 7, ou "" a partir do PHP 7

mktime e gmmktime

As funções mktime e gmmktime deixaram de receber o parâmetro $is_dst, que indicava se a data estava com o horário de verão considerado.

1 comentário