Поиск по сайту:

Что нового в PHP 8.2?


PHP 8.2 был выпущен в декабре 2022 года как последняя второстепенная версия в цикле выпуска PHP 8.x. Он добавляет новые функции, основанные на возможностях предыдущих версий, что еще больше упрощает процесс разработки. В этой статье мы рассмотрим каждое из основных изменений и покажем, как они упростят поддержку вашего кода.

Улучшения системы ввода

PHP постепенно развивает свою систему типов в сторону более строго типизированной модели в течение последних нескольких выпусков. 8.2 включает два усовершенствования, которые позволяют типам быть еще более выразительными.

Дизъюнктивная нормальная форма: комбинированные союзы и пересечения

Типы объединения и пересечения теперь можно комбинировать везде, где допускается тип, например параметры функции и возвращаемые значения. Полное определение типа должно быть записано с использованием булевой нотации дизъюнктивной нормальной формы (DNF). Это означает, что типы пересечений должны быть заключены в круглые скобки, чтобы быть допустимыми.

Следующая функция позволяет передать либо массив, или объект, который реализует оба Countable и Итератор интерфейсы:

function foo((Countable&Iterator)|array $values) : void {
    // ...
}

Это способствует более мощным определениям типов, подобно функции в примере, показанной выше. До версии PHP 8.2 вам приходилось писать две отдельные функции для достижения того же эффекта, по одной для каждого типа в объединении.

Автономные типы для Null и Booleans

Значения true, false и null теперь принимаются как отдельные типы. Их можно использовать в любом определении типа, чтобы указать, что конкретное значение будет возвращено:

function alwaysReturnsTrue() : true {}
 
function alwaysReturnsFalse() : false {}
 
function alwaysReturnsNull() : null {}

В реальных случаях использования вы обычно записываете эти значения как часть типа объединения. Например, обработка ошибок путем возврата false вместо обычного значения является распространенным шаблоном как в ядре PHP, так и в пользовательском коде. Теперь вы можете правильно описать это поведение в определении возвращаемого типа:

/**
 * Tries to connect to the database; returns `false` on failure
 */
function connectToDatabase() : DatabaseConnection|false;
 
/**
 * Tries to close the database connection; returns an error code on failure
 */
function closeDatabaseConnection() : true|DatabaseErrorCode;

Классы только для чтения

Свойства только для чтения были одной из главных особенностей PHP 8.1. Они позволяют вам обеспечить неизменность свойств класса после их первоначального назначения:

final class User {
 
    public function __construct(
 
        public readonly string $Username,
 
        public readonly bool $Admin) {}
 
}
 
$user = new User(
    Username: "howtogeek",
    Admin: true
);
 
// Error - The property is readonly
$user -> Username = "Demo";

На практике многие классы хотят, чтобы все их свойства были доступны только для чтения. Теперь вы можете пометить весь класс как «только для чтения», что позволит удалить ключевое слово readonly из отдельных свойств.

readonly final class User {
 
    public function __construct(
 
        public string $Username,
 
        public bool $Admin) {}
 
}
 
$user = new User(
    Username: "howtogeek",
    Admin: true
);
 
// Error - The property is readonly
$user -> Username = "Demo";

Помимо сохранения повторяющегося набора текста, создание класса только для чтения добавляет несколько других ограничений:

  • Вы не можете добавлять в класс какие-либо нетипизированные свойства. Нетипизированные свойства не поддерживаются как свойства только для чтения, для которых классы только для чтения являются синтаксическим сахаром.
  • Класс также не может содержать статические свойства, так как они также несовместимы с ключевым словом readonly.
  • Класс не может быть расширен дочерними элементами, не предназначенными только для чтения. Наследование разрешено, если дочерний элемент также включает ключевое слово readonly.
  • Динамические свойства нельзя создавать для экземпляров класса, и атрибут AllowDynamicProperties не может переопределить это.

Классы только для чтения упрощают написание объектов передачи данных и других структур, которые должны быть неизменяемыми. Однако их использование не является обязательным: вы все равно можете создавать частично изменяемые классы, продолжая использовать ключевое слово readonly для отдельных свойств.

Свойства Enum могут использоваться в постоянных выражениях

Свойства Enum теперь можно использовать в константных выражениях. Следующий код, который не поддерживался в версии перечислений, поставляемой с PHP 8.1, теперь допустим:

enum PostStatus : int {
 
    case Draft = 1;
 
    case Published = 2;
 
    const VALUES = [self::Published -> value => self::Published];
 
}

Доступ к свойству value случая Published перечисления осуществляется без выдачи ошибки, потому что движок PHP знает, что его значение никогда не может измениться.

Черты могут определять константы

Теперь черты могут определять константы, чего не было в предыдущих версиях PHP. Любые константы, написанные внутри трейта, будут видны в области действия классов, которые его используют.

Этот пример демонстрирует возможности доступа к константам трейта, внутри кода трейта, кода класса, который его использует, и из глобальной области видимости:

trait Loggable {
 
    public const LOG_TYPE_JSON = 1;
 
    public function log(string $message) : void {
        if ($this -> getLogType() === self::LOG_TYPE_JSON) {
            // ...
        }
    }
 
    abstract public function getLogType() : int;
 
}
 
 
final class NewUserEvent {
 
    use Loggable;
 
    public function getLogType() : int {
        return self::LOG_TYPE_JSON;
    }
 
}
 
// "1"
echo NewUserEvent::LOG_TYPE_JSON;

Обратите внимание, что вы не можете получить доступ к значению константы непосредственно в свойстве из глобальной области видимости. Следующий код выдаст ошибку «невозможно получить доступ к константе типажа напрямую»:

echo Loggable::LOG_TYPE_JSON;

Современный объектно-ориентированный подход к случайным числам

В PHP 8.2 добавлено новое объектно-ориентированное расширение для генерации случайных чисел. Он позволяет производить случайные числа, используя несколько движков современного поколения. В этом примере показано, как создать случайное целое число от 1 до 100 с помощью механизма xoshiro256**:

use Random\Engine\Xoshiro256StarStar;
use Random\Randomizer;
 
$randomizer = new Randomizer(
    new Xoshiro256StarStar(
        hash(
            algo: "sha256",
            data: "256-bit seed value",
            binary: true
        )
    )
);
 
/** Generated with xoshiro256** */
echo $randomizer -> getInt(1, 100);

Если впоследствии вы захотите переключиться на другой движок, вам нужно всего лишь заменить параметр, переданный вашему экземпляру Randomizer:

use Random\Engine\Mt19937;
use Random\Randomizer;
 
$randomizer = new Randomizer(new Mt19937(1234));
 
/** Generated with Mt19937 */
echo $randomizer -> getInt(1, 100);

Предотвращение утечки паролей в трассировку стека и журналы ошибок

Код, подобный следующему, является стандартной функцией во многих кодовых базах PHP:

function connectToDatabase(
    string $host,
    int $port,
    string $username,
    string $password) : void {
    // ...
}

Это создает проблему, когда функция выдает неперехваченное исключение. Трассировка стека PHP включает значения параметров функции, поэтому пароль в конечном итоге передается в ваши журналы ошибок. Это угроза безопасности.

PHP 8.2 решает эту проблему, предоставляя новый атрибут, который помечает параметры как «конфиденциальные». Применение атрибута #[\SensitiveParameter] к любому параметру удалит его значение из трассировки стека:

function connectToDatabase(
    string $host,
    int $port,
    string $username,
    #[\SensitiveParameter]
    string $password) : void {
    // ...
}

Измененная трассировка стека будет выглядеть примерно так:

Stack trace:
#0 index.php(1): connectToDatabase("localhost", "3306", "demo", Object(SensitiveParameterValue))
#1 {main}

Будет полезно провести аудит вашей кодовой базы, чтобы найти параметры с конфиденциальными значениями после обновления. Добавьте атрибут ко всем экземплярам, которые вы найдете. Это поможет предотвратить угрозу безопасности вашей среды из-за утечки журналов.

Свойства динамического класса устарели

PHP исторически позволял вам устанавливать свойства экземпляров объектов без их предварительного объявления:

final class User {}
 
$user = new User();
$user -> Username = "howtogeek";

Такое поведение часто вызывает проблемы. Ни вы, ни автоматизированные инструменты статического анализа не можете утверждать, какими свойствами будут обладать экземпляры.

Динамические свойства также облегчают опечатки, которые бывает трудно обнаружить:

final class User {
 
    public string $Username;
 
}
 
$user = new User();
$user -> Usernamee = "howtogeek";

Вы по ошибке установили свойство Usernamee, но PHP не выдаст ошибку и не предоставит никакой помощи. Это заставляет вас тратить время на отладку, почему правильное свойство Username не имеет ожидаемого значения.

PHP 8.2 решает эти проблемы, отказываясь от динамических свойств. В дальнейшем вы должны определить все свойства, которые может принимать класс, либо по отдельности, либо как продвигаемые свойства конструктора.

Поскольку это устаревание, а не удаление, существующий код пока будет продолжать функционировать. Уведомление об устаревании будет регистрироваться каждый раз при чтении или установке динамического свойства. Удаление, которое могло произойти в PHP 9.0, будет критическим изменением. Любой код, основанный на динамических свойствах, перестанет работать.

Однако есть способ продолжить использование динамических свойств. Вы можете явным образом включить класс в динамические свойства, пометив его новым атрибутом #[\AllowDynamicProperties]. Это будет продолжать работать и в PHP 9.0. Он решает некоторые законные варианты использования динамических свойств, таких как хранилища конфигурации, временные карты и кэши.

#[\AllowDynamicProperties]
final class ConfigStore {}
 
$store = new ConfigStore();
$settings = json_decode(file_get_contents(__DIR__ . "/settings.json"), true);
 
foreach ($settings as $key => $value) {
    $store -> $key = $value;
}

Использование этого атрибута не рекомендуется, за исключением случаев, подобных этому примеру, когда класс является динамическим по своей природе. Прекращение поддержки динамических свойств призвано помочь вам писать более безопасный код, менее подверженный ошибкам.

Это изменение не затрагивает классы, использующие магические методы __get() и __set(). Они будут продолжать работать в обычном режиме, позволяя вам реализовывать свои собственные процедуры при чтении или установке неопределенного свойства. Экземпляры \stdClass() также продолжают поддерживать динамические свойства.

Краткое содержание

PHP 8.2 — это новая захватывающая версия улучшений удобства использования. Он включает в себя экономящие время классы только для чтения, более гибкие определения типов и небольшие корректировки, улучшающие работу разработчика, такие как константы в типах, значения перечисления в постоянных выражениях и редактирование значений конфиденциальных параметров для трассировки стека.

Обновление уже доступно через все поддерживаемые каналы распространения PHP. Переход на 8.2 должен быть простым для большинства современных кодовых баз, которые уже написаны с использованием функций и стандартов PHP 8.x. Однако следует помнить о некоторых узких нарушениях обратной совместимости, поэтому обратитесь к официальному руководству по миграции, чтобы узнать обо всех изменениях и о том, что вы должны сделать для подготовки.




Все права защищены. © Linux-Console.net • 2019-2024