Многие фатальные и поправимые фатальные ошибки были переделаны в исключения в PHP 7. Эти исключения наследуют класс Error, который, в свою очередь, реализует интерфейс Throwable (новый базовый интерфейс, который наследуют все исключения).
Это означает, пользовательские обработчики ошибок могут не быть вызваны, потому что вместо вызова ошибки, будет выброшено исключение (порождая новые фатальные ошибки из-за неперехваченных исключений класса Error).
Более подробное описание того, как ошибки работают в PHP 7, можно найти на странице ошибки PHP 7. Это руководство всего лишь перечисляет изменения, которые могут привести к обратной несовместимости.
Код, реализующий регистрацию обработчика исключений с помощью 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.
Обработка ошибок eval() должна
включать в себя блок catch
, который будет ловить эту ошибку.
Все сообщения E_STRICT
переквалифицированы по другим уровням.
Константа E_STRICT
сохранена, так что
error_reporting(E_ALL|E_STRICT)
не вызовет
ошибки.
Ситуация | Новый уровень/поведение |
---|---|
Индексирование ресурсом | E_NOTICE |
Абстрактные статические методы | Сообщение убрано, не вызывает ошибки |
"Переопределение" конструктора | Сообщение убрано, не вызывает ошибки |
Несоблюдение сигнатуры при наследовании | 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.
Также это относится и к ключевому слову global
. Для эмуляции старого
поведения необходимо использовать фигурные скобки:
<?php
function f() {
// Корректно только в PHP 5.
global $$foo->bar;
// Корректно в PHP 5 и 7.
global ${$foo->bar};
}
?>
Теперь list() присваивает переменные в
том порядке, как они перечислены, а не в обратном. В целом,
это влияет только на случаи, когда list()
используется совместно с оператором массива
[]
, как показано ниже:
<?php
list($a[], $a[], $a[]) = [1, 2, 3];
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() больше не может быть пустой. Следующие примеры недопустимы:
<?php
list() = $a;
list(,,) = $a;
list($x, list(), $y) = $a;
?>
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) }
В PHP 5 при использовании избыточных скобок вокруг аргументов функции не выводилось предупреждение, когда аргумент передавался по ссылке. Теперь предупреждение выводится всегда.
<?php
function getArray() {
return [1, 2, 3];
}
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. Основное изменение касается модификации итерируемого массива и обработки его внутреннего указателя.
До PHP 7 в процессе итерации массива в foreach, его внутренний указатель изменялся. В примере ниже показано, что это поведение изменено:
<?php
$array = [0, 1, 2];
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 используется для перебора по ссылке, он будет лучше отслеживать изменения, вносимые в массив в процессе итерации. К примеру, добавление элементов к итерируемому массиву приведёт к тому, что эти новые элементы попадут в перебор:
<?php
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}
?>
Результат выполнения приведённого примера в PHP 5:
int(0)
Результат выполнения приведённого примера в PHP 7:
int(0) int(1)
Итерация объектов не реализующих Traversable теперь происходит так же, как и итерация массива по ссылке. Так получается вследствие того, что улучшения поведения при изменении массива во время итерации также влияет при добавлении или удалении свойств объекта.
Ранее восьмеричные литералы, содержащие некорректные числа молча
обрезались (0128
считались за 012
).
Сейчас в таких случаях будет выдана ошибка разбора.
Теперь побитовые смещения на отрицательную величину будут бросать исключение ArithmeticError:
<?php
var_dump(1 >> -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
Побитовые смещения (в обоих направлениях) за пределы ширины типа int будут всегда возвращать 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() для проверки строки на содержание шестнадцатеричного числа и преобразования этой строки к значению типа int:
<?php
$str = "0xffff";
$int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if (false === $int) {
throw new Exception("Некорректное целое число!");
}
var_dump($int); // int(65535)
?>
\u{
может вызывать ошибки
В связи с добавлением нового
синтаксиса экранирования кодов Unicode,
строки, содержащие строку \u{
, предваряющую некорректную
последовательность, может привести к фатальной ошибке.
Для того, чтобы этого избежать, необходимо экранировать первый обратный слеш.
Эти функции устарели в PHP 4.1.0 в пользу
функций call_user_func()
и call_user_func_array(). Обратите внимание
на функции переменных
и (или) оператор
...
.
Функции ereg
удалили.
Рекомендованная альтернатива — Perl-совместимые регулярные выражения PCRE.
Устаревшую функцию mcrypt_generic_end() удалили в пользу функции mcrypt_generic_deinit().
Кроме этого, устаревшие функции mcrypt_ecb(),
mcrypt_cbc(), mcrypt_cfb()
и mcrypt_ofb() удалили в пользу
функции mcrypt_decrypt()
с соответствующей константой
MCRYPT_MODE_*
.
Все функции модуля ext/mysql удалили. Подробнее о выборе другого API-интерфейса к СУБД MySQL рассказывает раздел «Выбор MySQL API».
Все функции модуля ext/mssql
удалили.
Устаревшие псевдонимы datefmt_set_timezone_id() и IntlDateFormatter::setTimeZoneID() удалили в пользу функции datefmt_set_timezone() и метода IntlDateFormatter::setTimeZone().
Функцию set_magic_quotes_runtime() и её псевдоним magic_quotes_runtime() удалили. Функции объявили устаревшими в PHP 5.3.0, поскольку они потеряли смысл с отказом от магических кавычек в PHP 5.4.0.
Устаревший псевдоним set_socket_blocking() удалили в пользу функции stream_set_blocking().
Функцию dl() больше нельзя использовать в PHP-FPM. Однако функцию оставили в CLI-интерфейсе и встроенных интерфейсах SAPI.
Поддержку шрифтов PostScript Type1 удалили из модуля GD. Соответственно удалили следующие функции:
Вместо удалённых функций рекомендуется использовать шрифты TrueType и функции, которые с ними связаны.
Следующие INI-директивы были удалены, так как связанные с ними функции также были удалены:
always_populate_raw_post_data
asp_tags
xsl.security_prefs
Директива xsl.security_prefs
была удалена.
Вместо неё для контроля настроек безопасности должен вызываться метод
XsltProcessor::setSecurityPrefs()
на уровне каждого процессора.
Результат оператора new
больше нельзя присваивать
переменной по ссылке:
<?php
class C {}
$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-кода.
Открывающий тег | Закрывающий тег |
---|---|
<% |
%> |
<%= |
%> |
<script language="php"> |
</script> |
Статические вызовы нестатических методов из неподходящего
контекста,
которые признали устаревшими в PHP 5.6
,
теперь оставят переменную $this
неопределённой
для вызываемого метода и выведут предупреждение.
<?php
class A
{
public function test()
{
var_dump($this);
}
}
// Обратите внимание: класс B не расширяет класс A
class B
{
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 больше не требует скобок
и изменилась на право-ассоциативный оператор с приоритетом
между языковой конструкцией 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. Например, такая конструкция выдаст
ошибку E_COMPILE_ERROR
:
<?php
switch (1) {
default:
break;
default:
break;
}
?>
Переменная $HTTP_RAW_POST_DATA больше недоступна. Вместо неё
пользуются потоком php://input
.
#
Поддержку префикса комментария #
в INI-файлах удалили. Вместо него пользуются префиксом ;
.
Это изменение касается как файла php.ini, так и файлов,
которые обрабатываются функциями
parse_ini_file()
и parse_ini_string().
Модуль JSON заменили на JSOND, что порождает три
небольших обратных несовместимости. Первое — числа
нельзя заканчивать точкой, значения наподобие 34.
требуется заменить на 34.0
или 34
.
Второе — в научной нотации экспоненте e
нельзя следовать
сразу за десятичной точкой, значения наподобие 3.e3
требуется заменить на 3.0e3
или 3e3
.
Третье — модуль не считает пустую строку корректным JSON-форматом.
Раньше встроенные функции иногда без предупреждения обрезали числа,
которые получили при приведении значений с типом float к значениям с типом integer,
если float-значение оказывалось больше, чем вмещает integer. Теперь функции
выдадут ошибку уровня E_WARNING и вернут null
.
Предикатные функции, которые реализовали через
пользовательские обработчики сессии и которые возвращают
значения false
или -1
, вызовут фатальную ошибку.
Значения вызовут сбой и ошибку уровня E_WARNING, если из этих функций вернётся значение,
которое отличается от логического, -1
или 0
.
Внутренний алгоритм сортировки улучшили. Изменение алгоритма иногда упорядочивает одинаковые элементы не так, как алгоритм делал это прежде.
Замечание:
Не полагайтесь на порядок одинаковых элементов, так как он изменится в любое время.
Операторы break
и continue
за пределами цикла
или управляющей структуры switch
теперь обрабатываются во время
компиляции, а не во время выполнения, как это было раньше, поэтому выдают ошибки
уровня E_COMPILE_ERROR
.
Инструкции break
и continue
больше
не принимают константу как аргумент и вызывают ошибку
уровня E_COMPILE_ERROR
.
Модуль Mhash полностью интегрировали в модуль Hash. Поэтому больше невозможно определить доступность модуля Mhash функцией extension_loaded(); вместо неё пользуются функцией function_exists(). Кроме того, функция get_loaded_extensions() и функции, которые с ней связаны, больше не сообщают о модуле Mhash.
Директива declare(ticks) больше не проникает в отдельные единицы компиляции.