Многие фатальные и поправимые фатальные ошибки были переделаны в исключения в 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 |
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()
используется совместно с оператором массива
[]
, как показано ниже:
<?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) }
Переменные переменные больше не могут быть использованы с ключевым словом global. Для эмуляции предыдущего поведения необходимо использовать фигурные скобки:
<?php
function f() {
// Работает только в PHP 5.
global $$foo->bar;
// Работает в PHP 5 и 7.
global ${$foo->bar};
}
?>
Основной принцип - не использовать global с чем либо отличающемся от голой переменной.
В 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
Побитовые смещения (в обоих направлениях) за пределы ширины типа 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($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if (false === $int) {
throw new Exception("Invalid integer!");
}
var_dump($int); // int(65535)
?>
В связи с добавлением нового синтаксиса экранирования кодов Unicode, строки, содержащие строку \u{, предворяющую некорректную последовательность, может привести к фатальной ошибке. Для того, чтобы этого избежать, необходимо экранировать первый обратный слеш.
Функции объявленные устаревшими в PHP 4.1.0 в пользу call_user_func() и call_user_func_array(). Возможно ват также будет интересно посмотреть Обращение к функциям через переменные и/или оператор ....
Устаревшая функция mcrypt_generic_end() была удалена в пользу функции mcrypt_generic_deinit().
Дополнительно, устаревшие функции mcrypt_ecb(),
mcrypt_cbc(), mcrypt_cfb() и
mcrypt_ofb() в пользу функции mcrypt_decrypt(),
используюмую с соответствующими константами
MCRYPT_MODE_*
.
Все функции ext/mysql были удалены. Для выбора альтернативного MySQL API, читайте раздел Выбор MySQL API.
Все функции ext/mssql были удалены. Список альтернатив смотрите в разделе Введение в 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-файла были удалены с соответствующими им возможностями:
xsl.security_prefs
Директива xsl.security_prefs
была удалена.
Вместо нее, для контроля настроек безопасности, используйте вызов метода
на уровне препроцессоре.
Результат оператора 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); }
}
// Обратите вимание: НЕ расширяет класс 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
Более невозможно задать более одного блока default в
операторе switch. К примеру, такая конструкция выдаст
ошибку 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, вызовут фатальную ошибку.
Если эти функции вернут любое значение кроме логического,
-1 или 0, они будут
считаться завершенными с ошибкой и вызовут
предупреждение E_WARNING.
Внутренний алгоритм сортировки был изменен, что может сказаться на отличном от предыдущего отсортированном порядке элементов, определенных как одинаковые.
Замечание:
Не полагайтесь на порядок одинаковых элементов, так как он может в любое время измениться.