PHP 8.3 что нового. Изменения и новый функционал.
PHP — проект с открытым исходным кодом. Узнать то, что будет включено в новую версию занимает всего минуту поиска. Например, на этой странице перечислены все принятые RFC для PHP 8.3.
Ниже вы найдёте краткий список нововведений с примерами кода.
Когда выйдет PHP 8.3?
Согласно шестимесячной фазы пре-релизов, PHP 8.3 вышел 23 ноября 2023 года после трёх альфа-релизов, трёх бета-версий и шести релиз-кандидатов.
Дата | Релиз |
---|---|
8 Июня, 2023 | Первый альфа-релиз |
22 Июня, 2023 | Второй альфа-релиз |
6 Июля, 2023 | Третий альфа-релиз |
18 Июля, 2023 | Заморозка функций |
20 Июля, 2023 | Первый бета-релиз |
3 Августа, 2023 | Второй бета-релиз |
17 Августа, 2023 | Третий бета-релиз |
31 Августа, 2023 | Первый релиз-кандидат |
14 Сентября, 2023 | Второй релиз-кандидат |
28 Сентября, 2023 | Третий релиз-кандидат |
12 Октября, 2023 | Четвёртый релиз-кандидат |
26 Октября, 2023 | Пятый релиз-кандидат |
9 Ноября, 2023 | Шестой релиз-кандидат |
23 Ноября, 2023 | GA |
Новые возможности PHP 8.3
json_validate()
Ранее единственным способом проверить, является ли строка валидной JSON строкой, было её декодирование и определение наличия ошибок. Эта новая функция json_validate()
удобна, если вам нужно только знать, является полученная JSON строка валидной, поскольку она использует меньше памяти по сравнению с декодированием строки.
json_validate(string $json, int $depth = 512, int $flags = 0): bool
Пример использования json_validate
:
json_validate('{ "foo": "bar", }');
// Syntax error
echo json_last_error_msg();
Как видите, json_validate()
возвращает логическое значение, и вы можете получить сообщение об ошибке с помощью json_last_error()
или json_last_error_msg()
.
Узнать больше можно в статье PHP 8.3: Добавлена функция json_validate
gc_status
возвращает дополнительную информацию
В PHP 8.3 функция gc_status()
возвращающая информацию о GC будет возвращать четыре новых поля с дополнительной информацией. В массив с возвращаемой информацией добавлены поля: running
, protected
, full
и buffer_size
.
var_dump(gc_status());
Узнать больше можно в статье PHP 8.3: gc_status
дополнительная информацию о GC
Изменения в PHP 8.3
Правки Readonly
В этом RFC было предложено два изменения, но приняли только одно: возможность повторной инициализации свойств readonly
во время клонирования. Это может показать очень важным, но этот RFC касается только весьма конкретного (не важного) пограничного случая: перезапись значения свойств в __clone()
, чтобы разрешить глубокое копирование readonly
свойств.
readonly class Post
{
public function __construct(
public DateTime $createdAt,
) {}
public function __clone()
{
$this->createdAt = new DateTime();
// Это разрешено,
// несмотря то, что `createdAt` является readonly свойством.
}
}
Типизированные константы класса
Теперь вы можете указывать тип константы класса:
class Foo
{
const string BAR = 'baz';
}
Атрибут #[Override]
Новый атрибут #[Override]
используется для отображения намерений программиста. В основном это говорит: Я знаю, что этот метод переопределяет родительский метод. Если это когда-нибудь изменится, пожалуйста, дайте мне знать.
Пример:
abstract class Parent
{
public function methodWithDefaultImplementation(): int
{
return 1;
}
}
final class Child extends Parent
{
#[Override]
public function methodWithDefaultImplementation(): void
{
return 2; // Переопределённый метод
}
}
Теперь представьте, что в какой-то момент родительский метод меняет имя своего метода:
abstract class Parent
{
public function methodWithNewImplementation(): int
{
return 1;
}
}
Благодаря атрибуту #[Override]
PHP сможет определить, что Child::methodWithDefaultImplementation()
больше ничего не переопределяет и выдаст ошибку.
Больше об атрибуте #[Override]
вы можете прочитать в статье #[Override] в PHP 8.3.
Отрицательные индексы в массивах
Если у вас есть пустой массив, добавьте элемент с отрицательным индексом, а затем ещё один элемент, этот второй элемент всегда будет начинаться с индекса 0
:
$array = [];
$array[-5] = 'a';
$array[] = 'b';
var_export($array);
//array (
// -5 => 'a',
// 0 => 'b',
//)
Начиная с PHP 8.3, следующий элемент будет добавлен с индексом -4
:
//array (
// -5 => 'a',
// -4 => 'b',
//)
Анонимные readonly
классы
Ранее нельзя было пометить анонимные классы, как readonly
. Это исправлено в PHP 8.3:
$class = new readonly class {
public function __construct(
public string $foo = 'bar',
) {}
};
unserialize()
обновление ошибки E_NOTICE до E_WARNING
До версии PHP 8.3 при передаче недопустимой строки функции unserialize()
в некоторых случаях, например при синтаксической ошибке в сериализованной строке выдавалось PHP уведомление (E_NOTICE
). Начиная с версии PHP 8.3 и в более поздних версиях это было изменено на выдачу предупреждений (E_WARNING
). Кроме того, некоторые условия функции serialize()
изменены, чтобы тоже выдавать E_WARNING
unserialize("invalid-string");
- PHP Notice: unserialize(): Error at offset 0 of 14 bytes
+ PHP Warning: unserialize(): Error at offset 0 of 14 bytes
В PHP 8.0 уровень сообщений об ошибках PHP по умолчанию был изменён на E_ALL
. Если значение error_reporting
не было изменено в пользовательском INI-файле, это не должно привести к возникновению каких-либо новых ошибок, кроме изменения уровня серьёзности.
Узнать больше можно в статье PHP 8.3: unserialize()
обновление ошибки E_NOTICE до E_WARNING
Дополнения Randomizer
В PHP 8.2 добавлен новый класс Randomizer
. Это обновление содержит несколько небольших дополнений:
Randomizer::getBytesFromString(string $string, int $length): string
Этот метод позволяет сгенерировать строку заданной длины, состоящую из случайно выбранных байтов из заданной строки.
Randomizer::getFloat(
float $min,
float $max,
IntervalBoundary $boundary = IntervalBoundary::ClosedOpen
): float
getFloat()
возвращает число с плавающей точкой между $min
и $max
. Вы можете определить, следует ли включать $min
и $max
благодаря перечислению IntervalBoundary
. Closed
означает, что значение включено, а Open
означает, что оно исключено.
Randomizer::nextFloat(): float {}
Наконец, nextFloat()
является сокращением для getFloat(0, 1, IntervalBoundary::ClosedOpen)
, другими словами оно даст вам случайное число с плавающей точкой между 0
и 1
, где 1
исключается.
Суммируя:
final class Randomizer {
public function getBytesFromString(string $string, int $length) : string {}
public function nextFloat() : float {}
public function getFloat(float $min, float $max, IntervalBoundary $boundary = IntervalBoundary::ClosedOpen) : float {}
}
enum IntervalBoundary
{
case ClosedOpen;
case ClosedClosed;
case OpenClosed;
case OpenOpen;
}
Список изменений:
- Переименован
getBytesFromAlphabet
вgetBytesFromString
, добавлено перечислениеGetFloatBounds
- Переименован
GetFloatBounds
вIntervalBoundary
. - Переименован третий параметр
getFloat()
с$bounds
в$boundary
, чтобы он соответствовал именам перечисления.
Узнать подробности можно в статье PHP 8.3: Дополнение Randomizer
Извлечение констант динамического класса
PHP 8.3 позволяет получать константы с более динамическим синтаксисом:
class Foo
{
const BAR = 'bar';
}
$name = 'BAR';
// Вместо этого:
constant(Foo::class . '::' . $name);
// Вы можете сделать так:
Foo::{$name};
Более подходящие исключения Date/Time
Во многих случаях PHP просто генерирует объект Exception
или Error
, или выдаёт предупреждение или ошибку, когда что-то пошло не так при работе с датами и временем. Этот RFC проходит через все эти крайности и добавляет для них соответствующие специальные исключения.
Теперь у нас есть такие исключения, как DateMalformedIntervalStringException
, DateInvalidOperationException
и DateRangeError
.
Как правило, эти дополнения не нарушают код, поскольку эти недавно добавленные исключения и ошибки являются подклассами общих классов Exception
и Error
. Однако, в этом RFC есть три небольших критических изменения:
Epoch doesn't fit in a PHP integer
, теперь возвращает новыйDateRangeError
вместо универсальногоValueError
, который не является подклассом. Это проблема только для 32-битных платформ.- Предупреждение
Only non-special relative time specifications are supported for subtraction
дляDateTime::sub()
иdate_sub()
становится новымDateInvalidOperationException
. - Предупреждения
Unknown or bad format (%s) at position %d (%c): %s
иString '%s' contains non-relative elements
создаваемые при анализе неправильных/неверных строкDateInterval
, теперь будут вызывать новое исключениеDateMalformedIntervalStringException
при использовании с OO Интерфейсом вместо отображения предупреждения о возвратаfalse
.
Изменения в функции range()
Из changelog:
- Теперь при передаче объектов, ресурсов или массивов в качестве граничных входных данных выдаётся ошибка
TypeError
. - При передаче
0
для$step
выдаётся более описательная ошибкаValueError
. - Теперь при использовании отрицательного
$step
для увеличения диапазона выдаётся ошибкаValueError
. - Если
$step
представляет собой число с плавающей запятой, которое можно интерпретировать как целое число, то теперь это делается так. - Теперь выдаётся
ValueError
, если какой-либо аргумент равенinfinity
илиNAN
. E_WARNING
теперь генерируется, если$start
или$end
являются пустой строкой. Значение продолжает преобразовываться в значение0
.E_WARNING
теперь генерируется, если$start
или$end
имеют более одного байта, только если это нечисловая строка.E_WARNING
теперь генерируется, если$start
или$end
приводится к целому числу, потому что другим граничным вводом является число. (Например,range(5, 'z');
)E_WARNING
теперь генерируется, если$step
является числом с плавающей запятой при попытке сгенерировать диапазон символов, за исключением случаев, когда оба граничных вывода является числовыми строками (напримерrange('5', '9', 0.5);
не выдаёт предупреждения)range()
теперь создаёт список символов, если один из граничных входных данных является строковой цифрой, вместо приведения другого ввод к типуint
(например,range('5', 'z')
)
Трейты и статические свойства
Из changelog:
Использование трейтов со статическими свойствами теперь будет повторно объявлять статические свойства, унаследованные от родительского класса. Это создаст отдельное хранилище статических свойств для текущего класса. Это аналогично добавлению статического свойства в класс напрямую без трейтов.
Обнаружение переполнения стека
В PHP 8.3 добавлены две новые ini
директивы с именами zend.max_allowed_stack_size
и zend.reserved_stack_size
. Программы, близкие к переполнению стека вызовов, теперь смогут выдавать Error
при использовании большего, чем разница между zend.max_allowed_stack_size
and zend.reserved_stack_size
.
Преимущество этой функции заключается в том, что ошибки сегментации, вызванные переполнением стека, больше не будут приводить к ошибкам сегментации, что значительно упрощает отладку.
Значение по умолчанию для zend.max_allowed_stack_size
равно 0
, что означает, что PHP автоматически определит значение. Вы также можете указать -1
, чтобы указать, что нет ограничения или определённого количества байт. Директива zend.reserved_stack_size
используется для определения буферной зоны
, так что PHP может по-прежнему выдавать ошибку вместо фактического исчерпания памяти. Её значение должно быть числом байт, но PHP сам определит для вас разумное значение по умолчанию, поэтому не обязательно устанавливать его, если только вы не сталкиваетесь с пограничными случаями для конкретных программ.
В заключение отметим, что для fiber в качестве максимально допустимого размера стека используется существующая директива fiber.stack_size
.
zend.max_allowed_stack_size=128K
Новая функция mb_str_pad
Из RFC:
В PHP различные строковые функции доступны в двух вариантах: одна для байтовых строк, другая - для многобайтовых. Однако среди многобайтовых строковых функций заметно отсутствие mbstring-эквивалента функции
str_pad()
. В функцииstr_pad()
отсутствует поддержка многобайтовых символов, что вызывает проблемы при работе с языками, использующими многобайтовые кодировки, например UTF-8. В данном RFC предлагается добавить такую функцию в PHP, которую мы назовёмmb_str_pad()
.
Функция выглядит следующим образом:
function mb_str_pad(
string $string,
int $length,
string $pad_string = " ",
int $pad_type = STR_PAD_RIGHT,
?string $encoding = null,
): string {}
Магические замыкания методов и именованные аргументы
Допустим, у вас есть класс, поддерживающий магические методы:
class Test {
public function __call($name, $args)
{
var_dump($name, $args);
}
public static function __callStatic($name, $args) {
var_dump($name, $args);
}
}
PHP 8.3 позволяет создавать замыкания из этих методов, а затем передавать именованные аргументы этим замыканиям. Ранее это было невозможно.
$test = new Test();
$closure = $test->magic(...);
$closure(a: 'hello', b: 'world');
Инвариантная видимость констант
Ранее видимость констант не проверялась при реализации интерфейса. В PHP 8.3 эта ошибка исправлена, но в некоторых местах она может привести к поломке кода, если вы не знали о таком поведении.
interface I {
public const FOO = 'foo';
}
class C implements I {
private const FOO = 'foo'; // Ошибка
}
Небольшие устаревания RFC
Как обычно бывает с каждым выпуском, в нем есть один RFC, который добавляет кучу мелких устареваний/deprecations. Следует помнить, что deprecations — это не ошибки, и в целом они являются положительным моментом для развития языка. Вот те deprecations, которые были внесены, более подробно о них можно прочитать в RFC:
- Объявлена устаревшей передача отрицательных значений
$widths
в функциюmb_strimwidth()
- Объявлена устаревшей и удалена константа
NumberFormatter::TYPE_CURRENCY
- Объявлена устаревшей и удалена неработающая реализация Mt19937, существовавшая до выхода версии PHP 7.1 (
MT_RAND_PHP
) - Объявлена устаревшей и удалена функция вызова
ldap_connect()
с двумя параметрами$host
и$port
- Объявлены устаревшими остатки строковых вычисляемых утверждений кода
Небольшие, но заметные изменения в PHP 8.3
Не каждое изменение в PHP проходит процесс RFC. На самом деле, большинство изменений включают поддержку и исправление ошибок и не требуют RFC. Все эти изменения перечислены в UPGRADING
. Я перечислю некоторые из самых известных, но вам обязательно следует прочитать весь список, если хотите знать о мельчайших деталях.
- При использовании FFI, функции C возвращающие тип
void
, теперь возвращаютnull
вместо возвратаFFI\CData:void
. posix_getrlimit()
теперь принимает необязательный параметр$res
, чтобы разрешить выборку одного лимита ресурсов.gc_status()
имеет четыре новых поля:running
,protected
,full
иbuffer_size
.class_alias()
теперь поддерживает создание псевдонима внутреннего класса.mysqli_poll()
теперь вызываетValueError
при передаче аргументовread
илиerror
.array_pad()
теперь ограничен только максимальным количеством аргументов, которые может иметь массив. Раньше можно было добавить не более1048576
элементов за раз.- Новые функции POSIX:
posix_sysconf()
,posix_pathconf()
,posix_fpathconf()
, иposix_eaccess()
. - Многократное выполнение
proc_get_status()
теперь всегда будет возвращать правильное значение в posix-системах. - ini директива
opcache.consistency_checks
была удалена - Улучшены
array_sum()
иarray_product()