2023年11月23日,PHP8.3正式發布。它包含了許多新功能,它包含了許多新功能,例如:類常量顯式類型、只讀屬性深拷貝,以及對隨機性功能的補充。一如既往,它還包括性能改進、錯誤修復和常規清理等。
類型化類常量
PHP < 8.3
interface I {
// 我們可能天真地假設 PHP 常量始終是一個字符串。
const PHP = 'PHP 8.2';
}
class Foo implements I {
// 但是實現類可以將其定義為數組。
const PHP = [];
}
PHP 8.3
interface I {
const string PHP = 'PHP 8.3';
}
class Foo implements I {
const string PHP = [];
}
// 致命錯誤:無法使用數組作為類常量的值
// 字符串類型的 Foo::PHP
動態獲取類常量
PHP < 8.3
class Foo {
const PHP = 'PHP 8.2';
}
$searchableConstant = 'PHP';
var_dump(constant(Foo::class . "::{$searchableConstant}"));
PHP 8.3
class Foo {
const PHP = 'PHP 8.3';
}
$searchableConstant = 'PHP';
var_dump(Foo::{$searchableConstant});
新增 #[\Override] 屬性
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');
}
}
// 日志文件永遠不會被刪除,因為
// 方法名稱輸入錯誤(taerDown 與tearDown)。
PHP 8.3
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');
}
}
// 致命錯誤:MyTest::taerDown() 有 #[\Override] 屬性,
// 但不存在匹配的父方法
通過給方法添加 #[\Override] 屬性,PHP 將確保在父類或實現的接口中存在同名的方法。添加該屬性表示明確說明覆蓋父方法是有意為之,并且簡化了重構過程,因為刪除被覆蓋的父方法將被檢測出來。
只讀屬性深拷貝
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;
// 致命錯誤:無法修改只讀屬性 Foo::$php
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;
$cloned->php->version = '8.3';
readonly 屬性現在可以在魔術方法 __clone 中被修改一次,以此實現只讀屬性的深拷貝
新增 json_validate() 函數
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
PHP 8.3
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true
json_validate() 可以檢查一個字符串是否為語法正確的 JSON,比 json_decode() 更有效。
新增Randomizer::getBytesFromString()方法
PHP < 8.3
// 該功能需要手動實現。
function getBytesFromString(string $string, int $length) {
$stringLength = strlen($string);
$result = '';
for ($i = 0; $i < $length; $i++) {
// random_int 不可用于測試,但安全。
$result .= $string[random_int(0, $stringLength - 1)];
}
return $result;
}
$randomDomain = sprintf(
"%s.example.com",
getBytesFromString(
'abcdefghijklmnopqrstuvwxyz0123456789',
16,
),
);
echo $randomDomain;
PHP 8.3
// 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;
在 PHP 8.2 中新增的 Random 擴展 通過一個新方法生成由特定字節組成的隨機字符串。這種方法可以使開發者更輕松的生成隨機的標識符(如域名),以及任意長度的數字字符串。
新增Randomizer::getFloat()和Randomizer::nextFloat()方法
PHP < 8.3
// 返回 $min 和 $max 之間的隨機浮點值,兩者都包括。
function getFloat(float $min, float $max) {
// 該算法對特定輸入有偏差,并且可能
// 返回超出給定范圍的值。這是不可能的
// 在用戶空間中解決。
$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;
PHP 8.3
$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;
由于浮點數的精度和隱式四舍五入的限制,在特定區間內生成無偏差的浮點數并非易事,常建的用戶解決方案可能會生成有偏差的結果或超出要求范圍的數字。
Randomizer 擴展了兩種方法,用于隨機生成無偏差的浮點數。Randomizer::getFloat() 方法使用的是 γ-section 算法,該算法發表于 Drawing Random Floating-Point Numbers from an Interval. Frédéric Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.
新的類、接口和函數
新增 DOMElement::getAttributeNames()、DOMElement::insertAdjacentElement()、DOMElement::insertAdjacentText()、DOMElement::toggleAttribute()、DOMNode::contains()、DOMNode::getRootNode()、DOMNode::isEqualNode()、DOMNameSpaceNode::contains() 和 DOMParentNode::replaceChildren() 方法。
新增 IntlCalendar::setDate()、IntlCalendar::setDateTime()、IntlGregorianCalendar::createFromDate() 和 IntlGregorianCalendar::createFromDateTime() 方法。
新增 ldap_connect_wallet() 和 ldap_exop_sync() 函數。
新增 mb_str_pad() 函數。
新增 posix_sysconf()、posix_pathconf()、posix_fpathconf() 和 posix_eaccess() 函數。
新增 ReflectionMethod::createFromMethodName() 方法
新增 socket_atmark() 函數。
新增 str_increment()、str_decrement() 和 stream_context_set_options() 函數。
新增 ZipArchive::getArchiveFlag() 方法。
支持在 OpenSSL 擴展中使用自定義 EC 參數生成 EC 密鑰。
新增 INI 設置 zend.max_allowed_stack_size 用于設置允許的最大堆棧大小。
棄用和向后不兼容
更合適的 Date/Time 異常。
現在在空數組中獲取負索引 n 時,將確保下一個索引是 n + 1 而不是 0。
對 range() 函數的更改。
在 traits 中重新聲明靜態屬性的更改。
U_MULTIPLE_DECIMAL_SEPERATORS 常量已被廢棄,改為 U_MULTIPLE_DECIMAL_SEPARATORS。
MT_RAND_PHP Mt19937 變體已被廢棄。
ReflectionClass::getStaticProperties() 不再為空。
INI 配置 assert.active、assert.bail、assert.callback、assert.exception 和 assert.warning 已被廢棄。
調用 get_class() 和 get_parent_class() 時未提供參數,已被廢棄。
詳細文檔:
https://www.php.net/releases/8.3/zh.php#typed_class_constants