PHP 7

PHP 7 – Algumas incompatibilidades com versões anteriores

Por em

PHP 7

Veremos algumas incompatibilidades que a versão 7 do PHP estará trazendo em relação a versões anteriores. Não abordarei todas, pois a lista não é muito pequena, mas são incompatibilidades que só vem a melhorar e a ajudar o PHP a continuar evoluindo e ajudando seus usuários.

Conhecer essas incompatibilidades é muito importante para que possamos migrar nossos sistemas de maneira transparente e segura, sem gerar novos bugs e o funcionamento correto.

Erros e exceções

Todo erro fatal agora é uma exceção, mas nem todos são Exception

Antes os erros fatais não eram tratados como exceções, e deveria ser configurado um manipulador para poder tratar eles, e nas classes os erros eram lançados como exceções, com isso deveria ter o tratamento de erros feitos de 2 maneiras distintas. Mas agora mudou e todo erro fatal agora é uma exceção, mas ainda há o tratamento antigo para erros fatais.

No PHP 7, foi adiciona a interface Throwable e toda exceção deve implementar ela. Com isso o PHP tem 2 exceções bases, Error e Exception, de qual as demais estendem, e explico a diferença a seguir.

Exceção Error: os erros antigos, que não eram reportados antes como exceção, viraram classes de exceção e estende a classe Error. Uma exceção Error é repassada até que o primeiro catchcapture-o e faça o que bem entender. Caso não exista um catch, então o PHP irá procurar um manipulador configurado via set_exception_handler. Caso não exista nenhum, o PHP meio que tem um manipulador padrão, onde pega essa exceção e converte para um erro fatal, e o mesmo pode ser manipulado como antigamente. Mudou mas se o programador não fizer o tratamento adequado via catch ou set_exception_handler, no final vira um erro fatal como antigamente.

Exceção Exception: a mesma de sempre, mas agora também implementa a interface Throwable. Não muda o tratamento e funcionamento dela. A única observação é que Exception e Error não herdam uma a outra, são coisas distintas que implementam uma mesma interface. Com isso o catch para Expcetion não irá pegar um erro fatal do tipo Error. Então deve-se tratar uma separada da outra, praticamente adicionar um catch para Error ou usar o set_exception_handlerou deixar o fluxo seguir até virar um erro fatal ao modo antigo, como explicado anteriormente.

Exemplo:

<?php

//...

public function processRequest(Request $request)
{
    try {
        $manager = $this->container->get('my.service');
        $manager->process($request);
    } catch(\Exception $e) {
        $this->container->get('logger_handler')->critical($e);
        
        throw new \My\Expcetion\InvalidRequestException();
    } catch(\Error $error) {
        $this->container->get('logger_handler')->error($error);
        
        throw new \My\Expcetion\InvalidExecutionException();
    }
}

//...
?>

 

Com isso você faz tratamento de exceções Exception, como sempre fez (eu espero), e também trata os errors fatais que estendem Error, de forma adequada para cada tipo. Algumas classes de exceção de erros fatais:

Não mais NULL, ou objetos inúteis, ao ocorrer um erro na inicialização de classes internas

Antigamente diversas classes internas do PHP, quando inicializadas de modo errado, os construtores retornavam NULL ou um objeto inútil, que mostra que não foi inicializado o objeto daquela classe. Mas agora, seguindo a idéia de tratar os errors e problemas como exceções, essas classes irão lançar uma exceção do tipo Exception (não uma Error), para indicar. Esse comportamento é basicamente o que boa parte da comunidade faz com suas classes/pacotes/bibliotecas e o PHP está mudando de forma bem positiva.

Mudanças no comportamento do E_STRICT

Todos os errors indicados quando usava-se o E_STRICT no error_reporting foram reclassificados e a constante E_STRICT não faz mais efeito, então utilizar ela no error_reporting não vai causar mudanças alguma como antigamente.

Situação Novo comportamento
Indexação por recurso Virou um E_NOTICE
Métodos abstratos estáticos Nenhum erro gerado
Redefinição do construtor Nenhum erro gerado
Assinatura incompatível em uma herança Virou E_WARNING
A mesma propriedade, compatíveis, em 2 traits utilizados Nenhum erro gerado
Acessando propriedade estática de modo não estático Virou E_NOTICE
Somente variáveis devem ser atribuídas por referência Virou E_NOTICE
Somente variáveis devem ser passadas por referência Virou E_NOTICE
Chamando métodos não estáticos de modo estático Virou E_DEPRECATED

Variáveis

Devido a mudanças internas no analisador de código, que trouxe diversas melhorias que antes não eram possíveis devido a limitações do analisador, houveram remoções em casos especiais por razões de inconsistências, e com isso algumas incompatibilidades.

Manipulação indireta de variáveis, propriedades e métodos a.k.a. variáveis-variáveis

O acesso indireto a variáveis, propriedades e métodos agora é avaliado da esquerda para a direita, o que em alguns casos eram feitos do jeito oposto. Explicando na prática:

Expressão Interpretação do PHP 5 Interpretação do PHP 7
$$foo[‘bar’][‘baz’] ${$foo[‘bar’][‘baz’]} ($$foo)[‘bar’][‘baz’]
$foo->$bar[‘baz’] $foo->{$bar[‘baz’]} ($foo->$bar)[‘baz’]
$foo->$bar[‘baz’]() $foo->{$bar[‘baz’]}() ($foo->$bar)[‘baz’]()
Foo::$bar[‘baz’]() Foo::{$bar[‘baz’]}() (Foo::$bar)[‘baz’]()

A refatoração é simples e compatível com a versão 5, basta utilizar as chaves ({ e }) para delimitar a sua variável-variável. É necessário refatorar pois, como exemplificado, o comportamento é diferente e pode causar enormes problemas.

list() não trabalha mais com ordem inversa

A list(), quando utilizada com array na parte que recebe as variáveis, no PHP 5, a atribuição era feita na ordem inversa, assim:

<?php
list($a[], $a[], $a[]) = [1, 2, 3];
var_dump($a);
?>

Resultado no PHP 5

array(3) {
  [0]=>
  int(3)
  [1]=>
  int(2)
  [2]=>
  int(1)
}

Resultado no PHP 7

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

Apesar da mudança poder causar grandes problemas devido ao comportamento oposto, o time pede que não seja considerada a ordem das atribuições quando utilizar a list(), pois ela poderá passar por novas mudanças no futuro.

Removida atribução de list() vazia

Não é mais suportada o uso do list() vazio para atribuições, como no exemplo abaixo:

<?php
list() = $a;
list(,,) = $a;
list($x, list(), $y) = $a;
?>

Lista não quebra string

Quando utilizáva-se a list() com strings, poderia-se quebrar a string letra-a-letra, levando em consideração que isso só funcionáva se a string fosse utilizada com a list() via variável, assim:

<?php 
// não quebra letra-a-letra
list($primeiraLetra) = "Silas";
var_dump($primeiraLetra); // NULL

// quebra letra-a-letra no PHP 5. No PHP 7 não funciona mais.
$nome = "Silas";
list($primeiraLetra) = $nome;
var_dump($primeiraLetra); // S
?>

 

No PHP 7, caso deseja quebrar uma string letra-a-letra, deve-se utilizar str_split.

Mudança na ordem dos elementos durante a criação via atribuição por referência

A ordem dos elementos no array mudou quando os elementos são criados automaticamente via atribuição por referência.

<?php
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>

Resultado no PHP 5

array(2) {
  ["b"]=>
  &int(1)
  ["a"]=>
  &int(1)
}

Resultado no PHP 7

array(2) {
  ["a"]=>
  &int(1)
  ["b"]=>
  &int(1)
}

 

O escopo global não aceita mais variáveis-variáveis

Devido às mudanças com variáveis-variáveis, explicado anteriormente, o escopo global não aceita mais passagem de variáveis-variáveis, mas a solução é simples, basta envolver com chaves ({ e }):

<?php
function f() {
    // Aceitável no PHP 5, mas não no PHP 7
    global $$foo->bar;

    // Aceitável no PHP 7, e também no PHP 5
    global ${$foo->bar};
}
?>

Utilização de parênteses ao redor de parâmetros da função não afeta o comportamento

Ao envolver o parâmetro da função com parênteses, ficando de forma redundante, e o parâmetro é por referência, sempre lançará um aviso:

<?php
function getArray() {
    return [1, 2, 3];
}

function squareArray(array &$a) {
    foreach ($a as &$v) {
        $v **= 2;
    }
}

// Generates a warning in PHP 7.
squareArray((getArray()));
?>

Saída:

Notice: Only variables should be passed by reference in XXX.php on line 13

Para uma visão mais completa das incompatibilidades do PHP 7 veja Backward incompatible changes