PHP 8.3 вышел 23 ноября 2023 г. В нем есть улучшения классов только для чтения, новая функция json_validate()
, дополнения к недавно добавленному классу Randomizer, обнаружение переполнения стека и многое другое.
В этой статье мы рассмотрим все функции, улучшения производительности и изменения.
Изменения только для чтения
В этом RFC предлагалось два изменения, но было принято только одно: возможность повторной инициализации свойств только для чтения во время клонирования. Этот RFC рассматривает только очень специфический (но важный) крайний случай: перезапись значений свойств внутри __clone()
, чтобы обеспечить глубокое клонирование свойств, доступных только для чтения.
readonly class Post
{
public function __construct(
public DateTime $createdAt,
) {}
public function __clone()
{
$this->createdAt = new DateTime();
// Это допустимо,
// даже если `createdAt` — свойство только для чтения.
}
}
Подробную публикацию об этом RFC и примечания можно прочитать здесь .
Типизированные константы класса
Теперь вы можете указывать тип константы класса:
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(): int
{
return 2; // Переопределяющий метод
}
}
Теперь давайте представим, что в какой-то момент родительский метод меняет имя своего метода:
abstract class Parent
{
public function methodWithNewImplementation(): int
{
return 1;
}
}
Благодаря этому атрибуту PHP сможет обнаружить, что метод больше ничего не переопределяет, и выдаст ошибку.
#[Override]Child::methodWithDefaultImplementation()
Подробнее об #[Override]-атрибуте можно прочитать здесь .
Отрицательные индексы в массивах
Ранее, если у вас был пустой массив, и вы добавляли в него сначала один элемент с отрицательным индексом, а затем еще один элемент, второй всегда начинался с индекса 0:
$array = [];
$array[-5] = 'a';
$array[] = 'b';
var_export($array);
//array (
// -5 => 'a',
// 0 => 'b',
//)
Начиная с PHP 8.3, по индексу будет добавлен следующий элемент -4:
//array (
// -5 => 'a',
// -4 => 'b',
//)
Анонимные классы только для чтения
В этой версии языка можно создавать анонимные классы доступные только для чтения:
$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::Closed|Open
): float
getFloat()
возвращает число с плавающей запятой между $min
и $max
. Вы можете определить, следует ли включать $min
и $max
значения, благодаря перечислению IntervalBoundary. Closed означает, что значение включено, а Open означает исключено.
Randomizer::nextFloat(): float {}
Наконец, nextFloat()
: оно даст вам случайное число с плавающей запятой между 0 и 1, где 1 исключено.
getFloat(0, 1, IntervalBoundary::Closed|Open)
Извлечение констант динамического класса
PHP 8.3 позволяет получать константы с более лаконичным синтаксисом:
class Foo
{
const BAR = 'bar';
}
$name = 'BAR';
// Вместо этого:
constant(Foo::class . '::' . $name);
// Вы можете делать так:
Foo::{$name};
Больше подходящих исключений по дате/времени
Во многих случаях 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
при использовании с объектно-ориентированным интерфейсом вместо отображения предупреждения и возврата false.
Улучшенная обработка ошибок unserialize()
unserialize()
теперь всегда будет выдавать E_WARNING при возникновении проблем вместо E_NOTICE.
В этом RFC также предлагалось добавить больше исключений при запуске unserialize()
, но эта часть не была принята.
Изменения в range() функции
Из журнала изменений:
-
TypeError
теперь выдается при передаче объектов, ресурсов или массивов в качестве граничных входных данных. -
Более подробное описание
ValueError
выдается при передаче 0 для аргумента$step
-
ValueError
теперь выбрасывается при использовании отрицательного значения$step
для увеличения диапазона. Так же оно выбрасывается, если$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');)
Трейты и статические свойства
Из журнала изменений:
Использование трейтов со статическими свойствами теперь будет переопределять статические свойства, унаследованные от родительского класса. Это создаст отдельное хранилище статических свойств для текущего класса. Это аналогично добавлению статического свойства в класс напрямую, без трейтов.
Обнаружение переполнения стека
В PHP 8.3 добавлены две новые директивы ini: zend.max_allowed_stack_size
и zend.reserved_stack_size
. Приложения, которые близки к переполнению стека вызовов, теперь могут выдавать ошибку Error при использовании разницы между 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()
.
Функция выглядит следующим образом:
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
-
Устарела и удалена неработающая реализация Mt19937 до PHP 7.1 (MT_RAND_PHP)
-
Устарел и удалён вызов
ldap_connect()
с двумя параметрами$host
и$port
-
Не рекомендуется использовать проверку утверждений (
assert()
) для (вычисляемых) строк
Небольшие, но важные изменения
Не каждое изменение в PHP проходит процесс RFC. Фактически, большинство изменений включают обслуживание и исправление ошибок и не требуют RFC. Все эти изменения перечислены в документе обновления. Я перечислю некоторые из наиболее важные, но вам рекомендуется прочитать весь список, если вы хотите узнать все детали.
-
При использовании FFI, C-функции, которые возвращали void, теперь, возвращают null вместо FFI\CData:void
-
posix_getrlimit()
теперь принимает необязательный$res
-параметр, позволяющий получить ограничение на один ресурс. -
gc_status()
имеет четыре новых поля: running, protected, full и buffer_size. -
class_alias()
теперь поддерживает создание псевдонима внутреннего класса. -
mysqli_poll()
теперь вызывает ошибку ValueError, когда передаются аргументы чтения или ошибки. -
array_pad()
теперь ограничено только максимальным количеством элементов, которые может иметь массив. Раньше за раз можно было добавить не более 1048576 элементов. -
Новые функции posix:
posix_sysconf()
,posix_pathconf()
,posix_fpathconf()
иposix_eaccess()
-
Многократное выполнение
proc_get_status()
теперь всегда будет возвращать правильное значение в системах posix -
opcache.consistency_checks ini-директива была удалена
Оригинал статьи - https://stitcher.io/blog/new-in-php-83.