Изменения, ломающие обратную несовместимость

Изменения в обработке ошибок и исключений

Многие фатальные и поправимые фатальные ошибки были переделаны в исключения в PHP 7. Эти исключения наследуют класс Error, который, в свою очередь, реализует интерфейс Throwable (новый базовый интерфейс, который наследуют все исключения).

Это означает, пользовательские обработчики ошибок могут не быть вызваны, потому что вместо вызова ошибки, будет выброшено исключение (порождая новые фатальные ошибки из-за неперехваченных исключений класса Error).

Более подробное описание того, как ошибки работают в PHP 7, можно найти на странице ошибки PHP 7. Это руководство всего лишь перечисляет изменения, которые могут привести к обратной несовместимости.

set_exception_handler() больше не гарантирует получение объекта класса Exception

Код, реализующий регистрацию обработчика исключений с помощью set_exception_handler(), используя декларацию типа Exception, вызовет фатальную ошибку, если будет выброшено исключение Error.

Если требуется работа обработчика в PHP 5 и 7, вы должны убрать декларацию класса из перехватчика. Если код предполагается использовать только в PHP 7, то можно просто поменять тип с Exception на Throwable.

<?php
// Только PHP 5. В PHP 7 может вызвать фатальную ошибку.
function handler(Exception $e) { ... }
set_exception_handler('handler');

// Будет работать PHP 5 и 7.
function handler($e) { ... }

// Только PHP 7.
function handler(Throwable $e) { ... }
?>

Встроенные конструкторы всегда вызывают исключения в случае неудачи

Ранее, некоторые встроенные конструкторы могли вернуть NULL или бесполезный объект, в случае ошибки. Все встроенные классы теперь в таком случае будут бросать исключение Exception, ровно как это уже делают пользовательские классы.

Ошибки разбора бросают исключение класса ParseError

Ошибки разбора бросают исключение класса ParseError. Обработка ошибок eval() должна включать в себя блок catch, который будет ловить эту ошибку.

Изменение строгости сообщений E_STRICT

Все сообщения E_STRICT переквалифицированы по другим уровням. Константа E_STRICT сохранена, так что error_reporting(E_ALL|E_STRICT) не вызовет ошибки.

Изменение строгости сообщений E_STRICT
Ситуация Новый уровень/поведение
Индексирование ресурсом E_NOTICE
Aбстрактные статические методы Сообщение убрано, не вызывает ошибки
"Переопределение" конструктора Сообщение убрано, не вызывает ошибки
Несоблюдение сигнатуры при наследовании E_WARNING
Одинаковые (совместимые) свойства в двух разных трейтах Сообщение убрано, не вызывает ошибки
Нестатический доступ к статическому свойству E_NOTICE
Только переменные могут быть присвоены по ссылке E_NOTICE
Только переменные могут быть переданы по ссылке E_NOTICE
Вызов нестатического метода статически E_DEPRECATED

Изменения в обработке переменных

PHP 7 использует абстрактное синтаксическое дерево при разборе файлов с исходным кодом. Это позволило внести множество улучшений в язык, которые ранее были невозможны из-за ограничений парсера, использовавшегося в предыдущих версиях PHP, но привело к удалению некоторых особых возможностей, по соображениям согласованности, и это поломало обратную совместимость. Описание этих особых случаев приведено в этой секции.

Изменения в обработке непрямых переменных, свойств и методов

Непрямой доступ к переменным, свойствам и методам теперь раскрывается строго слева-направо, в противовес предыдущему сочетанию из специальных правил. В таблице представлены изменения в порядке раскрытия.

Старая и новая оценка непрямых выражений
Выражение Интерпретация PHP 5 Интерпретация 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']()

Код, использующий старый порядок раскрытия справа-налево, должен быть переписан с использованием фигурных скобок (см. средний столбец в таблице выше). Это сделает код рабочим как в PHP 5.x, так и в PHP 7.x.

Изменение в обработке list()

list() более не присваевает переменные в обратном порядке

Теперь list() присваевает переменные в том порядке, как они перечислены, а не в обратном. В целом, это влияет только на случаи, когда list() используется совместно с оператором массива [], как показано ниже:

<?php
list($a[], $a[], $a[]) = [123];
var_dump($a);
?>

Результат выполнения данного примера в PHP 5:

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

Результат выполнения данного примера в PHP 7:

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

Также хочется отметить, что полагаться на порядок присвоения оператором list() - не самое разумное решение, поскольку он снова может поменяться в будущем.

Пустое присвоение list() больше не разрешено

Конструкция list() больше не может быть пустой. Следующие примеры недопустимы:

<?php
list() = $a;
list(,,) = 
$a;
list(
$x, list(), $y) = $a;
?>
list() не может раскрывать строки

list() более не может раскрывать строки. Используйте str_split().

Изменен порядок массива при автоматическом создании через присвоение по ссылке

Порядок создания элементов в массиве был изменен, когда элемент создается путем присвоения значения переменной, на которую ссылается этот элемент. Пример:

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

Результат выполнения данного примера в PHP 5:

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

Результат выполнения данного примера в PHP 7:

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

global принимает только простые переменные

Переменные переменные больше не могут быть использованы с ключевым словом global. Для эмуляции предыдущего поведения необходимо использовать фигурные скобки:

<?php
function f() {
    
// Работает только в PHP 5.
    
global $$foo->bar;

    
// Работает в PHP 5 и 7.
    
global ${$foo->bar};
}
?>

Основной принцип - не использовать global с чем-либо отличающемся от голой переменной.

Скобки вокруг аргументов функции больше ни на что не влияют

В PHP 5 при использовании избыточных скобок вокруг аргументов функции не выводилось предупреждение, когда аргумент передавался по ссылке. Теперь предупреждение выводится всегда.

<?php
function getArray() {
    return [
123];
}

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

// Выведет предупреждение в PHP 7.
squareArray((getArray()));
?>

Результат выполнения данного примера:

Notice: Only variables should be passed by reference in /tmp/test.php on line 13

Изменения foreach

Небольшие изменения были внесены в поведение управляющей структуры foreach. Основное изменение касается модификации итерируемого массива и обработки его внутреннего указателя.

foreach больше не изменяет внутренний указатель массива

До PHP 7, в процессе итерации массива в foreach, его внутренний указатель изменялся. В примере ниже показано, что это поведение изменено:

<?php
$array 
= [012];
foreach (
$array as &$val) {
    
var_dump(current($array));
}
?>

Результат выполнения данного примера в PHP 5:

int(1)
int(2)
bool(false)

Результат выполнения данного примера в PHP 7:

int(0)
int(0)
int(0)

foreach по значениям оперирует копией массива

Если foreach используется для стандартного перебора по значению, то он оперирует копией массива, а не самим массивом. Это значит, что изменения внесенные в массив внутри цикла не затронут перебираемые значения.

Для foreach по ссылке улучшили поведение при итерации

Когда foreach используется для перебора по ссылке, он будет лучше отслеживать изменения, вносимые в массив в процессе итерации. К примеру, добавление элементов к итерируемому массиву приведет к тому, что эти новые элементы попадут в перебор:

<?php
$array 
= [0];
foreach (
$array as &$val) {
    
var_dump($val);
    
$array[1] = 1;
}
?>

Результат выполнения данного примера в PHP 5:

int(0)

Результат выполнения данного примера в PHP 7:

int(0)
int(1)

Итерация объектов не реализующих Traversable

Итерация объектов не реализующих Traversable теперь происходит так же, как и итерация массива по ссылке. Так получается вследствие того, что улучшения поведения при изменении массива во время итерации также влияет при добавлении или удалении свойств объекта.

Изменение в обработке значений типа integer

Некорретная восьмеричная нотация

Ранее, восьмиричные литералы, содержащие некорректные числы молча обрезались (0128 считались за 012). Сейчас же, в таких случаях, будет выдана ошибка разбора.

Отрицательные побитовые смещения

Теперь, побитовые смещения на отрицательную величину, будут бросать исключение ArithmeticError:

<?php
var_dump
(>> -1);
?>

Результат выполнения данного примера в PHP 5:

int(0)

Результат выполнения данного примера в PHP 7:

Fatal error: Uncaught ArithmeticError: Bit shift by negative number in /tmp/test.php:2
Stack trace:
#0 {main}
  thrown in /tmp/test.php on line 2

Побитовые смещения с выходом из допустимого диапазона

Побитовые смещения (в обоих направлениях) за пределы ширины типа integer будут всегда возвращать 0. Ранее, поведение определялось архитектурой машины.

Изменение в делении на ноль

Ранее, в случае использования ноля в качестве делителя в операциях "деление" (/) и "деление по модулю" (%) приводило к ошибке уровня E_WARNING и возврату значения false. Теперь же, оператор деления возвращает число с плавающей запятой, равное +INF, -INF, или NAN, как определено в IEEE 754. Деление по модулю, вместо ошибки уровня E_WARNING, бросает исключение DivisionByZeroError.

<?php
var_dump
(3/0);
var_dump(0/0);
var_dump(0%0);
?>

Результат выполнения данного примера в PHP 5:

Warning: Division by zero in %s on line %d
bool(false)

Warning: Division by zero in %s on line %d
bool(false)

Warning: Division by zero in %s on line %d
bool(false)

Результат выполнения данного примера в PHP 7:

Warning: Division by zero in %s on line %d
float(INF)

Warning: Division by zero in %s on line %d
float(NAN)

PHP Fatal error:  Uncaught DivisionByZeroError: Modulo by zero in %s line %d

Изменения в обработке строк

Шестнадцатеричные строки более не считаются за числовые

Строки, содержащие шестнадцатиричные символы более не считаются за числовые. Пример:

<?php
var_dump
("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" "0x1");
var_dump(substr("foo""0x1"));
?>

Результат выполнения данного примера в PHP 5:

bool(true)
bool(true)
int(15)
string(2) "oo"

Результат выполнения данного примера в PHP 7:

bool(false)
bool(false)
int(0)

Notice: A non well formed numeric value encountered in /tmp/test.php on line 5
string(3) "foo"

Используйте функцию filter_var() для проверки строки на содержание шестнадцатеричного числа, и преобразования этой строки к значению типа integer:

<?php
$str 
"0xffff";
$int filter_var($strFILTER_VALIDATE_INTFILTER_FLAG_ALLOW_HEX);
if (
false === $int) {
    throw new 
Exception("Invalid integer!");
}
var_dump($int); // int(65535)
?>

\u{ может вызывать ошибки

В связи с добавлением нового синтаксиса экранирования кодов Unicode, строки, содержащие строку \u{, предворяющую некорректную последовательность, может привести к фатальной ошибке. Для того, чтобы этого избежать, необходимо экранировать первый обратный слеш.

Удаленные функции

call_user_method() и call_user_method_array()

Функции объявленные устаревшими в PHP 4.1.0 в пользу call_user_func() и call_user_func_array(). Возможно вам также будет интересно посмотреть Обращение к функциям через переменные и/или оператор ....

Все функции ereg*

Все функции ereg удалены. Рекомендованная альтернатива - PCRE.

Псевдонимы mcrypt

Устаревшая функция mcrypt_generic_end() была удалена в пользу функции mcrypt_generic_deinit().

Дополнительно, устаревшие функции mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() и mcrypt_ofb() в пользу функции mcrypt_decrypt(), используюмую с соответствующими константами MCRYPT_MODE_*.

Все функции расширения mysql

Все функции ext/mysql были удалены. Для выбора альтернативного MySQL API, читайте раздел Выбор MySQL API.

Все функции расширения mssql

Все функции ext/mssql были удалены. Список альтернатив смотрите в разделе Введение в MSSQL.

Псевдонимы intl

Устаревшие псевдонимы datefmt_set_timezone_id() и IntlDateFormatter::setTimeZoneID() были удалены в пользу функций datefmt_set_timezone() и IntlDateFormatter::setTimeZone(), соответственно.

set_magic_quotes_runtime()

set_magic_quotes_runtime(), наряду с ее псевдонимом magic_quotes_runtime(), были удалены. Они были объявлены устаревшими в PHP 5.3.0, и полностью потеряли свой смысл с отказом от волшебных кавычек в в PHP 5.4.0.

set_socket_blocking()

Устаревший псевдоним set_socket_blocking() был удален в пользу stream_set_blocking().

dl() в PHP-FPM

Функция dl() больше не может использоваться в PHP-FPM. Однако она сохранилась в CLI и встроенных SAPI.

Функции GD Type1

Поддержка шрифтов PostScript Type1 удалена из расширения GD. Соответственно были удалены следующие функции:

Вместо них рекомендуется использовать шрифты TrueType и связанные с ними функции.

Удаленные директивы INI-файла

Удаленные возможности

Следующие директивы настроечного INI-файла были удалены с соответствующими им возможностями:

xsl.security_prefs

Директива xsl.security_prefs была удалена. Вместо нее, для контроля настроек безопасности, используйте вызов метода на уровне препроцессоре.

Прочие изменения, затрагивающие обратную совместимость

Новые объекты не могут присваиваться по ссылке

Результат оператора new больше не может быть присвоен переменной по ссылке:

<?php
class {}
$c =& new C;
?>

Результат выполнения данного примера в PHP 5:

Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3

Результат выполнения данного примера в PHP 7:

Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3

Некорректные имена классов, интерфейсов и трейтов

Следующие имена нельзя использовать для классов, интерфейсов и трейтов:

Более того, следующие имена не должны использоваться. Они не приведут к ошибке в PHP 7.0, но они зарезервированы на будущее и должны считаться устаревшими.

Удалены теги ASP и script PHP

Удалена поддержка использования тегов ASP и script для оборачивания кода PHP.

Удаленные теги ASP и script
Открывающий тег Закрывающий тег
<% %>
<%= %>
<script language="php"> </script>

Удалены вызовы из неподходящего контекста

Ранее признанные устаревшими в PHP 5.6 статические вызовы нестатических методов из неподходящего контекста теперь приведут к тому, что для вызываемого метода переменная $this будет не определена и будет выведено предупреждение.

<?php
class {
    public function 
test() { var_dump($this); }
}

// Обратите вимание: НЕ расширяет класс A
class {
    public function 
callNonStaticMethodOfA() { A::test(); }
}

(new 
B)->callNonStaticMethodOfA();
?>

Результат выполнения данного примера в PHP 5.6:

Deprecated: Non-static method A::test() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 8
object(B)#1 (0) {
}

Результат выполнения данного примера в PHP 7:

Deprecated: Non-static method A::test() should not be called statically in /tmp/test.php on line 8

Notice: Undefined variable: this in /tmp/test.php on line 3
NULL

yield теперь право-ассоциативный оператор

Конструкция yield более не требует оборачивания в скобки и является право-ассоциативным оператором с приоритетом между print и =>. Это может привести к изменению поведения:

<?php
echo yield -1;
// Ранее интерпритировалось как
echo (yield) - 1;
// А теперь так
echo yield (-1);

yield $foo or die;
// Ранее интерпритировалось как
yield ($foo or die);
// А теперь так
(yield $foo) or die;
?>

Скобки могут быть использованы для устранения неоднозначности в таких случаях.

Функции не могут иметь несколько параметров с одинаковыми именами

Более нельзя задать несколько параметров функции с одинаковыми именами. К примеру такая функция выдаст ошибку уровня E_COMPILE_ERROR:

<?php
function foo($a$b$unused$unused) {
    
//
}
?>

Функции работающие с аргументами теперь возвращают их текущие значения

func_get_arg(), func_get_args(), debug_backtrace() и цепь вызова исключения возвращают не оригинальные переданные значения, а текущие значения, которые могут уже быть изменены.

<?php
function foo($x) {
    
$x++;
    
var_dump(func_get_arg(0));
}
foo(1);?>

Результат выполнения данного примера в PHP 5:

1

Результат выполнения данного примера в PHP 7:

2

Оператор switch более не может иметь несколько блоков default

Более невозможно задать более одного блока default в операторе switch. К примеру, такая конструкция выдаст ошибку E_COMPILE_ERROR:

<?php
switch (1) {
    default:
    break;
    default:
    break;
}
?>

Удалена $HTTP_RAW_POST_DATA

$HTTP_RAW_POST_DATA более не доступна. Вместо нее используйте поток php://input.

Запрещены комментарии # в INI-файлах

Поддержка префикса комментария # в INI-файлах удалена. Используйте префикс ;. Это изменение касается как php.ini, так и файлов обрабатываемых функциями parse_ini_file() и parse_ini_string().

Расширение JSON заменено на JSOND

Расширение JSON заменено на JSOND, что порождает три небольших обратных несовместимости. Первая - числа не должны заканчиваться на точку (т.е. 34. должно быть заменено на 34.0 или 34). Вторая - при использовании научной нотации, экспонента e не должна следовать сразу за десятичной точкой (т.е. 3.e3 надо поменять на 3.0e3 или 3e3). Третья - пустая строка более не считается за корректный JSON.

Ошибки внутренних функций при переполнении

Ранее, встроенные функции могли тихо обрезать числа полученные при приведении float к integer, если float был больше, чем способен вместить integer. Теперь же будет выводиться ошибка E_WARNING и возвращаться NULL.

Исправление для возвращаемых значений пользовательского обработчика сессии

Любые предикатные функции, реализованные с помощью пользовательских обработчиков сессии и возвращающие FALSE или -1, вызовут фатальную ошибку. Если эти функции вернут любое значение кроме логического, -1 или 0, они будут считаться завершенными с ошибкой и вызовут предупреждение E_WARNING.

Порядок сортировки одинаковых элементов

Внутренний алгоритм сортировки был изменен, что может сказаться на отличном от предыдущего отсортированном порядке элементов, определенных как одинаковые.

Замечание:

Не полагайтесь на порядок одинаковых элементов, так как он может в любое время измениться.

Mhash больше не является расширением

Расширение Mhash полностью интегрировано в расширение Hash. Таким образом, теперь нельзя определить доступность поддержки Mhash с помощью функции extension_loaded(); вместо нее используйте function_exists(). Кроме того, Mhash больше не будет выводить с помощью get_loaded_extensions() и подобных функций.

add a note add a note

User Contributed Notes 9 notes

up
153
me at fquff dot io
1 year ago
[Editor's note: fixed limit on user request]

As a mathematician, 3/0 == +INF IS JUST WRONG. You can't just assume 3/0 == lim_{x->0+} 3/x, which is +INF indeed, because division IS NOT A CONTINUOUS FUNCTION in x == 0.

Also, 3/0 == +INF ("positive" infinity) while -3/0 == -INF ("negative" infinity) requires the assumption that 0 is a positive number, which is just as illogical as it looks like.

The fact that a warning is emitted is good, but it should definitely equals to NaN. ±INF is just illogical (and arithmetically wrong).

Except for this "detail", looks an amazing update, can't wait to test it even further!

Cheers,
P.
up
69
mossy2100
1 year ago
Although $x/0 is technically not infinity in a purely mathematical sense, when you understand why the IEEE float includes a value for infinity, and returns infinity in this case, it makes sense that PHP would agree with this.

The reason is that programmers don't usually divide by 0 on purpose. A value of 0 as a divisor usually occurs due to underflow, i.e. a value which is too small to be represented as a float. So, for example, if you have values like:
$x = 1;
$y = 1e-15 * 1e-15;
$z = $x/$y;
Because $y will have underflowed to 0, the division operation will throw the division by zero warning, and $z will be set to INF. In a better computer, however, $y would not have the value 0 (it would be 1e-30) and $z would not have the value INF (it would be 1e30).

In other words, 0 is not only representative of an actual 0, but also a very small number which float cannot represent correctly (underflow), and INF does not only represent infinity, but also a very big number which float cannot represent correctly (overflow). We do the best we can within the limitations of floating point values, which are really just good approximations of the actual numbers being represented.

What does bother me is that division by zero is handled in two different ways depending on the operator. I would have preferred the new DivisionByZeroError exception to be thrown in all cases, for consistency and to enforce good programming practices.
up
89
tuxedobob
1 year ago
As a programmer, I don't care whether 3/0 is INF or NaN. Both answers are (probably) equally useless, and tell me that something somewhere else is screwed up.
up
4
maba at mb-systemhaus dot net
1 year ago
NOTE:
the new variable handling in PHP 7 also has side effects on the COM .NET extension. Numeric variants (in the Windows application space) now must be quoted when passed over from PHP. This is the case when the receiving application expects a variable of type variant.

Here is an example:

<?php
  $word
= new COM('Word.Application');

 
// now load a document, ...

  // the following works in PHP 5 but will throw an exception in PHP 7
 
$word->ActiveDocument->PrintOut(false, false, 0, $outfile);

 
// the following works in PHP 7 as well, please note the quotes around the '0'
 
$word->ActiveDocument->PrintOut(false, false, '0', $outfile);
?>
up
3
viktor dot csiky at nospam dot nospam dot eu
1 year ago
It is stated:

"foreach by-value operates on a copy of the array

When used in the default by-value mode, foreach will now operate on a copy of the array being iterated rather than the array itself. This means that changes to the array made during iteration will not affect the values that are iterated."

Please note that this is not exactly true. New foreach operates on a copy of the array, by-value or by-reference. It seems that in the latter case, the array copy is simply moved over (to) the original array before it is presumably destroyed.

As a consequence of this, you may not "dereference" an array containing values - e.g. for use with ReflectionMethod::invokeArgs() or the good ole' call_user_func().
Consider the snippet below:

<?php
function deref(Array $inputArray)
{
       
$retVal = [];
       
        foreach (
$inputArray as &$inputValue)
        {
           
$retVal[] = $inputValue;
        }

        return
$retVal;
}
?>

As of PHP 7.0, this *will no longer work*. You will get the usual suspect:

PHP Warning:  Parameter n to whatever() expected to be a reference, value given in baz.php on line x

You need to convert it to explicitly reference the original array:
<?php
function deref(Array $inputArray)
{
       
$retVal = [];
       
        foreach (
$inputArray as $inputKey => $inputValue)
        {
           
$retVal[$inputKey] = &$inputArray[$inputKey];
        }

        return
$retVal;
}
?>

PLEASE NOTE that this might have the unforeseen consequence of your code not working anymore in php versions less than 5.3 (that is, 5.2 and below).
up
4
Frank
1 year ago
[Editor's Note: that change is listed in the "Changed functions" section.]

The substr function has also been changed in a backward incompatible way.

<?php
substr
("test",4);  # false in PHP 5,  "" in PHP 7
?>

In fact, this is the only thing we had to change in a number of places for our code base to run on PHP 7. It's definitely an improvement though, as the old behavior tended to cause bugs in border cases.
up
-2
Ray.Paseur sometimes uses Gmail
6 months ago
In the section captioned "Changes to the handling of indirect variables, properties, and methods" there are parentheses used in the table directly beneath "PHP 7 interpretation." 

The parentheses are intended to show the evaluation order, but they are not part of the syntax, and should not be used in the variable definition or reference.  This juxtaposition confused one of my colleagues; hopefully this note will save someone else some time.

Examples of the correct curly-brace syntax is further down the page, in the section captioned "global only accepts simple variables."
up
-8
pablomartinrimoli2012 at gmail dot com
2 months ago
I have a question, the call to mysql conection, has changed?
To Top