PHP 7.0: Главные нововведения

Ветка PHP 7.0 — это не «ещё один минор», а смена поколения после долгой линейки PHP 5. В языке появляются скалярные объявления типов (по умолчанию работают в coercive-режиме, а strict_types включается осознанно), возвращаемые типы, операторы ?? (null coalescing) и <=> (spaceship), анонимные классы, метод Closure::call(), делегирование итерации через yield from и явный return из генератора с чтением результата через Generator::getReturn(), функции intdiv(), криптографически стойкие random_bytes() / random_int(), ограниченный unserialize() с белым списком классов и preg_replace_callback_array().

Под капотом сотни сценариев, которые раньше заканчивались fatal, превращаются в Error и родственные типы в иерархии Throwable. Обработчик, зарегистрированный через set_exception_handler, если он типизирован как Exception, при первом же TypeError или ParseError может сам вызвать новый fatal — миграцию фронт-контроллеров и логгеров стоит спланировать заранее.

Оглавление


Скалярные типы и strict_types

К параметрам можно добавлять string, int, float, bool наряду с уже привычными классами, array и callable. Если файл не начинается с declare(strict_types=1);, движок ведёт себя в духе «мягкой» типизации: строка "42" спокойно дойдёт до параметра int. Включили strict_types — любое несоответствие превращается в TypeError, без тихих приведений.

declare(strict_types=1);

function increment(int $x): int {
    return $x + 1;
}

Возвращаемые типы

У функций и методов можно объявить тип результата. Возврат null, если он не разрешён сигнатурой, или значение «не того» типа даёт TypeError. (Тип void и связанные с ним правила приходят уже в PHP 7.1 — на 7.0 речь о значениях, которые реально возвращаются.)

Оператор ??

$name = $_GET['name'] ?? 'guest';

Левая часть проверяется на существование и не-null без всплывающих notice для несуществующих ключей — удобнее и короче, чем вложенные isset() ? … : ….

Оператор <=>

Возвращает -1, 0 или 1 в зависимости от соотношения операндов — естественная основа для компараторов в usort(), usort() с составными ключами и любых трёхзначных сравнений.

usort($rows, function ($a, $b) {
    return $a['score'] <=> $b['score'];
});

Анонимные классы

Удобно подставлять разовую реализацию интерфейса без отдельного файла класса — например, логгер или стаб в тестах.

$logger = new class implements LoggerInterface {
    public function log($level, $message, array $context = []) { /* … */ }
};

Closure::call()

Временно привязывает $this и сразу вызывает замыкание — меньше шума, чем связка bindTo() + __invoke().

$getter = function () { return $this->value; };
$getter->call($object);

Генераторы: yield from и возврат значения

  • yield from передаёт управление другому генератору, массиву или объекту Traversable без ручного цикла.
  • return $итог; внутри генератора после завершения итерации доступен вызывающему коду через $gen->getReturn() (пока генератор не исчерпан, метод недоступен).

intdiv(), CSPRNG и опции unserialize

  • intdiv($a, $b) — целочисленное деление; деление на ноль бросает ArithmeticError.
  • random_bytes() / random_int() дают кроссплатформенный CSPRNG — для токенов, сессий и соли предпочтительнее mt_rand().
  • unserialize($data, ['allowed_classes' => ['My\DTO']]) ограничивает, какие классы могут восстановиться из потока — базовая защита от «неожиданных» объектов в данных.

Групповой импорт use

use Foo\Bar\{Baz, Qux};

Сокращает вертикаль из однотипных use при работе с одним пространством имён.

Практические рецепты

Безопасные значения из запроса

$limit = (int) ($_GET['limit'] ?? 25);

Сортировка по строковому ключу

usort($items, function ($a, $b) {
    return strcmp($a['id'], $b['id']);
});

Обратная несовместимость (обзор)

Полный перечень — в официальном migration70 incompatible. Ниже — то, что чаще всего «стреляет» в легаси без статического анализа.

Ошибки и исключения

  • Появилась иерархия Throwable; многие бывшие fatal стали Error, TypeError, ParseError.
  • Обработчик set_exception_handler(function (Exception $e) { … }) на PHP 7 ломается, если прилетает Error — сигнатура должна быть Throwable или без типа.

Парсер и переменные

  • Косвенный доступ вроде $$foo['bar']['baz'] разбирается строго слева направо. Чтобы сохранить семантику PHP 5, расставьте фигурные скобки явно, как в таблице мануала.
  • list() присваивает в порядке перечисления переменных (не в обратном, как в старых версиях). Пустой list() запрещён. Строку через list() распаковать нельзя — только str_split() и аналоги.

Массивы, foreach, ссылки

  • Порядок элементов при автоматическом создании через ссылочные присваивания отличается от PHP 5 — перепроверьте хаки с $a['x'] =& $a['y'].
  • foreach больше не двигает внутренний указатель массива так, как раньше; итерация по значению идёт по копии массива.
  • Лишние скобки вокруг аргумента не заглушают предупреждение «Only variables should be passed by reference».

Строки и числа

  • Шестнадцатеричные строки "0xFF" в числовом контексте не интерпретируются как числа автоматически.
  • Сложные конструкции "${...}" в двойных кавычках изменились — шаблоны с подстановкой переменных стоит прогнать по тестам.

Текст в UTF-8

  • substr(), strlen() и соседи работают в байтах. Для пользовательского текста комбинируйте с mb_*.

Детали по конкретным расширениям (удаление mysql, ereg и т.д.) — в разделе removed.

Удалённые расширения и SAPI

Из поставки исчезают mysql, семейство ereg*, ряд редких драйверов и устаревших SAPI — см. migration70 removed. Практичный маршрут: mysqli или PDO вместо mysql_*, preg_* вместо ereg_*.

Deprecated

Конструкторы «в стиле PHP 4» с именем класса, статические вызовы не-static методов, ручная соль в password_hash(), ldap_sort(), опция capture_session_meta у SSL-контекста и другие пункты — migration70 deprecated.

Прочие миграционные нюансы

  • У assert() появляется режим expectations и директива zend.assertions; строковый аргумент у assert() в последующих 7.x всё больше считается легаси.
  • Проверьте удалённые INI, поведение json_encode / json_decode при ошибках и отсутствие функции split() — замена preg_split() или explode().

Итог

Переезд 5.x → 7.0 — отдельный этап: статанализ, замена mysql/ereg, единая точка catch (Throwable $e) на входе приложения и регрессия по list()/foreach. Когда 7.0 стабилен, миноры 7.1+ идут уже более предсказуемыми шагами — nullable-типы, затем предупреждения count() в 7.2, затем JSON-исключения и heredoc в 7.3.