PHP 8.2: Что нового. Изменения и новый функционал.
Readonly-классы RFC
Readonly-свойства были введены в PHP 8.1. Этот RFC основан на них и добавляет синтаксический сахар, чтобы сделать все свойства класса доступными только для чтения. Вместо того чтобы писать так:
class Post
{
public function __construct(
public readonly string $title,
public readonly Author $author,
public readonly string $body,
public readonly DateTime $publishedAt,
) {}
}
Теперь вы можете писать так:
readonly class Post
{
public function __construct(
public string $title,
public Author $author,
public string $body,
public DateTime $publishedAt,
) {}
}
Функционально, сделать класс доступным только для чтения — то же самое, что сделать каждое свойство доступным только для чтения. Но это не даст добавить динамические свойства в класс:
$post = new Post(/* … */);
$post->unknown = 'wrong';
Uncaught Error: Cannot create dynamic property Post::$unknown
Обратите внимание, что вы можете расширять readonly-класс, только если дочерний класс также будет readonly-класс.
PHP сильно изменился и readonly-классы — долгожданное дополнение. Если хотите, можете посмотреть моё видео об эволюции PHP:
Динамические свойства объявлены устаревшими RFC
Я бы сказал, что это изменение к лучшему, но это будет немного болезненным. Динамические свойства объявлены устаревшими в PHP 8.2 и вызовут ErrorException
в PHP 9.0:
class Post
{
public string $title;
}
// …
$post->name = 'Name';
Имейте в виду, что классы реализующие __get
и __set
, по-прежнему будут работать, как и предполагалось:
class Post
{
private array $properties = [];
public function __set(string $name, mixed $value): void
{
$this->properties[$name] = $value;
}
}
// …
$post->name = 'Name';
Если хотите узнать больше о deprecated
, чем они полезны и как с ними разобраться, можете прочитать мою статью о том, как разобраться с устаревшей функциональностью или посмотрите мой видеоблог:
Новое расширение генерации случайных чисел RFC
В PHP 8.2 добавлен новый генератор случайных чисел устраняющий множество проблем с предыдущим: он более производительный, более безопасный, его проще поддерживать и не зависит от глобального состояния. Устранён ряд трудно обнаруживаемых ошибок при использовании функции случайных чисел PHP.
Новый класс Randomizer
поддерживает движок генерации случайных чисел. Вы можете изменять этот движок в зависимости от ваших потребностей. Например, использовать различные настройки для рабочей среды и тестирования.
$rng = $is_production
? new Random\Engine\Secure()
: new Random\Engine\Mt19937(1234);
$randomizer = new Random\Randomizer($rng);
$randomizer->shuffleString('foobar');
null
, true
и false
как автономные типы RFC
PHP 8.2 вводит три новых типа — или что-то похожее. Мы не будем углубляться в кроличью нору
безопасности типов в этой статье, но технически null
, true
и false
сами по себе могут считаться допустимыми типами. Типичными примерами являются встроенные функции PHP, где false
используется в качестве возвращаемого типа при возникновении ошибки. Например, в file_get_contents
:
file_get_contents(/* … */): string|false
До PHP 8.2 вы уже могли использовать false
вместе с другими типами, как объединение типов. Но теперь он может использоваться как отдельный тип:
function alwaysFalse(): false
{
return false;
}
То же самое относится к true
и null
.
Дизъюнктивная нормальная форма типов RFC
DNF типы позволяют комбинировать объединение и пересечение типов следуя строгому правилу: когда комбинируется объединение и пересечение типов, пересечение типов должно быть сгруппировано в скобках. На практике это выглядит так:
function generateSlug((HasTitle&HasId)|null $post)
{
if ($post === null) {
return '';
}
return
strtolower($post->getTitle())
. $post->getId();
}
В данном случае (HasTitle&HasId)|null
— это DFN тип.
Это приятное дополнение означающее, что у нас могут быть типы пересечений допускающие значение null
, что, вероятно, является наиболее важным вариантом использования этой функции.
Константы в трейтах RFC
Теперь вы можете использовать константы в трейтах:
trait Foo
{
public const CONSTANT = 1;
public function bar(): int
{
return self::CONSTANT;
}
}
Вы не сможете получить доступ к константе через имя трейта ни снаружи, ни внутри трейта.
trait Foo
{
public const CONSTANT = 1;
public function bar(): int
{
return Foo::CONSTANT; // Ошибка
}
}
Foo::CONSTANT; // Ошибка
Однако вы можете получить доступ к константе через класс, который использует трейт, учитывая, что он публичный:
class MyClass
{
use Foo;
}
MyClass::CONSTANT; // 1
Редактирование параметров в трассировках RFC
Отправка ошибок из рабочей среды в службу, которая их отслеживает и уведомляет разработчиков, если что-то пойдёт не так — обычная практика. Эта практика часто включает отправку трассировки стека в стороннюю службу. Однако бывают случаи, когда трассировки стека могут включать конфиденциальную информацию, такую как переменные среды, пароли и имена пользователей.
PHP 8.2 позволяет помечать такие конфиденциальные параметры
атрибутом #[\SensitiveParameter]
и вам не нужно беспокоиться и том, что они будут отображены в трассировке стека, когда что-то пойдёт не так. Вот пример из RFC:
function login(
string $user,
#[\SensitiveParameter] string $password
) {
// …
throw new Exception('Error');
}
login('root', 'root');
Fatal error: Uncaught Exception: Error in login.php:8
Stack trace:
#0 login.php(11): login('root', Object(SensitiveParameterValue))
#1 {main}
thrown in login.php on line 8
Получение свойств перечислений в константных выражениях RFC
Из RFC:
В этом RFC предлагается разрешить использование
->
/?->
для получения свойств перечислений в константных выражениях. Основная причина этого изменения — позволить извлекать имя и значение свойства в местах, где объекты перечисления не разрешены, например, в ключах массива.
Это означает, что следующий код теперь валиден:
enum A: string
{
case B = 'B';
const C = [self::B->value => self::B];
}
Изменение возвращаемого типа DateTime::createFromImmutable()
и DateTimeImmutable::createFromMutable()
Ранее эти методы выглядели так:
DateTime::createFromImmutable(): DateTime
DateTimeImmutable::createFromMutable(): DateTimeImmutable
В PHP 8.2 сигнатуры методов изменены следующим образом:
DateTime::createFromImmutable(): static
DateTimeImmutable::createFromMutable(): static
Это изменение имеет гораздо больше смысла, поскольку оно улучшает статическое понимание возможностей для классов расширяющихся от DateTime
и DateTimeImmutable
. Однако технически это критическое изменение, которое может повлиять на пользовательские реализации расширяющие любой из этих двух классов.
utf8_encode()
и utf8_decode()
объявлены устаревшими RFC
В PHP 8.2 использование utf8_encode()
или utf8_decode()
вызовет следующее предупреждение об использовании устаревшей функции:
Deprecated: Function utf8_encode() is deprecated
Deprecated: Function utf8_decode() is deprecated
RFC утверждает, что эти функции имеют неточное имя, которые часто вызывает путаницу. Эти функции преобразуют только между SO-8859-1 и UTF-8, в то время, как имя функции предполагает более широкое использование. В RFC есть более подробное объяснение рассуждений.
Альтернатива? RFC предлагает использовать вместо них mb_convert_encoding()
strtolower()
и strtoupper()
больше не зависят от локализации RFC
strtolower()
и strtoupper()
больше не зависят от локализации. Вы можете использовать mb_strtolower()
и mb_strtoupper
, если вам нужно локализованное преобразование регистра.
Изменение сигнатуры нескольких SPL методов
Несколько методов SPL были изменены, для обеспечения должным образом их правильной сигнатуры типа:
SplFileInfo::_bad_state_ex()
SplFileObject::getCsvControl()
SplFileObject::fflush()
SplFileObject::ftell()
SplFileObject::fgetc()
SplFileObject::fpassthru()
SplFileObject::hasChildren()
SplFileObject::getChildren()
Новый модификатор n
в PCRE
Теперь, вы можете использовать новый модификатор n
(NO_AUTO_CAPTURE
) в pcre*
функциях.
Экранирование имени пользователя и пароля в ODBC
Из руководства по обновлению:
Расширение ODBC теперь экранирует имя пользователя и пароль в обоих случаях, когда они передаются как строка подключения, так и имя пользователя/пароль.
Тоже применимо к PDO_ODBC
Интерполяция строк ${}
объявлена устаревшей RFC
В PHP есть несколько способов встраивания переменных в строки. Этот RFC объявляет устаревшими два способа сделать это, поскольку они редко используются и приводят к путанице:
"Hello ${world}";
Deprecated: Using ${} in strings is deprecated
"Hello ${(world)}";
Deprecated: Using ${} (variable variables) in strings is deprecated
Для ясности: два популярных способа интерполяции строк всё ещё работают:
"Hello {$world}";
"Hello $world";
Частично поддерживаемые вызываемые объекты объявлены устаревшими RFC
Ещё одно изменение, хотя и с меньшим влиянием, заключается в том, что частично поддерживаемые объекты теперь также объявлены устаревшими. Частично поддерживаемые объекты — вызываемые объекты, которые можно вызвать с помощью call_user_func($callable)
, но не путём прямого вызова $callable()
. Между прочим, список этих вызываемых объектов довольно короткий:
"self::method"
"parent::method"
"static::method"
["self", "method"]
["parent", "method"]
["static", "method"]
["Foo", "Bar::method"]
[new Foo, "Bar::method"]
Причина всего этого? Это шаг в правильном направлении к возможности использовать callable
для типизированных свойств. Никита хорошо объясняет это в RFC:
…все эти вызываемые объекты зависят от контекста. Метод, который ссылается на
self:method
, зависит от того, из какого класса выполняется вызов или проверка возможности вызова. На практике, это обычно справедливо и для последних двух случаев, когда используется в форме [new Foo, "parent::method"]Уменьшение зависимости вызываемых объектов от контекста является вторичной целью этого RFC. После этого RFC единственная оставшаяся зависимость от области видимости — "Foo::bar" может быть видим в одной области видимости, но не в другой. Если бы вызываемые объекты были ограничены публичными методами в будущем (пока приватные методы должны были бы использовать вызываемые объекты первого класса или Closure::fromCallable(), что бы стать независимыми от области видимости), то вызываемый тип стал бы чётко определённым и мог бы использоваться как тип свойства. Однако изменения в обработке видимости не предлагаются как часть этого RFC.
Это всё на данный момент. Я буду обновлять этот список в течение года.