PHP 8.3 — Типизированные константы и #[Override]
PHP 8.3 (ноябрь 2023) добавил типизацию для констант классов и атрибут для безопасного переопределения методов.
Typed Class Constants
<?php
// До PHP 8.3 — константы без типа
class OldConfig
{
const VERSION = '1.0'; // Тип не указан
const MAX_RETRIES = 3; // Может быть что угодно
}
// PHP 8.3+ — типизированные константы
class Config
{
public const string VERSION = '1.0.0';
public const int MAX_RETRIES = 3;
public const float TAX_RATE = 0.20;
public const bool DEBUG = false;
public const array ALLOWED_HOSTS = ['localhost', '127.0.0.1'];
}
// В интерфейсах — контроль типа при имплементации
interface HasVersion
{
public const string VERSION = '1.0';
}
class App implements HasVersion
{
// Тип должен совпадать
public const string VERSION = '2.0'; // OK
// public const int VERSION = 2; // Fatal error: тип не совпадает
}
Типизированные константы и наследование
<?php
class Base
{
public const string TYPE = 'base';
protected const int PRIORITY = 0;
}
class Child extends Base
{
// Можно переопределить значение, но тип должен быть совместимым
public const string TYPE = 'child'; // OK
protected const int PRIORITY = 10; // OK
// Нельзя изменить тип
// public const int TYPE = 42; // Fatal error
}
// Enum constants тоже типизируемы
enum Status: string
{
case Active = 'active';
// Типизированные константы в Enum
public const string DEFAULT_LABEL = 'Unknown';
public const int TIMEOUT = 30;
}
Поддерживаемые типы констант
<?php
class TypeShowcase
{
// Скалярные
public const int COUNT = 42;
public const float PI = 3.14159;
public const string NAME = 'app';
public const bool ENABLED = true;
// Составные
public const array CONFIG = ['key' => 'value'];
public const mixed ANYTHING = null; // mixed допускает любое значение
// Nullable
public const ?string OPTIONAL = null;
// Union types
public const int|string ID = 42;
// НЕ поддерживаются:
// public const object OBJ = ???; // Нельзя: object
// public const void NONE = ???; // Нельзя: void
// public const never NO = ???; // Нельзя: never
}
#[Override] Attribute
<?php
// Атрибут #[\Override] гарантирует, что метод действительно переопределяет родительский
class ParentClass
{
public function processData(): void
{
// ...
}
}
class ChildClass extends ParentClass
{
#[\Override]
public function processData(): void
{
parent::processData();
// Дополнительная обработка
}
// Если родительский метод переименован или удалён:
// #[\Override]
// public function proceesData(): void {} // Fatal error: method does not override
}
Зачем нужен #[Override]
<?php
// Сценарий: библиотека обновилась, метод переименован
// Без #[Override]: молча создастся новый метод, старый не вызывается
// С #[Override]: Fatal error при загрузке класса — баг обнаружен сразу
interface EventListener
{
public function onEvent(Event $event): void;
}
class UserListener implements EventListener
{
#[\Override]
public function onEvent(Event $event): void
{
// Гарантируем, что метод существует в интерфейсе
}
}
// Работает с:
// - Методами родительских классов
// - Методами интерфейсов
// - Абстрактными методами
// - Методами трейтов
// НЕ работает с:
// - Конструкторами (__construct)
// - Магическими методами (__toString, __get и т.д.)
Практическое применение
<?php
abstract class Repository
{
abstract public function find(int $id): ?object;
abstract public function save(object $entity): void;
abstract public function delete(int $id): void;
}
class UserRepository extends Repository
{
#[\Override]
public function find(int $id): ?User
{
// Covariant return type OK
return User::find($id);
}
#[\Override]
public function save(object $entity): void
{
// ...
}
#[\Override]
public function delete(int $id): void
{
// ...
}
// Этот метод НЕ переопределяет — без #[Override] всё тихо
// С #[Override] — ошибка:
// #[\Override]
// public function softDelete(int $id): void {} // Fatal error
}
json_validate()
<?php
// Проверка JSON без декодирования — быстрее и экономнее по памяти
$json = '{"name": "Alice", "age": 30}';
// До PHP 8.3
json_decode($json);
$isValid = json_last_error() === JSON_ERROR_NONE;
// PHP 8.3+
$isValid = json_validate($json); // true
// С указанием глубины
json_validate($json, 512); // max depth = 512
// С флагами
json_validate($json, 512, JSON_INVALID_UTF8_IGNORE);
// Примеры
var_dump(json_validate('')); // false
var_dump(json_validate('null')); // true (null — валидный JSON)
var_dump(json_validate('"hello"')); // true (строка — валидный JSON)
var_dump(json_validate('{invalid}')); // false
var_dump(json_validate('{"a": 1}')); // true
var_dump(json_validate('[1, 2, 3]')); // true
Преимущество:
json_validate()не создаёт PHP-структуру из JSON, что значительно быстрее и потребляет меньше памяти при простой валидации.
Dynamic Class Constant Fetch
<?php
class Permissions
{
const READ = 1;
const WRITE = 2;
const DELETE = 4;
const ADMIN = 7;
}
// До PHP 8.3 — constant() или Reflection
$name = 'READ';
$value = constant(Permissions::class . '::' . $name);
// PHP 8.3+ — динамический доступ
$value = Permissions::{$name}; // 1
// Практическое применение
function hasPermission(object $user, string $permission): bool
{
$required = Permissions::{strtoupper($permission)};
return ($user->permissions & $required) === $required;
}
// С Enum
enum Color: string
{
case Red = '#FF0000';
case Green = '#00FF00';
case Blue = '#0000FF';
}
$colorName = 'Red';
$color = Color::{$colorName}; // Color::Red
echo $color->value; // "#FF0000"
Randomizer: дополнения
<?php
use Random\Randomizer;
$random = new Randomizer();
// getBytesFromString — случайные байты из заданного алфавита
$code = $random->getBytesFromString('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 6);
echo $code; // Например: "K7X2QM"
// getFloat — случайное float значение
$value = $random->getFloat(0.0, 1.0); // от 0.0 до 1.0
$temp = $random->getFloat(-10.0, 40.0); // температура от -10 до 40
// nextFloat — случайное float от 0 до 1 (исключая 1)
$probability = $random->nextFloat(); // [0.0, 1.0)
// Практический пример: генерация промо-кода
function generatePromoCode(int $length = 8): string
{
$random = new Randomizer();
return $random->getBytesFromString(
'ABCDEFGHJKLMNPQRSTUVWXYZ23456789', // Убрали I, O, 1, 0 (путаются)
$length
);
}
echo generatePromoCode(); // "KX7V3QMN"
Readonly Amendments
<?php
// PHP 8.3 разрешает повторную инициализацию readonly в __clone()
readonly class Point
{
public function __construct(
public float $x,
public float $y,
) {}
// PHP 8.3: можно переинициализировать readonly свойства в __clone
public function __clone(): void
{
// Это работает ТОЛЬКО в __clone()
}
public function withX(float $x): self
{
$clone = clone $this;
// До PHP 8.3 — нельзя было модифицировать readonly в клоне
// PHP 8.3+ — можно через рефлексию внутри __clone
// Для чистого with-pattern используйте:
return new self($x, $this->y);
}
}
Другие изменения PHP 8.3
Улучшенная обработка DateTimeImmutable
<?php
// Новый метод createFromTimestamp
$dt = DateTimeImmutable::createFromTimestamp(1700000000);
$dt = DateTimeImmutable::createFromTimestamp(1700000000.123456); // С микросекундами
Улучшения stack trace
<?php
// PHP 8.3 редактирует чувствительные данные в stack traces
// Параметр #[\SensitiveParameter] скрывает значение в trace
function authenticate(
string $username,
#[\SensitiveParameter] string $password,
): bool {
throw new RuntimeException('Auth failed');
// В stack trace: password = Object(SensitiveParameterValue)
}
mb_str_pad()
<?php
// Многобайтовая версия str_pad — корректно работает с UTF-8
$name = 'Алиса';
echo str_pad($name, 10); // Неправильно для UTF-8 (считает байты)
echo mb_str_pad($name, 10); // "Алиса " — правильно (считает символы)
echo mb_str_pad($name, 10, '.'); // "Алиса....."
echo mb_str_pad($name, 10, '.', STR_PAD_LEFT); // ".....Алиса"
echo mb_str_pad($name, 10, '.', STR_PAD_BOTH); // "..Алиса..."
Сводная таблица: PHP 8.3
| Возможность | Синтаксис |
|---|---|
| Typed class constants | public const string VERSION = '1.0' |
#[\Override] |
#[\Override] public function method() |
json_validate() |
json_validate($json) |
| Dynamic constant fetch | ClassName::{$var} |
Randomizer additions |
getBytesFromString(), getFloat() |
Readonly in __clone |
Переинициализация в __clone() |
mb_str_pad() |
UTF-8 версия str_pad() |
Типичные вопросы на экзамене
- Можно ли использовать
objectкак тип константы? Нет, только скалярные типы, массивы, mixed и union types. - С чем работает
#[Override]? С методами родительских классов, интерфейсов и трейтов. Не с конструкторами. - Что вернёт
json_validate('null')?true—nullявляется валидным JSON. - Что такое dynamic constant fetch?
ClassName::{$variable}— доступ к константе по динамическому имени. - Зачем
json_validate(), если естьjson_decode()? Быстрее и не потребляет память на создание структуры.