interface I {
// 我們可能天真地認為 PHP 常數始終是字串。
const PHP = 'PHP 8.2';
}
class Foo implements I {
// 但是實作類別可能會將其定義為陣列。
const PHP = [];
}
介面 I {
const string PHP = 'PHP 8.3';
}
類別 Foo 實作 I {
const string PHP = [];
}
// 致命錯誤:類別常數不能使用陣列作為值
// Foo::PHP 的型別為字串
類別 Foo {
const PHP = 'PHP 8.2';
}
$searchableConstant = 'PHP';
var_dump(constant(Foo::class . "::{$searchableConstant}"));
類別 Foo {
const PHP = 'PHP 8.3';
}
$searchableConstant = 'PHP';
var_dump(Foo::{$searchableConstant});
#[\Override]
屬性 RFC使用 PHPUnit\Framework\TestCase;
最終類別 MyTest 繼承 TestCase {
protected $logFile;
protected function setUp(): void {
$this->logFile = fopen('/tmp/logfile', 'w');
}
protected function taerDown(): void {
fclose($this->logFile);
unlink('/tmp/logfile');
}
}
// 日誌檔將永遠不會被刪除,因為
// 方法名稱拼寫錯誤(taerDown vs tearDown)。
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 將會確保在父類別或實作的介面中存在具有相同名稱的方法。添加此屬性可以明確表示覆寫父類別方法的意圖,並簡化重構,因為移除被覆寫的父類別方法將會被偵測到。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
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()
函式 RFC 文件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(真)
json_validate()
允許檢查字串是否為語法有效的 JSON,同時比 json_decode()
更有效率。Randomizer::getBytesFromString()
方法 RFC 文件// 這個函式需要手動實作。
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;
// 可以傳入一個 \Random\Engine 來設定種子,
// 預設為安全引擎。
$randomizer = new \Random\Randomizer();
$randomDomain = sprintf(
"%s.example.com",
$randomizer->getBytesFromString(
'abcdefghijklmnopqrstuvwxyz0123456789',
16,
),
);
echo $randomDomain;
Randomizer::getFloat()
和 Randomizer::nextFloat()
方法 RFC 文件// 傳回一個介於 $min 和 $max 之間的隨機浮點數,包含 $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) 可能會傳回上限值,例如 1,
// 造成些微偏差。
$myBoolean = getFloat(0, 1) < $chanceForTrue;
$randomizer = new \Random\Randomizer();
$temperature = $randomizer->getFloat(
-89.2,
56.7,
\Random\IntervalBoundary::ClosedClosed,
);
$chanceForTrue = 0.1;
// Randomizer::nextFloat() 等同於
// Randomizer::getFloat(0, 1, \Random\IntervalBoundary::ClosedOpen)。
// 不會傳回上限值,例如 1。
$myBoolean = $randomizer->nextFloat() < $chanceForTrue;
由於浮點數的精度有限且會隱含捨入,因此產生位於特定區間內的無偏差浮點數並非易事,常用的使用者端程式碼解決方案可能會產生偏差結果或超出要求範圍的數字。
Randomizer 也新增了兩個方法,可用於產生無偏差的隨機浮點數。Randomizer::getFloat()
方法使用 γ-section 演算法,該演算法發表於 從區間中繪製隨機浮點數。Frédéric Goualard,ACM Trans. Model. Comput. Simul.,32:3,2022 年。
php -l foo.php bar.php foo.php 中沒有偵測到語法錯誤
php -l foo.php bar.php foo.php 中沒有偵測到語法錯誤 bar.php 中沒有偵測到語法錯誤
命令列程式碼檢查器現在接受可變輸入的檔名以進行程式碼檢查
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()
方法。zend.max_allowed_stack_size
來設定允許的最大堆疊大小。n
指派給空陣列現在將確保下一個索引是 n + 1
而不是 0
。range()
函式的變更。U_MULTIPLE_DECIMAL_SEPERATORS
已被棄用,請改用 U_MULTIPLE_DECIMAL_SEPARATORS
。(原文拼寫錯誤已修正)MT_RAND_PHP
已被棄用。ReflectionClass::getStaticProperties()
不再可為 null。assert.active
、assert.bail
、assert.callback
、assert.exception
以及 assert.warning
已被棄用。get_class()
和 get_parent_class()
已被棄用。