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

Давайте углубимся и посмотрим на большие — а иногда и не такие большие — изменения, которые появились в этом последнем выпуске.

Новые функции и улучшения в PHP 8.3

Начнем с изучения возможностей PHP 8.3, о которых говорится в большинстве заголовков.

Типизированные константы класса

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

Константы классов, включая константы интерфейса, типажа и перечисления, можно вводить в PHP 8.3, что снижает вероятность того, что разработчики отойдут от цели, лежащей в основе первоначального объявления константы.

Вот базовый пример использования интерфейса:

// Legal:
interface ConstTest {
    // Declared type and value are both strings
    const string VERSION = "PHP 8.3";
}

// Illegal:
interface ConstTest {
    // Type and value mismatch in this initial declaration
    const float VERSION = "PHP 8.3";
}

Реальное значение этих констант типизированного класса раскрывается при работе с классами, производными от базовых объявлений. Хотя дочерний класс часто может присваивать константе новое значение, PHP 8.3 может помочь предотвратить случайное изменение ее типа, в результате которого он станет несовместимым с исходным объявлением:

class ConstTest {
    const string VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // It's OK to change the value of VERSION here
    const string VERSION = "PHP 8.3";

    // Illegal:
    // Type must be declared if it was specified in the base class
    const VERSION = "PHP 8.3";

    // Illegal:
    // In this case, we can't change the type declared in the 
    // base class, even if the new type and its value are compatible.
    const float VERSION = 8.3;
}

Имейте в виду, что тип, назначенный константе класса, может меняться при «сужении» нескольких типов или использовании другого совместимого типа:

class ConstTest {
    const string|float VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // Here, it's OK to narrow the type declaration to string or float
    const string VERSION = "PHP 8.3";
    const float VERSION = 8.3;

    // Legal:
    // Value could be an int, but it's compatible with float 
    const float VERSION = 8;

    // Illegal:
    // We can't widen the type options here to include int
    const string|float|int VERSION = 8;
}

Для других свойств при проверке возвращаемых значений поддерживаются два типа: void и never — не поддерживаются как типы констант класса.

Новый json_validate() Функция

При работе с данными в формате JSON полезно знать, является ли полезная нагрузка синтаксически допустимой, прежде чем пытаться что-то с ней сделать.

В предыдущих выпусках PHP разработчики использовали json_decode() функцию и проверяется на наличие ошибок, пока эта функция пытается преобразовать данные JSON в ассоциативные массивы или объекты. Новое в PHP 8.3 json_validate() Функция выполняет проверку ошибок, не используя всю память, необходимую для построения этих структур массива или объектов.

Итак, раньше вы могли проверять полезную нагрузку JSON примерно так:

$obj = json_decode($maybeJSON);

if (json_last_error() === JSON_ERROR_NONE) {
    // Probably do something with $obj   
}

Если вы не собираетесь что-то делать прямо сейчас с $obj в приведенном выше примере много ресурсов используется только для подтверждения достоверности исходной полезной нагрузки JSON. В PHP 8.3 вы можете сделать что-то вроде этого и сэкономить немного памяти:

if (json_validate($maybeJSON)) {
    // Do something with $maybeJSON   
}

Примечание: Нет особого смысла использовать json_validate() а затем немедленно пропустить данные через json_decode(), в любом случае используя ресурсы памяти декодирования. Вы, скорее всего, будете использовать новую функцию для проверки JSON перед его сохранением где-либо или доставкой в ​​качестве ответа на запрос.

Глубокое клонирование readonly Характеристики

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

RFC для изменения readonly поведение выдвинуло два предложения:

  1. Разрешить занятия, которые не readonly расширять классы, которые
  2. Позволять readonly свойства, которые необходимо повторно инициализировать при клонировании

Это второе предложение, вошедшее в PHP 8.3. Новый подход позволяет создавать экземпляры класса с readonly свойства, подлежащие повторной инициализации в __clone магический метод (в том числе с помощью функций, вызываемых изнутри __clone).

Этот пример кода из RFC показывает, как это работает:

class Foo {
    public function __construct(
        public readonly DateTime $bar,
        public readonly DateTime $baz
    ) {}
 
    public function __clone() {
        // $bar will get a new DateTime when clone is invoked
        $this->bar = clone $this->bar; 

        // And this function will be called
        $this->cloneBaz();
    }
 
    private function cloneBaz() {
       // This is legal when called from within __clone
        unset($this->baz); 
    }
}
 
$foo = new Foo(new DateTime(), new DateTime());
$foo2 = clone $foo;

Новый #[\Override] Атрибут

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

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

PHP 8.3 представляет #[\Override] Атрибут, помогающий программистам дать понять, что метод должен иметь какое-то происхождение в коде.

Вот базовый пример:

class A {
    protected function ovrTest(): void {}
}

// This will work because ovrTest() 
// can be found in the parent class
class B extends A {
    #[\Override]
    public function ovrTest(): void {}
}

// This will fail because ovrBest() 
// (probably a typo) is not in the parent
class C extends A {
    #[\Override]
    public function ovrBest(): void {}
}

Динамическая выборка констант класса и членов перечисления

В отличие от других свойств в коде PHP, получение констант классов и членов Enum с именами переменных было немного запутанным. До PHP 8.3 вы могли сделать это с помощью constant() функционируйте следующим образом:

class MyClass {
    public const THE_CONST = 9;
}

enum MyEnum: int {
    case FirstMember = 9;
    case SecondMember = 10;
}

$constantName="THE_CONST";
$memberName="FirstMember";

echo constant('MyClass::' . $constantName);
echo constant('MyEnum::' . $memberName)->value;

Теперь, используя те же определения класса и Enum, что и выше, вы можете добиться того же результата с помощью динамической выборки констант PHP 8.3 следующим образом:

$constantName="THE_CONST";
$memberName="FirstMember";

echo MyClass::{$constantName};
echo MyEnum::{$memberName}->value;

Новый getBytesFromString() Метод

Вы когда-нибудь хотели генерировать случайные строки, используя заранее утвержденный набор символов? Теперь вы можете легко это сделать с помощью getBytesFromString() метод, который был добавлен в расширение Random в PHP 8.3.

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

Вот простой пример:

$rando = new Random\Randomizer();
$alpha="ABCDEFGHJKMNPQRSTVWXYZ";

$rando->getBytesFromString($alpha, 6); //  "MBXGWL"
$rando->getBytesFromString($alpha, 6); //  "LESPMG"
$rando->getBytesFromString($alpha, 6); //  "NVHWXC"

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

$rando = new Random\Randomizer();
$nums="123456";

$rando->getBytesFromString($nums, 10); //  "2526341615"

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

$rando = new Random\Randomizer();
$weighted = 'AAAAA12345';

$rando->getBytesFromString($weighted, 5); //  "1AA53"
$rando->getBytesFromString($weighted, 10); //  "42A5A1AA3A"

Новый getFloat() и nextFloat() Методы

В PHP 8.3, расширяя расширение Random, представлены два новых метода для генерации случайных значений с плавающей запятой: getFloat() и nextFloat().

Вот один пример:

$rando = new Random\Randomizer();

// Generate a float value between a minimum 
//  value of 0 and a maximum value of 5
$rando->getFloat(0,5); // 2.3937446906217

getFloat() метод также принимает третий параметр после минимального и максимального значений. Используя Случайный\ИнтервалГраница Enum там может определить, могут ли сами значения min и max быть возвращены функцией.

Вот правила:

  • IntervalBoundary::ClosedOpen: может быть возвращено минимальное значение, но не максимальное.
  • IntervalBoundary::ClosedClosed: могут быть возвращены как минимум, так и максимум.
  • IntervalBoundary::OpenClosed: min не может быть возвращено, max может быть возвращено
  • IntervalBoundary::OpenOpen: не может быть возвращено ни минимальное, ни максимальное значение.

Когда используешь getFloat() без указания Enum в качестве третьего параметра, значением по умолчанию является Интервалбаундари::ClosedOpen.

Полезный пример, предоставленный в документации для новой функции, генерирует случайные координаты долготы и широты, где широта может включать -90 и 90, но долгота не может включать одновременно -180 и 180 (поскольку они одинаковы):

$rando = new Random\Randomizer();

printf(
    "Lat: %+.6f Long: %+.6f",
    $rando->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed),

    // -180 will not be used 
    $rando->getFloat(-180, 180, \Random\IntervalBoundary::OpenClosed),
);

Новый nextFloat() метод по сути аналогичен использованию getFloat() чтобы запросить случайное значение в диапазоне от 0 до меньше 1:

$rando = new Random\Randomizer();

$rando->nextFloat(); // 0.3767414902847

Другие незначительные изменения в PHP 8.3

PHP 8.3 также включает ряд других новых функций и незначительных изменений. Мы упомянем их ниже со ссылками на дополнительные ресурсы (если таковые имеются):

  • Новые методы класса DOMElement: DOMElement::getAttributeNames(), DOMElement::insertAdjacentElement(), DOMElement::insertAdjacentText(), DOMElement::toggleAttribute(), DOMNode::contains(), DOMNode::getRootNode(), DOMNode::isEqualNode()DOMNameSpaceNode::contains() и MParentNode::replaceChildren().
  • Новые методы класса IntlCalendar: IntlCalendar::setDate(), IntlCalendar::setDateTime(), IntlGregorianCalendar::createFromDate()и IntlGregorianCalendar::createFromDateTime().
  • Новые функции LDAP: ldap_connect_wallet() и ldap_exop_sync().
  • Новый mb_str_pad() многобайтовая строковая функция.
  • Новые функции POSIX: posix_sysconf(), posix_pathconf(), posix_fpathconf()и posix_eaccess().
  • Новый ReflectionMethod::createFromMethodName() метод.
  • Новая функция сокета: socket_atmark().
  • Новые строковые функции: str_increment(), str_decrement()и stream_context_set_options().
  • Новый метод класса ZipArchive: ZipArchive::getArchiveFlag().
  • Новая настройка INI для установки максимально допустимого размера стека: zend.max_allowed_stack_size.

Устаревшие версии PHP 8.3

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

Вот список устаревших возможностей PHP 8.3 со ссылками на дополнительную информацию:

  • Константа U_MULTIPLE_DECIMAL_SEPERATORS устарела в пользу U_MULTIPLE_DECIMAL_SEPARATORS.
  • 3MT_RAND_PHP Вариант Mt19937 устарел.
  • ReflectionClass::getStaticProperties() больше не является нулевым.
  • INI-настройки assert.active, assert.bail, assert.callback, assert.exceptionи assert.warning устарели.
  • Вызов get_class() и get_parent_class() без аргументов устарели.

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

Мы рассмотрели существенные изменения, внесенные в PHP 8.3. Подробный список всех обновлений этой версии можно найти в официальном журнале изменений этого выпуска. Если вы планируете перенести свой код на платформу с последней версией PHP, Руководство по миграции с 8.2 на 8.3 может помочь вам избежать проблем.

Если вам предстоит установить PHP на серверы разработки или производства, версия 8.3 уже готова к загрузке.

Если вы являетесь клиентом Kinsta, вы можете рассчитывать на то, что мы сделаем этот выпуск доступным на серверах ваших проектов управляемого хостинга WordPress или хостинга приложений.

Стив Бонистил

Кинста

Стив Бонистил — технический редактор Kinsta, который начал свою писательскую карьеру в качестве печатного журналиста, гоняясь за машинами скорой помощи и пожарными машинами. Он освещает технологии, связанные с Интернетом, с конца 1990-х годов.