PHP 8.3 буде випущено 23 листопада 2023 року; у ньому покращено доступні тільки для читання класи, нову функціюjson_validate()
, доповнення до нещодавно доданого класуRandomizer
, виявлено переповнення стеку та багато іншого.У цій статті ми розглянемо всі можливості, покращення продуктивності, зміни та застарілі версії один за одним.
Поправки readonly властивостей
У цьому RFC було запропоновано дві зміни, з яких прийнято лише одну: можливість повторної ініціалізації властивостей, доступних лише для читання, під час клонування. Це може здатися великим кроком, але цей RFC стосується лише дуже специфічного (але важливого) граничного випадку: перезапису значень властивостей у __clone()
, щоб дозволити глибоке клонування властивостей, доступних лише для читання.
readonly class Post
{
public function __construct(
public DateTime $createdAt,
) {}
public function __clone()
{
$this->createdAt = new DateTime();
// This is allowed,
// even though `createdAt` is a readonly property.
}
}
Типізовані константи класу
Тепер ви можете типізувати константи класу:
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; // The overridden method
}
}
Тепер уявімо, що в якийсь момент батьківський метод змінює ім'я свого методу:
abstract class Parent
{
public function methodWithNewImplementation(): int
{
return 1;
}
}
Завдяки атрибуту #[Override]
PHP зможе виявити, що Child::methodWithDefaultImplementation()
більше нічого не перевизначає, і видасть помилку.
Від'ємні індекси в масивах
Якщо у вас порожній масив, додайте елемент з від'ємним індексом, а потім додайте ще один елемент, цей другий елемент завжди буде починатися з індексом 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. Це виправлено у PHP 8.3:
$class = new readonly class {
public function __construct(
public string $foo = 'bar',
) {}
};
Нова функція json_validate()
Раніше єдиним способом перевірити, чи є рядок коректним JSON, було декодувати його і перевірити, чи не було згенеровано жодних помилок. Ця нова функція json_validate()
є корисною, якщо вам потрібно лише дізнатися, чи є вхідний рядок коректним JSON, оскільки вона використовує менше пам'яті порівняно з декодуванням рядка.
json_validate(string $json, int $depth = 512, int $flags = 0): bool
Доповнення класу Randomizer
У PHP 8.2 додано новий клас Randomizer. Це оновлення приносить кілька незначних доповнень:
Randomizer::getBytesFromString(string $string, int $length): string
Цей метод дозволяє згенерувати рядок заданої довжини, який складається з випадково обраних байт із заданого рядка.
Randomizer::getFloat(
float $min,
float $max,
IntervalBoundary $boundary = IntervalBoundary::ClosedOpen
): float
getFloat()
повертає float
зі значенням між $min
і $max
. Ви можете визначити, чи потрібно включати значення $min
і $max
, за допомогою IntervalBoundary enum. Закрите значення означає, що значення включено, а відкрите - виключено.
Randomizer::nextFloat(): float {}
Нарешті, nextFloat()
- це скорочення від getFloat(0, 1, IntervalBoundary::ClosedOpen)
, іншими словами: ця функція повертає випадкове число з проміжком від 0 до 1, де 1 не включається.
Динамічий доступ до констант класу
PHP 8.3 дозволяє отримувати доступ до констант за допомогою більш динамічного синтаксису:
class Foo
{
const BAR = 'bar';
}
$name = 'BAR';
// Instead of this:
constant(Foo::class . '::' . $name);
// You can now do this:
Foo::{$name};
Винятки щодо дати/часу, які є доцільнішими
У багатьох випадках PHP просто згенерує виключення або помилку; або видасть попередження або помилку, коли щось піде не так у роботі з датами та часом. У цьому документі RFC розглядаються всі ці крайні випадки та додаються відповідні, спеціальні Exception
для них.
Тепер у нас є такі Exception
, як DateMalformedIntervalStringException
, DateInvalidOperationException
і DateRangeError
.
Загалом, ці доповнення не зламають жодного коду, оскільки ці нові винятки та помилки є підкласами загальних класів Exception
та Error
. Однак, є три невеликі зміни, які призводять до порушень, що вносяться з цим RFC:
- Якщо Епоха не вписується в ціле число PHP, тепер повертає нову
DateRangeError
замість типовоїValueError
, яка не є його підкласом. Це проблема тільки для 32-бітних платформ. - Єдині неспеціальні специфікації відносного часу підтримуються для попередження про віднімання за допомогою
DateTime::sub()
, аdate_sub()
стає новимDateInvalidOperationException
. - Невідомий або неправильний формат (
%s
) у позиції%d
(%c
):%s
і String%s
містить не релятивні елементи попередження, які створюються при розборі неправильних/пошкодженихDateInterval
рядків, тепер генеруватимуть новеDateMalformedIntervalStringException
при використанні з інтерфейсом OO, замість того, щоб показувати попередження і повертатиfalse
.
Покращено обробку помилок у unserialize()
unserialize()
тепер завжди видаватиме E_WARNING
при виникненні проблем замість E_NOTICE
у деяких випадках.
У цьому RFC також пропонувалося додати більше винятків при виконанні unserialize()
, але цю частину не було прийнято.
Зміни у функції range()
Зі списку змін:
- Тепер генерується помилка типу
TypeError
у разі передачі об'єктів, ресурсів або масивів як граничних входів - Більш зрозуміле повідомлення про помилку
ValueError
з'являється у разі передачі0
для$step
- Тепер генерується
ValueError
при використанні від'ємного$step
для збільшення діапазонів - Якщо
$step
є числом з плаваючою комою тепер це може бути інтерпретовано якint
- Тепер генерується
ValueError
, якщо будь-який аргумент дорівнює нескінченності абоNAN
- Тепер видається
E_WARNING
, якщо$start
або$end
є порожнім рядком. Значення продовжує приводитися до значення0
. -
E_WARNING
тепер видається, якщо$start
або$end
містить більше одного байта, тільки якщо це нечисловий рядок. -
E_WARNING
тепер видається, якщо$start
або$end
приводиться до цілого числа, тому що інший граничний вхід є числом. (наприклад,range(5, 'z');
) - Тепер видається
E_WARNING
, якщо$step
єfloat
при спробі згенерувати діапазон символів, за винятком випадків, коли обидва граничні входи є числовими рядками (наприклад,range('5', '9', 0.5);
не видає попередження) -
range()
тепер генерує список символів, якщо один з граничних входів є рядковою цифрою, замість приведення іншого входу до типуint
(наприклад,range('5', 'z');
)
Трейти та статичні властивості
Важливо зазначити, що це означає внесення змін з порушенням цілісності:
Використання трейтів зі статичними властивостями тепер перевизначатиме статичні властивості, успадковані від батьківського класу. Це створить окреме сховище статичних властивостей для поточного класу. Це аналогічно додаванню статичної властивості безпосередньо до класу без використання трейтів.
Виявлення переповнення стеку
У PHP 8.3 додано дві нові ini-директиви zend.max_allowed_stack_size
та zend.reserved_stack_size
. Програми, які близькі до переповнення стеку викликів, тепер можуть згенерувати помилку при використанні більше, ніж різниця між zend.max_allowed_stack_size
і zend.reserved_stack_size
.
Перевагою цієї можливості є те, що помилки сегментації, спричинені переповненням стеку, більше не призводитимуть до сегментних помилок, що значно спрощує налагодження.
За замовчуванням zend.max_allowed_stack_size
дорівнює 0
, тобто PHP автоматично визначить значення. Ви також можете вказати -1
, щоб вказати відсутність обмеження або конкретну кількість байт. Директива zend.reserved_stack_size
використовується для визначення "буферної зони", щоб PHP міг видавати помилку замість того, щоб фактично вичерпувати пам'ять. Значення тут повинно бути вказано у байтах, але PHP визначить для вас розумне значення за замовчуванням, тому вам не обов'язково встановлювати його, якщо тільки ви не стикаєтесь з граничними випадками для конкретних програм.
Наостанок, для волокон, наявна директива 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()
.
The function looks like this:
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, який оголошує купу невеликих змін застарілими. Майте на увазі, що застарілі функції не є помилками, і це, як правило, добре для мови, щоб рухатися вперед. Ось ці вилучення, які пройшли, ви можете прочитати докладніше про них у RFC:
- Вилучено передачу від'ємних
$widths
уmb_strimwidth()
- Вилучено константу
NumberFormatter::TYPE_CURRENCY
- Застарілу та вилучено несправну реалізацію до PHP 7.1 Mt19937 (
MT_RAND_PHP
) - Вилучено виклик
ldap_connect()
з 2 параметрами$host
та$port
- Вилучено залишки коду, що обчислює рядки
Невеликі, але помітні зміни
Не кожна зміна в 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-системах. - Видалено директиву
opcache.consistency_checks
ini - Покращено
array_sum()
таarray_product()
Наразі це все, але з часом цей список буде розширюватися.
Ще немає коментарів