2020年10月29日,PHP8將釋出 RC3的版本,11月26日將正式釋出。
下面每個新特性介紹,都是禪師閱讀過相關的RFC說明後,加入了禪師的理解說明,還擴充了一些易理解的程式碼,不要理解為簡單的譯文哦,讓我們一起通過實際程式碼,看看PHP8都有哪些變化。
這裡的 union 和c/c++的不同,是指PHP支援多型別定義,比如例子裡的 $number 同時支援整形(int) 和 浮點型(float)。
如果這樣使用 $number->setNumber(‘string’),將會引發錯誤提示。所以強型別定義的好處是提前在程式碼編譯期就能發現錯誤,從而避免程式碼錯誤賦值。
當然這是強型別語言的特性,因為減少型別處理的麻煩,自然可以提升執行速度。這也是為啥強型別語言比指令碼語言快的根本原因之一。如下面示例程式碼:
<?php
declare(strict_types=1);
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
/**
* We can pass both floats or integer values
* to the number object. Try passing a string.
*/
$number = new Number();
$number->setNumber(5);
dump($number->getNumber());
$number->setNumber(11.54);
dump($number->getNumber());
輸出:
5
11.54
WeakMap不同於PHP7.4引入的Weak References(弱型別引用)。
當一個物件作為WeakMap的 Key 時,物件銷燬時,Key 會自動從 WeakMap 裡移除。
如下面示例程式碼:
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);
// object(WeakMap)#1 (1) {
// [0]=>
// array(2) {
// ["key"]=>
// object(stdClass)#2 (0) {
// }
// ["value"]=>
// int(42)
// }
// }
// The object is destroyed here, and the key is automatically removed from the weak map.
unset($obj);
var_dump($map);
// object(WeakMap)#1 (0) {
// }
<?php
declare(strict_types=1);
/**
* array_rand 第一個引數不能是空陣列,否則會引發 ValueError
*/
array_rand([], 0);
/**
* 第三個引數 depth 必須是大於0的整數,否則會引發 ValueError
*/
json_decode('{}', true, -1);
錯誤輸出:
ValueError array_rand(): Argument #1 ($array) cannot be empty
物件語言多型的特性,這裡我覺得叫 overloading(過載)更好點。因為保持原有引數不變的情況下,繼承類中使用才叫 overriding。
原有PHP的多型沒有c++/Java完善,這裡就不咬文嚼字了,大家理解含義就好。這個新特性也恰恰彌補了這點。
三個點開頭 …$param 這種叫可變引數,這個特性見如下程式碼:
<?php
declare(strict_types=1);
class A {
public function method(int $many, string $parameters, $here) {
}
}
class B extends A {
public function method(...$everything) {
dd($everything);
}
}
$b = new B();
$b->method('i can be overwritten!');
輸出:
array:1 [▼
0 => “i can be overwritten!”
]
這個不是什麼好玩的新特性,應該是針對 static語法衝突的解決方案。如果我理解有誤,歡迎指正。先看這個程式碼:
class A {
public function test(static $a) {}
}
class B extends A {}
function call_with_new_a(A $a) {
$a->test(new A);
}
call_with_new_a(new B);
class A {
// Is this an untyped static property,
// or an instance property of type static?
public static $a;
}
注意上面兩段程式碼就是存在 static 衝突的程式碼。是無效程式碼,因為PHP8為了解決衝突,僅允許在函式返回型別中使用 static修飾,含義為返回當前類的定義。正確使用方法如下:
<?php
declare(strict_types=1);
class Test {
public function doWhatever(): static {
// Do whatever.
return $this;
}
}
原來我們返回型別名,需要呼叫 get_class($object),PHP8新增了一個語法糖 $object::class,是比以前簡單了很多,看來未來有可能刪除掉 get_class。
<?php
declare(strict_types=1);
auth()->loginUsingId(1);
dump(auth()->user()::class);
// Or with a temporary variable
$user = auth()->user();
dump($user::class);
輸出:
“App\User”
“App\User”
這個是針對字串連續性的語法問題,仔細研究後,感覺還存在一些問題,等PHP8正式發版再看看。或者我理解有誤的地方,也歡迎指正。
/**
* Variable Syntax Tweaks
* New and instanceof can now be used with arbitrary expressions,
* using new (expression)(...$args) and $obj instanceof (expression).
*/
<?php
class Foo {}
$f = 'Foo';
new $f; // PHP7 需要箇中間變數才合法
new 'Foo'; // PHP7 不合法,PHP8裡變合法了
// PHP8的這個特性,目前根據RFC,以下程式碼還是存在一些問題,等待正式釋出再看看
echo __FUNCTION__[0]; // RFC文件說明應該合法,但還是不合法
$k = 'o';
new "F$k$k"; // RFC文件說明應該合法,但還是不合法
下面程式碼在PHP8中合法:
<?php
declare(strict_types=1);
class Foo {}
class Bar {}
$class = new (collect(['Foo', 'Bar'])->random());
dd($class);
PHP8引入了 Stringable,當一個類實現 __toString 時,類會自動轉成 Stringable 的例項,而不需要顯式宣告,程式碼如下:
<?php
declare(strict_types=1);
class Foo {
public function __toString() {
return 'I am a class';
}
}
$obj = new Foo;
dump($obj instanceof Stringable); // 輸出 true
<?php
declare(strict_types=1);
trait MyTrait {
abstract private function neededByTheTrait(): string;
public function doSomething() {
return strlen($this->neededByTheTrait());
}
}
class TraitUser {
use MyTrait;
// This is allowed:
private function neededByTheTrait(): string { }
// This is forbidden (incorrect return type)
// private function neededByTheTrait(): stdClass { }
// This is forbidden (non-static changed to static)
// private static function neededByTheTrait(): string { }
}
直接看下面程式碼中的差異:
<?php
declare(strict_types=1);
// 下面程式碼在PHP7中不合法,在PHP8中合法了
$callable = fn() => throw new Exception();
$nullableValue = null;
// $value is non-nullable.
$value = $nullableValue ?? throw new \InvalidArgumentException();
直接看程式碼,$d, 4, 後面都允許有多餘的逗號了:
<?php
declare(strict_types=1);
function method_with_many_arguments(
$a,
$b,
$c,
$d,
) {
dump("this is valid syntax");
}
method_with_many_arguments(
1,
2,
3,
4,
);
以前必須定義 $e,像這樣 catch($e \Exception),PHP8中可以不需要定義$e 了。
<?php
declare(strict_types=1);
$nullableValue = null;
try {
$value = $nullableValue ?? throw new \InvalidArgumentException();
} catch (\InvalidArgumentException) {
dump("Something went wrong");
}
mixed 是一種混合型別,等同於 array|bool|callable|int|float|null|object|resource|string。
<?php
declare(strict_types=1);
function debug_function(mixed ...$data) {
dump($data);
}
debug_function(1, 'string', []);
輸出:
array:3 [▼
0 => 1
1 => “string”
2 => []
]
這個是PHP8比較大的改動,新語法 #[Attribute] 定義屬性。以前操作需要使用反射,現在變簡單了許多。程式碼後面使用 ReflectionClass 來驗證 *#[xxxx] *這種語法。
<?php
declare(strict_types=1);
// First, we need to define the attribute. An Attribute itself is just a plain PHP class, that is annotated as an Attribute itself.
#[Attribute]
class ApplyMiddleware
{
public array $middlware = [];
public function __construct(...$middleware) {
$this->middleware = $middleware;
}
}
// This adds the attribute to the MyController class, with the "auth" middleware as an argument.
#[ApplyMiddleware('auth')]
class MyController
{
public function index() {}
}
// We can then retrieve all ApplyMiddleware attributes on our class using reflection
// And read the given middleware arguments.
$reflectionClass = new ReflectionClass(MyController::class);
$attributes = $reflectionClass->getAttributes(ApplyMiddleware::class);
foreach ($attributes as $attribute) {
$middlewareAttribute = $attribute->newInstance();
dump($middlewareAttribute->middleware);
}
<?php
declare(strict_types=1);
class User {
public function __construct(
public int $id,
public string $name,
) {}
}
$user = new User(1, 'Marcel');
dump($user->id);
dump($user->name);
match 表示式 和swtich 條件分支類似,主要作用是直接返回值。
<?php
declare(strict_types=1);
echo match (1) {
0 => 'Foo',
1 => 'Bar',
2 => 'Baz',
};
這個改動很好,不過有點參照 typescript 和 swift 的影子。管他呢,好用就行,這個語法糖漂亮多了。
<?php
declare(strict_types=1);
class User {
public function getAddress() {}
}
$user = new User();
$country = $user?->getAddress()?->country?->iso_code;
dump($country);
python 的影子,管他呢,好用就行,這個特性也非常實用,不用再考慮必須保證引數順序了,每個引數做啥的也能一目瞭然。
// 原來必須這樣按順序
array_fill(0, 100, 50);
// PHP8有了命名引數後,可以這樣了
array_fill(start_index: 0, num: 100, value: 50);
array_fill(value: 50, num: 100, start_index: 0);
以上關於PHP8的新特性,雖然沒有介紹JIT相關,但比如針對強型別上的改進,union type, mixed, Weakmap等也是為了適應JIT的變化。
也有一些語法上的改進,尤其最後介紹的 nullsafe和命名引數,都是非常實用的。PHP8的釋出值得期待。
本文由禪師原創,團隊長期招募
Laravel,Vue.js,Uniapp/Cordova/Electron
技術棧的人才,至少符合1項的小夥伴歡迎加我嶶xzencodex
,除了平時一起交流學習,有專案或者遠端offer的時候,可以隨時合作。
本作品採用《CC 協議》,轉載必須註明作者和本文連結