PHP 8已经处于发布候选阶段,RC 3版本于10月29日发布,完整版本计划于11月26日发布。现在是时候看看PHP 8中等待我们的新功能了。发布时间表可以在此处查看。有关更新到新版本的官方指南,请参见此处。
添加了对联合类型(RFC)的支持
联合类型接受其他各种类型的值,而不仅仅是一种。
<?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());
exit;
添加了WeakMap(RFC)
弱映射使您可以在对象和任意值(以及SplObjectStorage
)之间创建关系,而用作键的对象不受垃圾收集器的保护。如果收集器破坏了此类对象,则只需将其从地图中删除即可。
这是一个非常有用的功能。它使我们可以更少地考虑代码中的内存泄漏。虽然这对于大多数PHP开发人员来说不是问题,但是在创建长时间运行的进程(例如使用ReactPHP)时值得研究。使用WeakMaps,当对象不可用时,垃圾收集器会自动收集对象引用。
如果对数组执行相同操作,则对象引用将持久存在,从而导致内存泄漏。
<?php
declare(strict_types=1);
class FooBar {
public WeakMap $cache;
public function __construct() {
$this->cache = new WeakMap();
}
public function getSomethingWithCaching(object $obj) {
return $this->cache[$obj] ??= $this->computeSomethingExpensive($obj);
}
public function computeSomethingExpensive(object $obj) {
dump("I got called");
return rand(1, 100);
}
}
$cacheObject = new stdClass;
$obj = new FooBar;
// "I got called" only will be printed once
$obj->getSomethingWithCaching($cacheObject);
$obj->getSomethingWithCaching($cacheObject);
dump(count($obj->cache));
// When unsetting our object, the WeakMap frees up memory
unset($cacheObject);
dump(count($obj->cache));
exit;
新异常ValueError
PHP 8引入了一个新的内置异常类
ValueError
。它补充自己\Exception
。每次将正确类型的值传递给函数时,PHP都会引发此异常,但是不能在此操作中使用它。以前,在这种情况下会发出警告。例子:
<?php
declare(strict_types=1);
/**
* We pass an array to array_rand,
* which is of the correct type. But
* array_rand expects non-empty arrays.
*
* This throws a ValueError exception.
*/
array_rand([], 0);
/**
* The depth argument for json_decode is a
* valid integer, but it must be greater than 0
*/
json_decode('{}', true, -1);
定义函数时,可以使用可变参数
如果类型兼容,现在可以使用可变参数替换任意数量的函数参数。例如,以下代码不正确:
<?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!');
exit;
返回类型静态(RFC)
现在可以使用静态返回类型来确定方法是否返回为其调用了该方法的类,即使该方法是继承的(静态绑定后期)。
<?php
declare(strict_types=1);
class Test {
public function doWhatever(): static {
// Do whatever.
return $this;
}
}
exit;
对象类名称文字(RFC)
现在,您可以使用检索对象的类名称
$object::class
。结果将与相同get_class($object)
。
<?php
declare(strict_types=1);
auth()->loginUsingId(1);
dump(auth()->user()::class);
// Or with a temporary variable
$user = auth()->user();
dump($user::class);
exit;
变量语法设置(RFC)
New
而instanceof
现在可以用任意表达式中使用:new ()(...$args)
和$obj instanceof ()
。
<?php
declare(strict_types=1);
class Foo {}
class Bar {}
$class = new (collect(['Foo', 'Bar'])->random());
dd($class);
exit;
可串接介面(RFC)
PHP 8引入了一个新接口
Stringable
,该接口在类实现方法后立即自动添加__toString
。您无需显式实现此接口。
<?php
declare(strict_types=1);
class Foo {
public function __toString() {
return 'I am a class';
}
}
$obj = new Foo;
dump($obj instanceof Stringable);
exit;
特性现在可以定义抽象私有方法(RFC)
<?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 { }
}
exit;
throw现在可以用作表达式(RFC)
表达
throw
,现在可以使用其中仅表达式允许:在箭头的功能,聚结操作符,三元条件算子(三元/猫王)。
<?php
declare(strict_types=1);
$callable = fn() => throw new Exception();
$nullableValue = null;
// $value is non-nullable.
$value = $nullableValue ?? throw new \InvalidArgumentException();
exit;
列表参数中现在允许使用可选的挂逗号(RFC)
通过类似于数组中的悬挂逗号,现在可以在列表参数中对其进行定义。
<?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,
);
exit;
在不存储变量的情况下捕获异常(RFC)
现在,您可以编写
catch ()
捕获异常而无需将其存储在变量中。
<?php
declare(strict_types=1);
$nullableValue = null;
try {
$value = $nullableValue ?? throw new \InvalidArgumentException();
} catch (\InvalidArgumentException) {
dump("Something went wrong");
}
exit;
添加了对混合(RFC)类型的支持
PHP 8引入了一种称为混合的新类型。它可以等效于以下类型:数组,布尔,可调用,整数,浮点数,空值,对象,资源,字符串。
<?php
declare(strict_types=1);
function debug_function(mixed ...$data) {
dump($data);
}
debug_function(1, 'string', []);
exit;
增加了对属性的支持
对于在PHP 8中实现属性,有一些建议:
- https://wiki.php.net/rfc/attributes_v2
- https://wiki.php.net/rfc/attribute_amendments
- https://wiki.php.net/rfc/shorter_attribute_syntax
- https://wiki.php.net/rfc/shorter_attribute_syntax_change
这是PHP 8中最大的变化之一。乍一看可能并不容易。简而言之,属性允许您将元数据添加到PHP函数,参数,类等。然后可以以编程方式检索此元数据。如果在PHP 7或更低版本中需要解析时钟,则属性将帮助您访问已集成到PHP本身的信息。
为了更清楚地说明,假设您希望用户能够通过使用属性将中间件添加到类或方法控制器。
<?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 $middleware = [];
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);
}
exit;
添加了对转发构造函数属性(RFC)的支持
建议添加一种简单的语法以将构造函数与属性定义结合起来:
<?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);
exit;
添加了对匹配表达式(RFC)的支持
建议添加一个
match
类似的新表达式switch
,只是具有更安全的语义和返回值的功能。
<?php
declare(strict_types=1);
echo match (1) {
0 => 'Foo',
1 => 'Bar',
2 => 'Baz',
};
exit;
添加了对nullsafe运算符(?->)(RFC)的支持
当运算符左侧的结果为null时,将停止执行整个链,并将其结果设置为null。否则,该链的行为类似于普通运算符->
。
<?php
declare(strict_types=1);
class User {
public function getAddress() {}
}
$user = new User();
$country = $user?->getAddress()?->country?->iso_code;
dump($country);
exit;
增加了对命名参数(RFC)的支持
命名允许您根据参数的名称而不是其位置将参数传递给函数。也就是说,参数的值成为自记录的,并且参数不再依赖于枚举顺序,因此您可以任意跳过默认值。
<?php
declare(strict_types=1);
array_fill(start_index: 0, num: 100, value: 50);
exit;