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

Закрывающая метка больше не обязана стоять в нулевой колонке «как раньше»: её можно отступить, и такой же отступ снимается со всех строк тела. 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(), INI session.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 — MIME image/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, тем чище следующий шаг.