Что нового в PHP 8.3
PHP 8.3 был выпущен 23 ноября 2023 года. В нем есть улучшения классов только для чтения, новая функция json_validate(), дополнения к недавно добавленному классу Randomizer, обнаружение переполнения стека и многое другое.
Поправки для только для чтения rfc
В этом RFC было предложено два изменения, но было принято только одно: возможность повторной инициализации свойств только для чтения во время клонирования. Правда этот RFC касается только конкретного (но важного) случая: перезапись значений свойств внутри __clone(), чтобы разрешить глубокое клонирование свойств только для чтения:
readonly class Post
{
public function __construct(
public DateTime $createdAt,
) {}
public function __clone()
{
$this->createdAt = new DateTime();
// здесь это разрешено,
// даже не смотря на то что свойство `createdAt` только для чтения.
}
}
Типизированные константы класса rfc
Типизированные константы класса могут быть объявлены в классах, интерфейсах, трейтах и перечислениях, вы можете указать типы констант класса так:
class Foo
{
public const string A = 'a';
public const int B = 1;
public const float C = 1.1;
public const bool D = true;
public const array E = ['a', 'b'];
}
Типы констант класса поддерживают все объявления типов PHP, кроме void, callable и never. Константы класса являются ковариантными, что означает, что их типы не могут расширяться во время наследования.
Атрибут #[Override] rfc
Новый атрибут #[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 сможет определить, что он больше ничего не переопределяет, и выдаст ошибку.
Отрицательные индексы в массивах нарушение
Если у вас есть пустой массив, и вы добавите элемент с отрицательным индексом, а затем добавите еще один элемент, этот второй элемент всегда будет начинаться с индекса 0:
$array = [];
$array[-5] = 'a';
$array[] = 'b';
var_export($array);
//array (
// -5 => 'a',
// 0 => 'b',
//)
Начиная с PHP 8.3, следующий элемент будет добавлен в index -4:
//array (
// -5 => 'a',
// -4 => 'b',
//)
Анонимные классы только для чтения upd
Ранее нельзя было пометить анонимные классы как доступные только для чтения. Это исправлено в PHP 8.3:
$class = new readonly class {
public function __construct(
public string $foo = 'bar',
) {}
};
Новая функция json_validate rfc
В PHP 8.3 добавлена новая функция json_validate(), которая возвращает true или false в том случае, если заданная строка является допустимой строкой JSON. До PHP 8.3 единственным способом определить, является ли заданная строка допустимой строкой JSON, была попытка ее декодирования и проверка наличия каких-либо ошибок. Новая функция json_validate() использует тот же базовый синтаксический анализатор JSON, что и PHP, но потребует меньше памяти:
json_validate('[1, 2, 3]'); //true
json_validate('{1, 2, 3]'); //false
Динамический доступ к константам класса и Enum rfc
До PHP 8.3 синтаксис доступа к константам класса типа ClassName::{$varName} был запрещен и приводил к синтаксической ошибке:
Parse error: syntax error, unexpected token ";", expecting "(" in ... on line ...
То же ограничение применялось и к Enum, где было невозможно получить элемент Enum динамически:
Parse error: syntax error, unexpected token "->", expecting "(" in ``` on line ```
Теперь PHP 8.3 и более поздние версии поддерживают динамическое получение констант класса и объектов Enum с именем переменной:
class MyClass {
public const MY_CONST = 42;
}
$constName = 'MY_CONST';
echo MyClass::{$constName};
Функция gc_status() возвращает дополнительную информацию GC rfc
Функция gc_status() возвращает статистику сборщика мусора PHP и имеет четыре новых поля: running, protected, full и buffer_size: работает ли сборщик мусора, защищен ли сборщик мусора и размер буфера. Эта информация может быть полезна при отладке долго работающих PHP-приложений для обнаружения и оптимизации использования памяти.
var_dump(gc_status());
Новый метод \Random\Randomizer::getBytesFromString rfc
Класс \Random\Randomizer в PHP 8.3 поддерживает новый метод getBytesFromString, который возвращает последовательность случайных чисел запрошенной длины (параметр $length), содержащую только байты из запрошенной последовательности байтов (параметр $string).
namespace Random;
class Randomizer {
// ...
public function getBytesFromString(string $string, int $length): string { }
}
$rng = new Random\Randomizer();
$crockfordAlphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
$rng->getBytesFromString($crockfordAlphabet, 5); // "5YH8T"
Новые методы \Random\Randomizer::getFloat() и nextFloat() rfc
RFC добавляет новые методы в расширении Random, генерирующие случайное значение. \Random\Randomizer::getFloat()\Random\Randomizer::nextFloat(). Например если необходимо сгенерировать случайное число float между 0 <и < 10 и в диапазоне 0 <= и < 1:
$rng = new Random\Randomizer();
$rng->getFloat(0, 10, \Random\IntervalBoundary::OpenOpen); // 9.3835746900717
...
$rng = new Random\Randomizer();
$rng->nextFloat(); // 0.21185336351144
PHP CLI Lint (php -l) поддерживает проверку нескольких файлов одновременно
PHP CLI предоставляет функцию Linting, которая проверяет переданное имя файла на наличие синтаксических ошибок. Это полезно для быстрой проверки PHP-файла или куска кода перед выполнением:
php -l index.php
//No syntax errors detected in index.php
До PHP 8.3 было невозможно выполнить анализ нескольких файлов PHP за один вызов; независимо от количества предоставленных файлов, PHP CLI анализирует только первый файл. Начиная с PHP 8.3, можно передавать несколько файлов и PHP CLI проверяет их все за один вызов:
php -l index.php loader.php
// No syntax errors detected in index.php
// No syntax errors detected in loader.php
Более подходящие исключения даты/времени rfc break
Во многих случаях PHP просто выбрасывал объект Exception или Error или выдавал предупреждение/ошибку, когда что-то пошло не так при работе с датами и временем. Этот RFC добавляет для них соответствующие специальные исключения.
Теперь у нас есть исключения, такие как DateMalformedIntervalStringException, DateInvalidOperationException и DateRangeError.
Как правило, эти дополнения не нарушают код, поскольку эти недавно добавленные исключения и ошибки являются подклассами общего Exception/Error классов. Однако в этом RFC есть три небольших критических изменения:
- Теперь Epoch doesn't fit in a PHP integer возвращает new 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.
unserialize(): Вывод E_WARNING ошибок вместо E_NOTICE
До версии 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.3 был увеличен, а в будущем возможно его обновление для создания исключений.
Трейты и статические свойства
Использование трейтов со статическими свойствами теперь будет повторно объявлять статические свойства, унаследованные от родительского класса. Это создаст отдельное хранилище статических свойств для текущего класса. Это аналогично добавлению статического свойства в класс напрямую без трейтов.
Обнаружение переполнения стека pr
В 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 различные строковые функции доступны в двух вариантах: один для байтовых строк, а другой для многобайтовых строк (mb). Однако заметным отсутствием среди многобайтовых строковых функций 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 {}
// This will pad such that the string will become 10 bytes long.
var_dump(str_pad('Français', 10, '_', STR_PAD_RIGHT)); // BAD: string(10) "Français_"
var_dump(str_pad('Français', 10, '_', STR_PAD_LEFT)); // BAD: string(10) "_Français"
var_dump(str_pad('Français', 10, '_', STR_PAD_BOTH)); // BAD: string(10) "Français_"
// This will pad such that the string will become 10 characters long, and in this case 11 bytes.
var_dump(mb_str_pad('Français', 10, '_', STR_PAD_RIGHT));// GOOD: string(11) "Français__"
var_dump(mb_str_pad('Français', 10, '_', STR_PAD_LEFT)); // GOOD: string(11) "__Français"
var_dump(mb_str_pad('Français', 10, '_', STR_PAD_BOTH)); // GOOD: string(11) "_Français_"
Новая функция stream_context_set_options
В PHP есть функция stream_context_set_option, которая поддерживает две сигнатуры функций. Она может либо принимать массив параметров, которые можно установить для одного или нескольких контекстов или обёрток, либо принимать одно имя обёртки, имя параметра или его значение.
В рамках усилий PHP по удалению перегруженных сигнатур функций (функций, поддерживающих более одной сигнатуры), PHP 8.3 объявляет новую функцию stream_context_set_options (обратите внимание на последнюю букву "s" во множественном числе), которая поддерживает вторую сигнатуру выше.
Замыкания магических методов и именованные аргументы
Допустим, у вас есть класс, который поддерживает магические методы:
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 SomeInterface {
public const FOO = 'foo';
}
class C implements SomeInterface {
private const FOO = 'foo';
}
class_alias() поддерживает псевдонимы встроенных классов PHP
Функция class_alias создает псевдоним для предоставленного класса. Класс с псевдонимом ведет себя точно так же, как исходный класс.
Но до PHP 8.3 попытка создать псевдоним для встроенного класса PHP приводила к исключению ValueError:
class_alias('stdClass', 'MyClass');
class_alias('Traversable', 'NewTraversableInterface');
ValueError: class_alias(): Argument #1 ($class) must be a user-defined class name, internal class name given
Начиная с PHP 8.3 можно использовать псевдонимы для встроенных классов и интерфейсов и код отработает корректно:
class_alias('stdClass', 'MyClass');
class_alias('Traversable', 'NewTraversableInterface');
Функция class_alias() принимает bool $autoload = true в качестве третьего параметра. Этот параметр не влияет на встроенные классы, поскольку их не нужно загружать автоматически.
Небольшие устаревшие RFC
- Не рекомендуется передавать отрицательное значение $width в mb_strimwidth()
- Устареет и будет удалена константа NumberFormatter::TYPE_CURRENCY
- Устареет и будет удалена неработающая реализация Mt19937 до PHP 7.1 (MT_RAND_PHP)
- Устареет и будет удален вызов ldap_connect() с 2 параметрами $host и $port
- Устаревшие остатки строковых вычисляемых утверждений кода
Небольшие, но заметные изменения
И как обычно, не каждое изменение в PHP проходит процесс RFC. На самом деле, большинство изменений включают обслуживание и исправление ошибок и не требуют RFC. Все эти изменения перечислены в документе ОБНОВЛЕНИЕ . Я перечислю некоторые из самых известных, но вам обязательно следует прочитать весь список, если вы хотите узнать о мельчайших деталях.
- При использовании FFI функции C, которые имеют возвращаемый тип void теперь возвращают null вместо FFI\CData:void
- posix_getrlimit() теперь принимает необязательный параметр $res, позволяющий получить ограничение на один ресурс.
- mysqli_poll() теперь выдает ValueError, когда передаются аргументы чтения или ошибки.
- array_pad() теперь ограничено только максимальным количеством элементов, которые может иметь массив. Раньше можно было добавить не более 1048576 элементов за раз.
- Новые функции posix: posix_sysconf(), posix_pathconf(), posix_fpathconf() и posix_eaccess()
- proc_get_status() многократное выполнение теперь всегда будет возвращать правильное значение в posix-системах.
- директива opcache.consistency_checks в ini была удалена
- Улучшены array_sum() и array_product()
Ниолай Петрозаводск30.11.2023
Классная обнова, ждал ее с рождения, НАЙС