目前PHP8.2的發(fā)布時間還沒有確定,但是預計將在2022年末發(fā)布。這篇文章將為你介紹在新版本中的功能、性能改進、棄用的特性等。
null和false將作為獨立的類型
PHP并不會陷入到完美的類型安全方向中,但是從技術(shù)的角度考慮,將null和false作為獨立的數(shù)據(jù)類型是值得的。一般情況下,PHP的很多常見的函數(shù),會通過返回false表示出錯了。比如在file_get_content中:
file_get_contents(/* … */): string|false
在以前,false可以在聯(lián)合類型中使用,但是不能獨立使用,在PHP8.2中可以單獨使用:
function alwaysFalse(): false { return false; }
當然,對于這個做法,一些開發(fā)者都持謹慎態(tài)度。他并不支持true作為獨立類型。這些開發(fā)者們認為,false只是一個值,類型應該代表類別而不是一個值。當然在類型系統(tǒng)中,有一個概念是單元類型,它是只允許一個值的類型。但是這真的有用嗎?
不過另一個RFC也正在討論將true作為一種類型添加到PHP中。
一個獨立的null卻很有意義,這樣可以簡單地實現(xiàn)空對象模式:
class Post { public function getAuthor(): ?string { /* … */ } } class NullPost extends Post { public function getAuthor(): null { /* … */ } }
這對NullPost::getAuthor()能夠說它只會返回null,不必像以前那樣必須將null和string一起聯(lián)合聲明。
棄用動態(tài)屬性
對于語言規(guī)范來說,這是更好的設計,但是也會限制很多用法。動態(tài)屬性在PHP8.2中被棄用,并且會在PHP中拋出錯誤異常。
什么是動態(tài)屬性?就是你沒有在類中聲明這些屬性,但是仍然可以設置和獲?。?/p>
class Post { public string $title; } // … $post->name = 'Name'; // 在PHP8.2中不能這樣使用,因為并沒有在類中聲明
不過放心,__set和__get等魔術(shù)方法將仍然按預期中工作:
class Post { private array $properties = []; public function __set(string $name, mixed $value): void { $this->properties[$name] = $value; } } // … $post->name = 'Name';
標準對象也是如此:stdClass將繼續(xù)支持動態(tài)屬性。
PHP曾經(jīng)是一種動態(tài)程度很強的動態(tài)語言,但是現(xiàn)在已經(jīng)有很多人愿意接受更加嚴格的編程方式了。盡可能的嚴格,盡可能的依賴靜態(tài)分析是一件好事,這能讓開發(fā)者們寫出更好的代碼。
不過可能一部分很看重動態(tài)屬性的開發(fā)人員對這種變化會很不滿意,如果你不想在使用PHP8.2時看到這些警告,可以這樣做:
可以使用#[AllowDynamicProperties]
#[AllowDynamicProperties] class Post { public string $title; } // … $post->name = 'Name'; // 一切正常
另一種方法是修改報警級別,但不建議這樣做。等你打算升級到PHP9.0時會遇到麻煩。
error_reporting(E_ALL ^ E_DEPRECATED);
追蹤調(diào)用時參數(shù)脫敏
什么叫參數(shù)脫敏?在我們開發(fā)時,遇到錯誤,都會使用Trace調(diào)試,但是目前的堆棧記錄下一些敏感數(shù)據(jù),比如環(huán)境變量、密碼、用戶。
在PHP8.2中允許對參數(shù)進行一些編訂( Redact ,姑且叫做編訂,有一些修飾的意思,但直接稱為修飾并不合適),比如將一些參數(shù)設置脫敏,這樣這些參數(shù)的調(diào)用值不會在堆棧信息中列出:
function test( $foo, #[\SensitiveParameter] $bar, $baz ) { throw new Exception('Error'); } test('foo', 'bar', 'baz');
如果報錯的話,會發(fā)現(xiàn),第二個參數(shù)bar并沒有記錄實際的值。這樣能起到脫敏的作用,如果傳的是密碼的話,就不會別記錄下來。
Fatal error: Uncaught Exception: Error in test.php:8
Stack trace:
#0 test.php(11): test('foo', Object(SensitiveParameterValue), 'baz')
#1 {main}
thrown in test.php on line 8
棄用了部分對象的調(diào)用方式
一些以前的調(diào)用對象方式杯棄用了。這里面一些是需要通過call_user_func($callable)來調(diào)用的,而不是能夠$callable()直接調(diào)用的。
"self::method" "parent::method" "static::method" ["self", "method"] ["parent", "method"] ["static", "method"] ["Foo", "Bar::method"] [new Foo, "Bar::method"]
為什么要這樣做呢?Nikita在RFC討論中很好的做出了解釋:
這些廢棄的調(diào)用都是有關上下文的, “self::method”所指的方法取決于從哪個類執(zhí)行調(diào)用或可調(diào)用性檢查。實際上,當以[new Foo, "parent::method"]的形式使用時,這通常也適用于最后兩種情況。
減少可調(diào)用對象的上下文相關性是本RFC的次要目標。在這個RFC之后,唯一剩下的范圍依賴是方法可見性:“Foo::bar”可能在一個范圍內(nèi)可見,但在另一個范圍內(nèi)不可見。如果將來可調(diào)用對象僅限于公共方法,那么可調(diào)用類型將變得明確定義并且可以用作屬性類型。但是,對可見性處理的更改不建議作為本RFC的一部分。
提升對未定義變量的檢測機制和級別
未定義的變量是那些在被讀取之前還沒有被初始化的變量。訪問未定義的變量當前會發(fā)出E_WARNING“警告:未定義的變量$varname”,并將變量視為null,但不會中斷執(zhí)行,從而允許代碼執(zhí)行繼續(xù)有增無減,但可能處于意外狀態(tài)。
目前可以通過一些配置,讓PHP執(zhí)行時對未定義變量產(chǎn)生錯誤級異常,但這需要單獨配置。PHP應當默認提供更安全的檢驗。
一般什么情況下會出現(xiàn)未定義變量的情況呢?
用法1
變量在某個分支中聲明,比如在if中設置一個值。
if ( $user -> admin ) { $restricted = false ; } if ( $restricted ) { die ( '你沒有進入這里的權(quán)限' ) ; }
用法2
變量拼寫錯誤:
$name = 'Joe'; echo 'Welcome, ' . $naame;
這種用法在1中也可能會發(fā)生:
if ($user->admin) { $restricted = false; } else { $restrictedd = true; } if ($restricted) { die('You do not have permission to be here'); }
用法3
在循環(huán)中定義,但這個循環(huán)可能并沒有執(zhí)行:
while ($item = $itr->next()) { $counter++; // 定義變量 } // 這里調(diào)用了變量,但是很有可能并沒有定義這個變量 echo 'You scanned ' . $counter . ' items';
解決方法
在這些分支之前提前定義好一個默認值。
對于第1種用法:
$restricted = true; if ($user->admin) { $restricted = false; } if ($restricted) { die('You do not have permission to be here'); }
對于第3種用法:
$counter = 0; while ($item = $itr->next()) { $counter++; } echo 'You scanned ' . $counter . ' items';
這樣做的好處是消除了整個訪問和使用這些未定義變量的后果,以及回退到引擎默認狀態(tài)的用戶態(tài)錯誤。這樣我們提供了另一層保護,防止PHP程序發(fā)生了這種意外后繼續(xù)運行。
這種更改也會讓PHP引擎和JIT等方面不會那么復雜。
這個版本主要是針對PHP9.0的,在PHP8.2的還是警告,在以后會將這種行為提升到錯誤級別。
增加只讀類
通過給類添加只讀修飾符來聲明只讀類。
readonly class Test { public string $prop; }
這樣做會隱式地將類的所有實例屬性標記為只讀。此外,它將阻止創(chuàng)建動態(tài)屬性。
readonly class Foo { public int $bar; public function __construct() { $this->bar = 1; } } $foo = new Foo(); $foo->bar = 2; // 拋出錯誤,不能修改只讀屬性 Foo::$bar $foo->baz = 1; // 拋出錯誤:不能動態(tài)創(chuàng)建屬性 Foo::$baz
可以通過增加#[AllowDynamicProperties]屬性,可以不觸發(fā)錯誤的情況下創(chuàng)建動態(tài)屬性。
#[AllowDynamicProperties] readonly class Foo { }
一些限制:
由于是只讀類,必須對屬性聲明類型:
readonly class Foo { public $bar; } // 以上定義會產(chǎn)生錯誤。
不能使用靜態(tài)屬性:
readonly class Foo { public static int $bar; } // 拋出錯誤: 只讀屬性不能聲明靜態(tài)類型
原文地址:https://phpreturn.com/index/a626a74a300dc5.html
原文平臺:PHP武器庫
版權(quán)聲明:本文由phpreturn.com(PHP武器庫官網(wǎng))原創(chuàng)和首發(fā),所有權(quán)利歸phpreturn(PHP武器庫)所有,本站允許任何形式的轉(zhuǎn)載/引用文章,但必須同時注明出處。