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.
// 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:
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:
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:
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”:
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 void và never 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:
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:
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:
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ặcfalse
để kiểm tra chuỗi đã cho có phải là chuỗi JSON hợp lệ hay không:
$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ụngjson_last_error()
và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()
.
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.
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.
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.
$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
getFloat
()nextFloat
()PHP 8.3 thêm các phương thức Randomizer::getFloat()
và 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:
$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ị min
và max
có phải bao gồm ở kết quả trả về hay không.
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 và $max đều có thể được return.IntervalBoundary::OpenClosed
: $min có thể không được return, $max có thể.IntervalBoundary::OpenOpen
: Cả $min và $max đều không được return.
$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
$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:
php -l foo.php bar.php
No syntax errors detected in foo.php
- Ở PHP 8.3:
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.
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ự.
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 🙇♂️