PHP 7.3: Главные нововведения
PHP 7.3 — релиз полировки и безопасности в ветке 7.x: он упрощает повседневный синтаксис (гибкий heredoc/nowdoc, хвостовые запятые в вызовах, ссылки в list() / деструктуризации []), добавляет точечные помощники (array_key_first(), array_key_last(), is_countable(), hrtime()) и обновляет платформу (JSON_THROW_ON_ERROR / JsonException, PASSWORD_ARGON2ID, PCRE2, заметные правки mbstring). При миграции держите в фокусе три кластера: разбор строк (тело heredoc vs закрывающая метка, классы символов PCRE), семантика ссылок (чтение из массива/свойства, строковые ключи ArrayAccess) и расширения (доли секунд в MySQL, дефолты IMAP, имена cookie с 7.3.23).
Оглавление
- Гибкий Heredoc и Nowdoc
- Завершающие запятые в вызовах функций и методов
- Присваивание по ссылке в
list()и[] instanceofс литераламиCompileError- Новые и обновлённые помощники ядра
- JSON:
JSON_THROW_ON_ERRORиJsonException - Хеширование паролей: Argon2id
- Заметные изменения расширений и stdlib
- Практические рецепты
- Обратно несовместимые изменения (миграционные заметки)
- Deprecated (исправить заранее)
- Прочие изменения и эксплуатация (сборка, INI, производительность)
Гибкий Heredoc и Nowdoc
Закрывающая метка больше не обязана стоять в нулевой колонке «как раньше»: её можно отступить, и такой же отступ снимается со всех строк тела. SQL, HTML и справка CLI становятся читабельнее в коде с отступами.
Миграция: если внутри строки встречается тот же токен, что и метка закрытия (в том числе с отступом), PHP может закрыть строку раньше — выбирайте длинные уникальные метки (SQL_GET_USER, а не SQL).
$query = <<<SQL
SELECT id, name
FROM users
WHERE active = 1
SQL;
Завершающие запятые в вызовах функций и методов
В конце списка аргументов вызова может стоять лишняя запятая — удобно для многострочных diff и генерации аргументов:
$this->logger->info(
'User saved',
[
'id' => $user->id,
'ip' => $request->ip(),
],
);
Речь именно о вызовах; не путайте с объявлением параметров функции в других версиях PHP.
Присваивание по ссылке в list() и []
Деструктуризация поддерживает ссылки:
[&$head, $middle, &$tail] = $parts;
list(&$a, $b) = $pair;
Используйте осознанно, когда нужно менять элементы общей структуры; иначе предпочитайте значения.
instanceof с литералами
Слева может быть литерал — результат всегда false. Это в основном для единообразия и статанализа, а не для бизнес-логики.
CompileError
Добавлен CompileError; от него наследуется ParseError. На практике заметно при token_get_all($code, TOKEN_PARSE) и инструментах: часть ситуаций вместо fatal может приходить как исключение.
Новые и обновлённые помощники ядра
array_key_first()/array_key_last()— без трюков сreset()/end()для ключа (см. мануал про пустые массивы).is_countable($value)—trueдля массивов и объектов сCountable.hrtime($as_number = false)— монотонное высокоразрешающее время; для интервалов предпочтительнее «календарных» часов.net_get_interfaces()— список сетевых интерфейсов (зависит от ОС).
JSON: JSON_THROW_ON_ERROR и JsonException
Флаг JSON_THROW_ON_ERROR для json_encode / json_decode даёт JsonException вместо цепочки json_last_error(). При совместном использовании с JSON_PARTIAL_OUTPUT_ON_ERROR приоритет у последнего.
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
Хеширование паролей: Argon2id
При сборке с подходящей версией libargon2 доступна константа PASSWORD_ARGON2ID для password_hash() и связанных функций — фактическая доступность зависит от дистрибутива и флагов сборки.
Заметные изменения расширений и stdlib
- PCRE: переход на PCRE2 — возможны тонкие отличия (классы символов и т.д.);
preg_quote()экранирует и#. - mbstring: Unicode 11, строки >2 ГБ, ускорение регистронезависимых операций; полный case-folding; именованные группы в
mb_ereg_*ближе к привычному PCRE-стилю (проверьте старый код). - LDAP: поддержка controls в поиске/модификации и
ldap_parse_result(). - MySQLi / PDO MySQL: в результатах native prepare могут появиться доли секунд у
DATETIME(6)/TIMESTAMP(6). - FPM: настройки логов;
getallheaders()доступна под FPM. - Сессии и cookie: массив опций у
session_set_cookie_params(), INIsession.cookie_samesite;setcookie/setrawcookieс массивом опций, включаяsamesite. - Потоки: IPv6 в
stream_socket_get_name()в квадратных скобках. - SPL autoload: исключение из автозагрузчика останавливает дальнейшие автозагрузчики (раньше цепочка продолжалась иначе).
- IMAP: rsh/ssh по умолчанию выключены — включайте только осознанно (
imap.enable_insecure_rsh).
Практические рецепты
Перед count()
if (is_countable($rows)) {
$n = count($rows);
}
Первый и последний ключ
$firstKey = array_key_first($map);
$lastKey = array_key_last($map);
Строгий JSON
try {
$payload = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
// обработка
}
Высокоразрешающий интервал
$t0 = hrtime(true);
// ...
$elapsedNs = hrtime(true) - $t0;
Обратно несовместимые изменения (миграционные заметки)
Ядро
- Гибкий heredoc/nowdoc: совпадение с меткой закрытия внутри тела — риск ошибки разбора или смены смысла; переименуйте метку или перепишите содержимое.
continueвнутриswitch: теперь предупреждение (в PHP это эквивалентноbreak, в других языках ожидали быcontinue 2). Проверьте всеswitch+continue.ArrayAccessи строковые ключи-числа: для литерала"123"больше нет неявного приведения к int при доступе — вызываетсяoffsetGet("123"), а неoffsetGet(123). Обычные массивы не меняются.- Статические свойства и ссылки: нельзя «разделить» наследуемое статическое поле присваиванием по ссылке — исправлена дыра в реализации.
- Ссылки из цепочек
[]/->: ссылка сразу снимается; промежуточные трюки с «подвешенной» ссылкой не работают. - Распаковка
...$traversable: у итераторов с нецелочисленными ключами распаковка не поддерживается (в 7.2 часть случаев работала случайно). - Исключения из режима
EH_THROW: не заполняютerror_get_last(). TypeError: в тексте типыint/bool, неinteger/boolean.compact(): неопределённые переменные дают notice.getimagesize()и родственные: BMP — MIMEimage/bmp, неimage/x-ms-bmp.- Cookie (с PHP 7.3.23): имена входящих cookie не URL-декодируются.
BCMath
- Предупреждения идут через стандартный error handler, а не прямой вывод в stderr.
bcmul()/bcpow()строже соблюдают scale (хвостовые нули могут отличаться от старого поведения).
IMAP
- rsh/ssh выключены по умолчанию; с недоверенными именами ящиков это критично, если включите обратно.
mbstring regex
- Именованные группы меняют состав
$matchesи подстановки вmb_ereg_replace().
MySQLi и PDO MySQL
- Доли секунд в типичных тестах на точное строковое представление времени могут «всплыть».
Reflection
- В текстовом выводе —
int/bool.
SimpleXML
- Арифметика с текстовыми узлами выбирает int/float адекватнее, чем раньше.
Прочее
- Поддержка BeOS снята;
ext_skelпереписан (авторам расширений — обновить скрипты). - Формат IPv6 у
stream_socket_get_name()со скобками.
Deprecated (исправить заранее)
- Регистронезависимые
define()и обращение к константе в другом регистре, чем объявление. - Функция
assert()в namespace — не объявляйте. - Не-строковый needle в семействе
strpos— явный(string)илиchr(). fgetss,gzgetss,SplFileObject::fgetss, фильтрstring.strip_tags.FILTER_FLAG_SCHEME_REQUIRED/FILTER_FLAG_HOST_REQUIREDдляFILTER_VALIDATE_URL.image2wbmp().Normalizer::NONEпри ICU ≥ 56.- Алиасы
mbereg_*→mb_ereg_*. - INI
pdo_odbc.db2_instance_nameформально deprecated.
Полный список: migration73 deprecated.
Прочие изменения и эксплуатация
- Syslog INI:
syslog.facility,syslog.filter,syslog.ident; с 7.3.8 фильтрrawдля прежнего поведения. - Сборщик мусора: улучшения циклического GC — профили памяти/времени долгоживущих воркеров могут сдвинуться.
var_export()дляstdClassчерез(object) array (...).- FTP: режим по умолчанию — binary.
- FILTER_VALIDATE_FLOAT: опция
thousandдля разделителей тысяч. - OpenSSL:
min_proto_version/max_proto_versionдля потоков. - PDO SQLite: только чтение через
PDO::SQLITE_ATTR_OPEN_FLAGS. - XML: возврат обработчика внешних сущностей учитывается при сборке с libxml.
- cURL: требуется libcurl ≥ 7.15.5.
- OPcache: удалена
opcache.inherited_hack. - ODBC: убраны Birdstep / ODBCRouter.
Итог
Сфокусируйте ревью на continue+switch, compact(, реализациях ArrayAccess, heredoc-метках и ...$generator. Прогоните тесты по регуляркам и датам/времени, затем готовьтесь к 7.4 — там уже typed properties и другие крупные темы; чем раньше закроете deprecations 7.3, тем чище следующий шаг.