Có gì mới ở PHP 8.3?

PHP 8.3 đã được released vào ngày 23/11/2023 và có nhiều tính năng cũng như cải tiến mới kể từ khi PHP 8.2 ra mắt. Mặc dù nó chính thức được coi là một bản released nhỏ nhưng một số thay đổi trong 8.3 có thể ảnh hưởng trực tiếp đến công việc của chúng ta với PHP, có thể giúp chúng ta viết mã nhanh hơn và ít lỗi hơn.

Hãy cùng tìm hiểu PHP có những tính năng nào mới và sự thay đổi nào ở bản phát hành mới nhất này.

1. Typed Class Constants

PHP 8.3 trở lên hỗ trợ khai báo kiểu dữ liệu cho các hằng số (constant) class PHP. Điều này đảm bảo tính tương thích về kiểu của các constant khi các class con và các class implement interface ghi đè lên chúng.

PHP
// Khai báo kiểu dữ liệu sau từ khoá "const"
interface I {
    const string PHP = 'PHP 8.3';
}

class X {
  protected const string FOO = 'foo';
}

trait Y {
  public const string BAR = 'bar';
}
  • Điều gì xảy ra nếu giá trị của constant có type khác với type đã khai báo:
PHP
class X {
    const string PHP = 1;
}

// Fatal error: Cannot use int as value for class constant X::PHP of type string
  • Ở PHP version trước 8.3, chúng ta có thể override lại giá trị của constant khi triển khai interface hoặc class cha:
PHP
interface I {
    const PHP = 'PHP 8.2';
}

class Foo implements I {
    const PHP = [];
}
  • Nhưng PHP 8.3 đã ngăn chặn việc thay đổi type của constant không tương thích với type đã khai báo ở interface hoặc class cha:
PHP
interface I {
    const string PHP = 'PHP 8.3';
}

class Foo implements I {

    // Illegal
    // Fatal error: Cannot use array as value for class constant Foo::PHP       
    // of type string
    const string PHP = [];
    
    // Illegal:
    // Type must be declared if it was specified in the base class
    const PHP = "PHP 8.3";

    // Illegal:
    // In this case, we can't change the type declared in the 
    // base class, even if the new type and its value are compatible.
    const float PHP = 8.3;
    
    // Legal:
    // It's OK
    const string PHP = "PHP 8.3.x.y.z";
}
  • Chúng ta có thể khai báo nhiều kiểu theo dạng “narrowed”:
PHP
class Foo {
    public const string|int FOO = 'foo';
}

class Bar extends Foo {
    public const string FOO = 'bar';
    public const int FOO = 8;
    
    // Illegal:
    // Không thể mở rộng thêm types ở class con
    public const string|int|float FOO = 8;
}

❗️ Hai types voidnever không được hỗ trợ khi khai báo constant.

2. Dynamic class constant fetch

  • Việc fetch các constants, có thể sử dụng hàm constant(). Ở PHP version trước 8.3, chúng ta sẽ làm thế này:
PHP
class Foo {
    public const THE_FOO = 9;
}

enum FooEnum: int {
    case FirstMember = 9;
    case SecondMember = 10;
}

$constantName = 'THE_FOO';
$enumName = 'FirstMember';

echo constant('Foo::' . $constantName); // 9
echo constant('FooEnum::' . $enumName)->value; // 9
  • Từ PHP 8.3 trở đi, chúng ta có thể fetch chúng một cách linh động và gọn gàng hơn:
PHP
echo Foo::{$constantName};
echo FooEnum::{$enumName}->value;

3. json_validate() function

Khi làm việc với dữ liệu JSON-encoded, chúng ta muốn biết liệu phần payload có hợp lệ về mặt cú pháp hay không trước khi làm điều gì đó với nó.

  • Trước PHP 8.3, cách duy nhất để xác định xem một chuỗi có phải là chuỗi JSON hợp lệ hay không là cố gắng decode nó và xem liệu có lỗi nào không:
PHP
function json_check(string $string): bool {
    json_decode($string);

    return json_last_error() === JSON_ERROR_NONE;
}

$string = '{ "test": { "foo": "bar" } '
echo json_check($string); // false
  • Ở PHP 8.3 này, một hàm mới được bổ sung có tên json_validate() trả về true hoặc false để kiểm tra chuỗi đã cho có phải là chuỗi JSON hợp lệ hay không:
PHP
$string = '{ "test": { "foo": "bar" } '
echo json_validate($string); // false
  • Nếu json_validate() trả về false, nguyên nhân có thể được truy xuất bằng cách sử dụng json_last_error()json_last_error_msg().

❗️Important: Nếu bạn không làm gì với những thuộc tính trong payload của chuỗi JSON và chỉ cần kiểm tra JSON có hợp lệ hay không để làm 1 việc gì đó (insert vào DB chẳng hạn), thì sử dụng json_validate() sẽ sử dụng ít bộ nhớ hơn json_decode(), vì nó không cần phải mã hoá hay xây dựng cấu trúc array/object trong chuỗi JSON trên.

4. #[\Override] attribute

Giả sử chúng ta muốn override method test() của lớp cha, nhưng lại gõ sai thành best(), việc này ngầm hiểu là tạo ra method mới có tên là best(), không đúng mục đích ban đầu là override lại method test().

PHP
class Parent {
  protected function test(): void {}
}

class Child extends Parent {
  public function best(): void {}
}

PHP 8.3 cung cấp một thuộc tính mới có tên #[\Override] có thể được thêm vào các method class.

  • Khi thuộc tính #[\Override] được thêm vào một method class có extend class cha hoặc implement một interface, PHP bắt buộc rằng method đó phải override hoặc triển khai method của class cha hoặc interface.
  • Mục đích là để ngăn chặn việc chúng ta muốn override/triển khai method của class cha nhưng lại vô tình viết sai tên method hoặc method đó đã bị xoá khỏi lớp cha.
PHP
class Parent {
  protected function test(): void {}
}

class Child extends Parent {
  #[\Override]
  public function test(): void {}
  // Good
}

class Grand extends Parent {
  #[\Override]
  public function best(): void {}
  // Bad:
  // Fatal error: Grand::best() has #[\Override] attribute,
  // but no matching parent method exists
}

5. Deep Cloning of readonly Properties

Trong PHP 8.1, các thuộc tính readonly đã được giới thiệu, tiếp theo là class readonly trong PHP 8.2.

Một vấn đề khác là khi clone các đối tượng. Việc clone các đối tượng và thuộc tính readonly là điều không thể.

Kể từ PHP 8.3, bạn có thể linh hoạt khởi tạo lại hoặc unset các thuộc tính readonly trong quá trình clone.

PHP
readonly class Foo
{
    public function __construct(private DateTime $createdAt)
    {
    }

    public function getCreatedAt(): DateTime
    {
        return $this->createdAt;
    }

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

$instance = new Foo(new DateTime('2023-12-15'));
$cloned = clone $instance;

// For PHP < 8.3:
// PHP Fatal error: Uncaught Error: Cannot modify readonly 
// property Foo::$createdAt in Foo.php

// For PHP 8.3:
$cloned = clone $instance;
$cloned->getCreatedAt()->setDate(2023, 12, 16);

6. Randomizer::getBytesFromString() method

PHP 8.3 hỗ trợ phương thức Randomizer::getBytesFromString() mới trả về một chuỗi số ngẫu nhiên từ chuỗi đầu vào và có độ dài được yêu cầu.

Cơ hội để một byte được chọn tỷ lệ thuận với tỷ lệ xuất hiện của nó trong chuỗi đầu vào. Nếu mỗi byte xuất hiện với số lần như nhau thì mỗi byte đều có khả năng được chọn như nhau.

PHP
$random = new Random\Randomizer();
$alpha = 'ABCDEFGHJKMNPQRSTVWXYZ';

$random->getBytesFromString($alpha, 6); //  "MBXGWL"
$random->getBytesFromString($alpha, 6); //  "LESPMG"

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

echo $randomDomain;

Lưu ý rằng phương thức Randomizer::getBytesFromString() hoạt động ở cấp độ byte. Nó không thể xáo trộn một cách hiệu quả các ký tự nhiều byte như biểu tượng cảm xúc, ký tự CJK.

7. Randomizer::getFloat() and Randomizer::nextFloat() method

PHP 8.3 thêm các phương thức Randomizer::getFloat()Randomizer::nextFloat() tạo ra giá trị float ngẫu nhiên.

7.1. Randomizer::getFloat()

  • Ví dụ, generate 1 số float có khoảng giá trị 0 -> 5:
PHP
$random = new Random\Randomizer();

$random->getFloat(0, 5); // 2.3937446906217

Phương thức Randomizer::getFloat() chấp nhận \Random\IntervalBoundary Enum làm tham số thứ ba để cho biết liệu các giá trị minmax có phải bao gồm ở kết quả trả về hay không.

PHP
enum IntervalBoundary {
    case ClosedOpen;
    case ClosedClosed;
    case OpenClosed;
    case OpenOpen;
}

Rules như sau:

  • IntervalBoundary::ClosedOpen: $min có thể được return, $max có thể không.
  • IntervalBoundary::ClosedClosed: cả $min$max đều có thể được return.
  • IntervalBoundary::OpenClosed: $min có thể không được return, $max có thể.
  • IntervalBoundary::OpenOpen: Cả $min$max đều không được return.
PHP
$random = new Random\Randomizer();

$random->getFloat(0, 10, \Random\IntervalBoundary::OpenOpen); 
// 9.3835746900717

7.2. Randomizer::nextFloat()

Method Randomizer::nextFloat() mới có chức năng giống hệt với Randomizer::getFloat(0, 1, \Random\IntervalBoundary::ClosedOpen), trả về một giá trị ngẫu nhiên trong phạm vi 0 <= x < 1

PHP
$random = new Random\Randomizer();

$random->nextFloat(); // 0.3767414902847

8. PHP CLI Lint (php -l) supports linting multiple files at once

PHP CLI cung cấp tính năng Linting để kiểm tra tên tệp đã truyền để tìm lỗi syntax. Điều này rất hữu ích để kiểm tra nhanh tệp PHP hoặc đoạn mã trước khi thực thi.

  • Ở PHP < 8.3, chỉ thực hiện kiểm tra lỗi syntax ở file đầu tiên được khai báo:
Zsh
php -l foo.php bar.php
No syntax errors detected in foo.php
  • Ở PHP 8.3:
Zsh
php -l foo.php bar.php
No syntax errors detected in foo.php
No syntax errors detected in bar.php

9. mb_str_pad() function

PHP cung cấp hàm str_pad được sử dụng để đệm một chuỗi đến một độ dài xác định bằng một chuỗi khác.

Tuy nhiên, hàm str_pad không hỗ trợ các ký tự nhiều byte, dẫn đến các vấn đề khi xử lý các ngôn ngữ sử dụng mã hóa nhiều byte như UTF-8.

Kể từ PHP 8.3, hàm mb_str_pad được cung cấp để giải quyết hạn chế trước đó của hàm str_pad trong việc xử lý mã hóa ký tự nhiều byte như UTF-8.

PHP
echo mb_str_pad('卞卞', 6, '', STR_PAD_BOTH); 
// 义义卞卞义义

echo mb_str_pad('▶▶', 6, '❤❓❇', STR_PAD_RIGHT); 
 // string(18) "▶▶❤❓❇❤"
 
echo mb_str_pad('▶▶', 6, '❤❓❇', STR_PAD_LEFT);  
// string(18) "❤❓❇❤▶▶"

10. str_increment(), str_decrement() functions

Kể từ PHP 8.3, các hàm str_increment có thể được sử dụng tăng một chuỗi chữ và số, và hàm str_decrement giảm một chuỗi chữ và số.

Các hàm này có thể đặc biệt hữu ích trong các tình huống mà bạn cần quản lý các chuỗi chữ và số đại diện cho các giá trị tuần tự.

PHP
echo str_increment('ABC'); // ABD
echo str_decrement('ABC'); // ABB
echo str_increment('39'); // 40
echo str_decrement('38'); // 37

Vẫn còn một vài tính năng mới và thay đổi trong version này, mọi người có thể xem thêm tại: https://www.php.net/releases/8.3/en.php#other_new_things

Cảm ơn mọi người đã dành thời gian để đọc 🙇‍♂️

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like
Read More

SOLID Principles

Table of Contents Hide Single-responsibility PrincipleOpen/Closed Principle (OCP)Liskov Substitution Principle (LSP)Interface Segregation Principle (ISP)Dependency Inversion Principle (DIP)Advantages of…