PHP 7.4 — Ключевой релиз
PHP 7.4 (ноябрь 2019) — один из самых важных релизов ветки 7.x. Typed properties, arrow functions и spread operator кардинально изменили стиль написания кода.
Typed Properties
Самое значительное нововведение PHP 7.4. Свойства классов теперь могут иметь явное объявление типа.
Базовый синтаксис
<?php
declare(strict_types=1);
class User
{
public int $id;
public string $name;
public string $email;
public ?string $phone = null; // Nullable с default
public bool $active = true; // Default value
public array $roles = []; // Default array
public function __construct(int $id, string $name, string $email)
{
$this->id = $id;
$this->name = $name;
$this->email = $email;
}
}
$user = new User(1, 'Alice', '[email protected]');
$user->name = 'Bob'; // OK
// $user->name = 123; // TypeError: Cannot assign int to string
// $user->name = null; // TypeError: Cannot assign null to string (non-nullable)
$user->phone = null; // OK — phone nullable
Поддерживаемые типы
<?php
class TypeShowcase
{
// Скалярные
public int $count;
public float $price;
public string $title;
public bool $enabled;
// Составные
public array $items;
public object $config;
public iterable $data;
// Классы и интерфейсы
public \DateTime $createdAt;
public \DateTimeInterface $updatedAt;
// Nullable
public ?string $description;
// Self и parent
public self $next;
public ?self $previous = null;
// НЕ поддерживаются в 7.4:
// public void $value; // void — только для return type
// public callable $handler; // callable — нельзя для свойств!
}
Подвох:
callableНЕ может использоваться как тип свойства. Причина: callable зависит от контекста видимости (private метод callable изнутри класса, но не снаружи). Используйте\Closureвместо callable.
Состояние свойства: initialized vs uninitialized
<?php
class Product
{
public int $id; // Uninitialized
public string $name; // Uninitialized
public float $price = 0.0; // Initialized (default)
public ?string $sku = null; // Initialized (null)
}
$product = new Product();
// Обращение к uninitialized свойству — TypeError
// echo $product->id; // Error: Typed property must not be accessed before initialization
// Проверка через isset()
var_dump(isset($product->id)); // false (uninitialized)
var_dump(isset($product->price)); // true
var_dump(isset($product->sku)); // false (null — isset возвращает false)
// Правильная инициализация
$product->id = 1;
$product->name = 'Widget';
echo $product->id; // 1 — теперь OK
Для экзамена:
uninitialized— это новое состояние, отличное отnull. Свойство?string $xбез присвоения — uninitialized. Свойство?string $x = null— initialized со значением null.
Типизированные свойства и наследование
<?php
class Animal
{
public string $name;
public int $legs;
}
class Dog extends Animal
{
// Нельзя изменить тип свойства!
// public float $legs; // Fatal error
// Но можно добавить новые свойства
public string $breed;
}
Arrow Functions (короткие замыкания)
<?php
// До PHP 7.4 — verbose синтаксис
$doubled = array_map(function ($n) {
return $n * 2;
}, [1, 2, 3]);
// PHP 7.4+ — arrow function
$doubled = array_map(fn($n) => $n * 2, [1, 2, 3]);
// Arrow functions автоматически захватывают переменные из scope (by value)
$multiplier = 3;
$result = array_map(fn($n) => $n * $multiplier, [1, 2, 3]);
// [3, 6, 9]
// Без fn нужен use:
$result = array_map(function ($n) use ($multiplier) {
return $n * $multiplier;
}, [1, 2, 3]);
Особенности arrow functions
<?php
// Только одно выражение (нет тела с фигурными скобками)
$fn = fn($x) => $x * 2; // OK
// $fn = fn($x) => { return $x * 2; }; // Syntax error!
// Поддерживают типы
$fn = fn(int $x): int => $x * 2;
// Вложенные arrow functions
$addThenMultiply = fn($a) => fn($b) => ($a + $b) * 2;
echo $addThenMultiply(3)(4); // 14
// Захват по значению, НЕ по ссылке
$counter = 0;
$increment = fn() => $counter + 1;
echo $increment(); // 1
echo $counter; // 0 — не изменился!
// Для изменения нужна обычная функция с use (&$var)
$increment = function () use (&$counter) {
return ++$counter;
};
Null Coalescing Assignment Operator ??=
<?php
// До PHP 7.4
$data['key'] = $data['key'] ?? 'default';
// или
if (!isset($data['key'])) {
$data['key'] = 'default';
}
// PHP 7.4+
$data['key'] ??= 'default';
// Практические примеры
class Config
{
private array $settings = [];
public function get(string $key, mixed $default = null): mixed
{
// Кешируем default при первом обращении
return $this->settings[$key] ??= $default;
}
}
// Инициализация массива
$counts = [];
foreach ($items as $item) {
$counts[$item->category] ??= 0;
$counts[$item->category]++;
}
Spread Operator в массивах
<?php
// Распаковка массивов
$first = [1, 2, 3];
$second = [4, 5, 6];
$merged = [...$first, ...$second]; // [1, 2, 3, 4, 5, 6]
// Можно комбинировать с обычными элементами
$result = [0, ...$first, 99, ...$second];
// [0, 1, 2, 3, 99, 4, 5, 6]
// Распаковка результата функции
function getDefaults(): array
{
return ['color' => 'blue', 'size' => 'M'];
}
// ВНИМАНИЕ: в PHP 7.4 spread работает ТОЛЬКО с числовыми ключами!
$numbers = [...[1, 2], ...[3, 4]]; // OK
// $assoc = [...['a' => 1], ...['b' => 2]]; // Fatal error в 7.4!
// Ассоциативные массивы поддерживаются с PHP 8.1
// Распаковка генераторов
function fibonacci(): Generator
{
[$a, $b] = [0, 1];
while (true) {
yield $a;
[$a, $b] = [$b, $a + $b];
}
}
// Нельзя распаковать бесконечный генератор!
// $all = [...fibonacci()]; // Fatal: memory
Числовой разделитель
<?php
// Подчёркивание для читаемости больших чисел
$million = 1_000_000;
$pi = 3.141_592_653;
$hex = 0xFF_AA_BB;
$binary = 0b1010_0011;
$octal = 0177_777;
// Не влияет на значение
var_dump(1_000_000 === 1000000); // true
// Нельзя:
// $bad = _100; // Это имя переменной
// $bad = 100_; // Trailing underscore
// $bad = 1__000; // Двойной underscore
// $bad = 1_.0; // Рядом с точкой
FFI (Foreign Function Interface)
<?php
// FFI позволяет вызывать C-функции и использовать C-структуры
// Без написания PHP-расширений!
$ffi = FFI::cdef("
int printf(const char *format, ...);
int rand(void);
typedef struct {
int x;
int y;
} Point;
", "libc.so.6"); // Linux
echo $ffi->rand(); // Случайное число из C stdlib
// Создание C-структуры
$point = $ffi->new("Point");
$point->x = 10;
$point->y = 20;
// FFI полезен для:
// - Вызова системных библиотек
// - Работы с бинарными данными
// - Прототипирования расширений
Безопасность: FFI по умолчанию доступен только в CLI. Для веб-среды нужна опция
ffi.enable=preloadи загрузка через preloading.
Preloading
<?php
// php.ini:
// opcache.preload=/path/to/preload.php
// opcache.preload_user=www-data
// preload.php — загружается один раз при старте PHP-FPM
// Все классы и функции становятся доступны во всех запросах
// preload.php
$files = glob('/var/www/app/vendor/autoload_classmap.php');
$classmap = require $files[0];
foreach ($classmap as $class => $file) {
if (str_starts_with($class, 'App\\Domain\\')) {
opcache_compile_file($file);
}
}
// Преимущества:
// - Классы загружаются ОДИН раз, не на каждый запрос
// - Нет overhead от autoload
// - Меньше disk I/O
// Ограничения:
// - Изменения в preloaded файлах требуют перезапуска PHP-FPM
// - Нельзя preload файлы с side effects (echo, define и т.д.)
// - Все зависимости должны быть preloaded тоже
WeakReference
<?php
// Слабая ссылка не предотвращает сборку мусора
$object = new stdClass();
$object->data = 'important';
$weakRef = WeakReference::create($object);
echo $weakRef->get()->data; // "important"
// Удаляем оригинальный объект
unset($object);
var_dump($weakRef->get()); // null — объект собран GC
// Полезно для:
// - Кэшей (объект живёт, пока на него есть сильные ссылки)
// - Observer pattern (наблюдатель не удерживает наблюдаемый объект)
// - ORM identity map
Covariant returns и contravariant parameters
<?php
// PHP 7.4 добавил полную поддержку ковариантности/контравариантности
interface Factory
{
public function create(ChildClass $input): ParentClass;
}
class ConcreteFactory implements Factory
{
// Параметр расширен (contravariant): ChildClass -> ParentClass
// Return сужен (covariant): ParentClass -> ChildClass
public function create(ParentClass $input): ChildClass
{
return new ChildClass();
}
}
Сводная таблица: PHP 7.4
| Возможность | Описание |
|---|---|
| Typed properties | public int $id; |
| Arrow functions | fn($x) => $x * 2 |
| Null coalescing assignment | $x ??= 'default' |
| Spread in arrays | [...$arr1, ...$arr2] (только числовые ключи) |
| Numeric literal separator | 1_000_000 |
| FFI | Вызов C-функций |
| Preloading | opcache.preload |
| WeakReference | Слабые ссылки |
| Covariance/Contravariance | Полная поддержка |
Типичные вопросы на экзамене
- Можно ли использовать
callableкак тип свойства? Нет, используйте\Closure. - Что произойдёт при обращении к uninitialized typed property?
TypeError(ранееError). - Arrow functions захватывают переменные по ссылке? Нет, по значению.
- Можно ли распаковать ассоциативный массив через
...в PHP 7.4? Нет, только с числовыми ключами. - Разница между uninitialized и null?
?string $xбез присвоения — uninitialized;?string $x = null— initialized с null.