- Классы “только для чтения”
- Отказ от динамических свойств
- Новое расширение random
- Типы в виде дизъюнктивной нормальной формы (ДНФ)
- Константы в трейтах
- Редактирование параметров в обратных трассировках
- Изменения сигнатуры для нескольких методов SPL
- Экранирование имени пользователя и пароля ODBC
- Устаревание частично поддерживаемых вызываемых объектов
Классы “только для чтения”
Свойства, доступные только для чтения, были введены в PHP 8.1. Это изменение построено поверх них и добавляет синтаксический сахар, чтобы сделать все свойства класса доступными только для чтения сразу. Вместо того, чтобы писать это:
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
Обратите внимание, что Вы можете расширять классы, доступные только для чтения, только в том случае, если дочерний класс также доступен только для чтения. (Подробнее про принципы наследования в PHP Вы можете прочитать тут.)
PHP довольно сильно изменился, и классы, доступные только для чтения, являются желанным дополнением.
Отказ от динамических свойств
Можно сказать, что это перемена к лучшему, но для кого-то это будет немного болезненно. Динамические свойства устарели в PHP 8.2 и будут вызывать ошибки исключения в 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';
Если Вы хотите узнать больше о том, почему устаревания полезны и как с ними иметь дело, то Вы можете прочитать пост об этом.
Новое расширение random
PHP 8.2 добавляет новый генератор случайных чисел, который устраняет множество проблем с предыдущим: он более производителен, более безопасен, его проще поддерживать и он не зависит от глобального состояния; устранен ряд трудно обнаруживаемых ошибок при использовании random функций 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 как самостоятельные типы
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
.
Типы в виде дизъюнктивной нормальной формы (ДНФ)
Типы в ДНФ позволяют нам комбинировать union- и intersection-типы, следуя строгому правилу: при объединении union- и intersection-типов, последние должны быть сгруппированы с помощью скобок. На практике это выглядит так:
function generateSlug((HasTitle&HasId)|null $post)
{
if ($post === null) {
return '';
}
return
strtolower($post->getTitle())
. $post->getId();
}
В этом случае
- это тип ДНФ.(HasTitle&HasId)|null
Это приятное дополнение, особенно потому, что оно означает, что теперь мы можем объединять intersection-типы с
, что, вероятно, является наиболее важным вариантом использования этой функции.null
Константы в трейтах
Теперь Вы можете использовать константы в трейтах:
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
Редактирование параметров в обратных трассировках
Обычной практикой в любой кодовой базе является отправка ошибок продакшена в службу, которая отслеживает их и уведомляет разработчиков, когда что-то идет не так. Эта практика часто включает отправку трассировок стека к третьей стороне. Однако бывают случаи, когда эти трассировки стека могут содержать конфиденциальную информацию, такую как переменные среды, пароли или имена пользователей.
PHP 8.2 позволяет вам помечать такие "чувствительные параметры" атрибутом, так что вам не нужно беспокоиться о том, что они будут перечислены в ваших трассировках стека, когда что-то пойдет не так. Вот пример:
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*/
Извлечение свойств enum в выражениях const
Это изменение предлагает разрешить использование
/->
для извлечения свойств перечислений в постоянных выражениях. Основная мотивация для этого изменения заключается в том, чтобы разрешить выборку свойств ?->
и name
в местах, где value
не разрешены, например, ключи массива.enumobjects
Это означает, что следующий код теперь работает:
enum A: string
{
case B = 'B';
const C = [self::B->value => self::B];
}
Изменения типа возвращаемого значения createFromImmutable() класса DateTime и createFromMutable() класса DateTimeImmutable
Ранее эти методы выглядели так:
DateTime::createFromImmutable(): DateTime
DateTimeImmutable::createFromMutable(): DateTimeImmutable
В PHP 8.2 эти сигнатуры методов изменены следующим образом:
DateTime::createFromImmutable(): static
DateTimeImmutable::createFromMutable(): static
Это изменение имеет гораздо больше смысла, поскольку оно улучшает возможности статического анализа для классов, расширяющихся от DateTime
и DateTimeImmutable
. Однако технически это критическое изменение, которое может повлиять на пользовательские реализации, расширяющие любой из этих двух классов.
Устаревание utf8_encode() и utf8_decode()
В PHP 8.2 использование и utf8_encode()
, и utf8_decode()
вызовет следующие уведомления об устаревании:
//Deprecated: Function utf8_encode() is deprecated
//Deprecated: Function utf8_decode() is deprecated
Смысл данного изменения в том, что эти функции имеют неточное имя, которое часто вызывает путаницу: эти функции преобразуются только между
и ISO-8859-1
, в то время как имя функции предполагает более широкое использование.UTF-8
Здесь Вы найдете более подробное объяснение ситуации.
Нечувствительные к локали strtolower() и strtoupper()
И strtolower()
, и strtoupper()
больше не зависят от локали. Вы можете использовать mb_strtolower()
, если хотите локализованное преобразование регистра.
Изменения сигнатуры для нескольких методов 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
Из руководства по UPDATING
ODBC
теперь экранирует имя пользователя и пароль в случае, когда передаются и строка подключения, и имя пользователя/пароль, и строка должна быть приписана.
То же самое относится к PDO_ODBC
.
Устаревание вставки в строку с помощью ${}
В PHP есть несколько способов встраивания переменных в строки. В данном изменении устаревают два способа сделать это, поскольку они редко используются и часто приводят к путанице:
"Hello ${world}";
//Deprecated: Using ${} in strings is deprecated
"Hello ${(world)}";
//Deprecated: Using ${} (variable variables) in strings is deprecated
Для ясности: два популярных способа интерполяции строк все еще работают:
"Hello {$world}";
"Hello $world";
Устаревание частично поддерживаемых вызываемых объектов
Еще одно изменение, хотя и с немного меньшим влиянием, заключается в том, что частично поддерживаемые вызываемые объекты теперь также устарели. Частично поддерживаемые вызываемые объекты — это вызываемые объекты, которые можно вызывать с помощью 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 для типизированных свойств. Вот пример хорошего объяснения этого: