PHPerKaigi 2024

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

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

Многие фатальные и поправимые фатальные ошибки были переделаны в исключения в 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
Абстрактные статические методы Сообщение убрано, не вызывает ошибки
"Переопределение" конструктора Сообщение убрано, не вызывает ошибки
Несоблюдение сигнатуры при наследовании 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() больше не присваивает переменные в обратном порядке

Теперь 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() больше не разрешено

Конструкция 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)
}

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

В 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

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

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 используется для стандартного перебора по значению, то он оперирует копией массива, а не самим массивом. Это значит, что изменения внесённые в массив внутри цикла не затронут перебираемые значения.

Для 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 теперь происходит так же, как и итерация массива по ссылке. Так получается вследствие того, что улучшения поведения при изменении массива во время итерации также влияет при добавлении или удалении свойств объекта.

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

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

Ранее восьмеричные литералы, содержащие некорректные числа молча обрезались (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{, предваряющую некорректную последовательность, может привести к фатальной ошибке. Для того, чтобы этого избежать, необходимо экранировать первый обратный слеш.

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

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 были удалены.

Псевдонимы 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. Соответственно были удалены следующие функции:

  • imagepsbbox()
  • imagepsencodefont()
  • imagepsextendfont()
  • imagepsfreefont()
  • imagepsloadfont()
  • imagepsslantfont()
  • imagepstext()

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

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

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

Следующие 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, но они зарезервированы на будущее и должны считаться устаревшими.

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

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

Удалённые теги ASP и script
Открывающий тег Закрывающий тег
<% %>
<%= %>
<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 теперь право-ассоциативный оператор

Конструкция 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.

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

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

Замечание:

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

Изменение порядка обработки операторов break и continue

Операторы break и continue вне цикла или управляющей структуры switch теперь обрабатываются во время компиляции, а не во время выполнения, как это было ранее, поэтому выдают ошибки уровня E_COMPILE_ERROR.

Mhash больше не является модулем

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

declare(ticks)

Директива declare(ticks) больше не проникает в различные модули компиляции.

add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top