Випущено!

PHP 8.3 — це значне оновлення мови PHP.
Воно містить багато нових можливостей, таких як явна типізація констант класів, глибоке клонування readonly-властивостей і доповнення до функціоналу генерування випадкових чисел. Як завжди, воно також включає покращення продуктивності, виправлення помилок і загальний рефакторинг.

Типізовані константи класу

PHP < 8.3
interface I {
    // We may naively assume that the PHP constant is always a string.
    const PHP = 'PHP 8.2';
}

class Foo implements I {
    // But implementing classes may define it as an array.
    const PHP = [];
}
PHP 8.3 RFC
interface I {
    const string PHP = 'PHP 8.3';
}

class Foo implements I {
    const string PHP = [];
}

// Fatal error: Cannot use array as value for class constant
// Foo::PHP of type string

Динамічна вибірка констант класу

PHP < 8.3
class Foo {
    const PHP = 'PHP 8.2';
}

$searchableConstant = 'PHP';

var_dump(constant(Foo::class . "::{$searchableConstant}"));
PHP 8.3 RFC
class Foo {
    const PHP = 'PHP 8.3';
}

$searchableConstant = 'PHP';

var_dump(Foo::{$searchableConstant});

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

Додавши до методу атрибут #[\Override], PHP буде впевнюватися, що метод із такою ж назвою існує у батьківському класі або реалізованому інтерфейсі. Додавання цього атрибута дає можливість зрозуміти, що перевизначення батьківського методу є навмисним і спрощує рефакторинг, оскільки видалення перевизначеного батьківського методу не залишиться непоміченим.

PHP < 8.3
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase {
    protected $logFile;

    protected function setUp(): void {
        $this->logFile = fopen('/tmp/logfile', 'w');
    }

    protected function taerDown(): void {
        fclose($this->logFile);
        unlink('/tmp/logfile');
    }
}

// The log file will never be removed, because the
// method name was mistyped (taerDown vs tearDown).
PHP 8.3 RFC
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase {
    protected $logFile;

    protected function setUp(): void {
        $this->logFile = fopen('/tmp/logfile', 'w');
    }

    #[\Override]
    protected function taerDown(): void {
        fclose($this->logFile);
        unlink('/tmp/logfile');
    }
}

// Fatal error: MyTest::taerDown() has #[\Override] attribute,
// but no matching parent method exists

Глибоке клонування readonly-властивостей

Щоб забезпечити можливість глибокого клонування властивостей, доступних лише для читання, readonly властивості тепер можуть бути модифіковані один раз, за допомогою магічного методу __clone.

PHP < 8.3
class PHP {
    public string $version = '8.2';
}

readonly class Foo {
    public function __construct(
        public PHP $php
    ) {}

    public function __clone(): void {
        $this->php = clone $this->php;
    }
}

$instance = new Foo(new PHP());
$cloned = clone $instance;

// Fatal error: Cannot modify readonly property Foo::$php
PHP 8.3 RFC
class PHP {
    public string $version = '8.2';
}

readonly class Foo {
    public function __construct(
        public PHP $php
    ) {}

    public function __clone(): void {
        $this->php = clone $this->php;
    }
}

$instance = new Foo(new PHP());
$cloned = clone $instance;

$cloned->php->version = '8.3';

Нова функція json_validate()

Функція json_validate() дозволяє перевірити, чи є рядок синтаксично правильним JSON, при цьому є ефективнішою за функціюjson_decode().

PHP < 8.3
function json_validate(string $string): bool {
    json_decode($string);

    return json_last_error() === JSON_ERROR_NONE;
}

var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true

Новий метод Randomizer::getBytesFromString()

Модуль Random, що додано у PHP 8.2, було розширено новим методом генерування випадкових рядків, які складаються лише з певних байтів. Цей метод дозволяє розробнику легко генерувати випадкові ідентифікатори, такі як імена доменів і числові рядки довільної довжини.

PHP < 8.3
// This function needs to be manually implemented.
function getBytesFromString(string $string, int $length) {
    $stringLength = strlen($string);

    $result = '';
    for ($i = 0; $i < $length; $i++) {
        // random_int is not seedable for testing, but secure.
        $result .= $string[random_int(0, $stringLength - 1)];
    }

    return $result;
}

$randomDomain = sprintf(
    "%s.example.com",
    getBytesFromString(
        'abcdefghijklmnopqrstuvwxyz0123456789',
        16,
    ),
);

echo $randomDomain;
// A \Random\Engine may be passed for seeding,
// the default is the secure engine.
$randomizer = new \Random\Randomizer();

$randomDomain = sprintf(
    "%s.example.com",
    $randomizer->getBytesFromString(
        'abcdefghijklmnopqrstuvwxyz0123456789',
        16,
    ),
);

echo $randomDomain;

Нові методи Randomizer::getFloat() і Randomizer::nextFloat()

Через обмежену точність і неявне округлення чисел з рухомою комою, генерування незміщеного числа з рухомою комою, що лежить у межах певного інтервалу, є нетривіальним завданням, а загальноприйняті користувацькі рішення можуть генерувати зміщені результати або числа, що виходять за межі заданого діапазону.

Клас Randomizer було розширено двома методами для неупередженого генерування випадкових чисел з рухомою комою. Метод Randomizer::getFloat() використовує алгоритм y-section, який було опубліковано у статті Drawing Random Floating-Point Numbers from an Interval. Frédéric Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.

PHP < 8.3
// Returns a random float between $min and $max, both including.
function getFloat(float $min, float $max) {
    // This algorithm is biased for specific inputs and may
    // return values outside the given range. This is impossible
    // to work around in userland.
    $offset = random_int(0, PHP_INT_MAX) / PHP_INT_MAX;

    return $offset * ($max - $min) + $min;
}

$temperature = getFloat(-89.2, 56.7);

$chanceForTrue = 0.1;
// getFloat(0, 1) might return the upper bound, i.e. 1,
// introducing a small bias.
$myBoolean = getFloat(0, 1) < $chanceForTrue;
$randomizer = new \Random\Randomizer();

$temperature = $randomizer->getFloat(
    -89.2,
    56.7,
    \Random\IntervalBoundary::ClosedClosed,
);

$chanceForTrue = 0.1;
// Randomizer::nextFloat() is equivalent to
// Randomizer::getFloat(0, 1, \Random\IntervalBoundary::ClosedOpen).
// The upper bound, i.e. 1, will not be returned.
$myBoolean = $randomizer->nextFloat() < $chanceForTrue;

Лінтер командного рядка підтримує можливість перевірки декількох файлів

Лінтер командного рядка тепер може приймати декілька імен файлів для перевірки

PHP < 8.3
php -l foo.php bar.php
No syntax errors detected in foo.php
php -l foo.php bar.php
No syntax errors detected in foo.php
No syntax errors detected in bar.php

Нові класи, інтерфейси та функції

Застаріла функціональність і зміни у зворотній сумісності

  • Доречніші винятки у модулі Date/Time.
  • Присвоєння від'ємного індексу n до порожнього масиву тепер гарантує, що наступним індексом буде n + 1 замість 0.
  • Внесено зміни до функціїrange().
  • Зміни у повторному оголошенні статичних властивостей у трейтах.
  • Константу U_MULTIPLE_DECIMAL_SEPERATORS оголошено застарілою, натомість рекомендується використовувати U_MULTIPLE_DECIMAL_SEPARATORS.
  • Механізм Mt19937 MT_RAND_PHP оголошено застарілим.
  • ReflectionClass::getStaticProperties() тепер не повертає значення null.
  • Параметри INI assert.active, assert.bail, assert.callback, assert.exception і assert.warning оголошено застарілими.
  • Можливість виклику функції get_class() і get_parent_class() без аргументів оголошено застарілою.
  • SQLite3: Режим помилок за замовчуванням встановлено на винятки.
To Top