php7.4.x~php8.0.x 新特性
PHP 核心中的新特性
命名参数
新增 命名参数 的功能。
// array_fill(int $start_index, int $count, mixed $value): array
// 使用顺序传递参数:
array_fill(0, 100, 50);
// 使用命名参数:
array_fill(start_index: 0, count: 100, value: 50);
// 参数顺序不同的示例(同上例)
array_fill(value: 50, count: 100, start_index: 0);
注解
新增注解的功能。
语法:注解声明总是以 #[ 开头,以 ] 结尾来包围。 内部则是一个或以逗号包含的多个注解。
注解是一种在类、属性、方法等声明上方添加元数据的方式,可以在运行时通过反射 API 读取和处理。
// a.php
namespace MyExample;
use Attribute;
#[Attribute]
class MyAttribute
{
const VALUE = 'value';
private $value;
public function __construct($value = null)
{
$this->value = $value;
}
}
// b.php
namespace Another;
use MyExample\MyAttribute;
#[MyAttribute]
#[\MyExample\MyAttribute]
#[MyAttribute(1234)]
#[MyAttribute(value: 1234)]
#[MyAttribute(MyAttribute::VALUE)]
#[MyAttribute(array("key" => "value"))]
#[MyAttribute(100 + 200)]
class Thing
{
}
#[MyAttribute(1234), MyAttribute(5678)]
class AnotherThing
{
}
// 读取注解的示例:
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)]
class MyAnnotation {
public $value;
public function __construct($value) {
$this->value = $value;
}
}
#[MyAnnotation('Class Annotation')]
class MyClass {
#[MyAnnotation('Property Annotation')]
public $property;
#[MyAnnotation('Method Annotation')]
public function myMethod() {
// 方法体
}
}
$reflectionClass = new ReflectionClass(MyClass::class);
// 获取类上的注解
$classAttributes = $reflectionClass->getAttributes(MyAnnotation::class);
foreach ($classAttributes as $attribute) {
/** @var MyAnnotation $instance */
$instance = $attribute->newInstance();
echo "Class Annotation: " . $instance->value . PHP_EOL;
}
// 获取属性上的注解
$property = $reflectionClass->getProperty('property');
$propertyAttributes = $property->getAttributes(MyAnnotation::class);
foreach ($propertyAttributes as $attribute) {
/** @var MyAnnotation $instance */
$instance = $attribute->newInstance();
echo "Property Annotation: " . $instance->value . PHP_EOL;
}
// 获取方法上的注解
$method = $reflectionClass->getMethod('myMethod');
$methodAttributes = $method->getAttributes(MyAnnotation::class);
foreach ($methodAttributes as $attribute) {
/** @var MyAnnotation $instance */
$instance = $attribute->newInstance();
echo "Method Annotation: " . $instance->value . PHP_EOL;
}
构造器属性提升
新增在构造函数中声明类的属性的支持。
// 使用构造器参数
class Point {
protected int $x;
protected int $y;
public function __construct(int $x, int $y = 0) {
$this->x = $x;
$this->y = $y;
}
}
// **使用构造器属性提升**
class Point2 {
public function __construct(protected int $x, protected int $y = 0) {
}
public function getX() {
return $this->x;
}
}
// 两个参数都传入
$p1 = new Point2(1, 5);
// 仅传入必填的参数。 $y 会默认取值 0。
$p2 = new Point2(2);
// 使用命名参数(PHP 8.0 起):
$p3 = new Point2(y: 5, x: 3);
var_dump($p1->getX());
var_dump($p2->getX());
var_dump($p3->getX());
当构造器参数带修饰符时,PHP 会同时把它当作对象属性和构造器参数, 并赋值到属性。 构造器可以是空的,或者包含其他语句。
并非所有参数都需要提升。可以混合提升或不提升参数作为属性,也不需要按顺序。 提升后的参数不影响构造器内代码调用。
联合类型
新增 联合类型。
class A {}
function f(A|string $c) {
var_dump($c);
}
f(new A); // object(A)#1 (0) {}
f('a'); // a
null 类型语法糖,单个基本类型声明可以通过在类型前添加问号(?)来标记可为 null,?T 和 T|null 是相同的。
// 使参数可以为 null 的旧方式
class A {}
function f(A $c = null) {
var_dump($c);
}
f(new A); // object(A)#1 (0) {}
f(null); // null
class A {}
function f(?A $c) {
var_dump($c);
}
f(new A); // object(A)#1 (0) {}
f(null); // null
match 表达式
新增 match 表达式。
match表达式和 switch 语句类似, 都有一个表达式主体,可以和多个可选项进行比较。 与 switch 不同点是,它会像三元表达式一样求值。 与 switch 另一个不同点,它的比较是严格比较( =)而不是松散比较()。
$food = 'cake';
$return_value = match ($food) {
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
};
var_dump($return_value);
Nullsafe 运算符
新增 Nullsafe 运算符(?->)。
对象引用解析(dereference)为 null 时不抛出异常,而是返回 null。 并且如果是链式调用中的一部分,剩余链条会直接跳过。
$repository = null;
// Nullsafe 运算符
$result = $repository?->getUser(5)?->name;
var_dump($result);
// 上边代码等价于以下代码
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
其他新特性
- 新增 WeakMap 类。
WeakMap 是将对象作为 key 来访问的 map(或者说字典), WeakMap 中的对象 key 不影响对象的引用计数。
class A {
public function __destruct() {
echo "A!\n";
}
}
$wm = new WeakMap();
$o = new stdClass;
$wm[$o] = new A;
var_dump(count($wm));
unset($o);
var_dump(count($wm));
-
新增 ValueError 类。
当参数类型正确但是值不正确的时候会抛出 ValueError。 -
现在,只要类型兼容,任意数量的函数参数都可以用一个可变参数替换。例如允许编写下面的代码:
class A {
public function method(int $many, string $parameters, $here) {}
}
class B extends A {
public function method(...$everything) {}
}
- static(“后期静态绑定”中)可以作为返回类型:
class Test {
public function create(): static {
return new static();
}
}
- 现在可以通过 $object::class 获取类名,返回的结果和 get_class($object) 一致。
class A {
public function method(int $many, string $parameters, $here) {}
}
$a = new A();
var_dump($a::class);
var_dump(get_class($a));
-
new、instanceof 可用于任何表达式,用法为 new (expression)(...$args) 和 $obj instanceof (expression)。
?
-
添加对一些变量语法一致性的修复,例如现在能够编写 Foo::BAR::$baz。
-
添加 Stringable interface,当类定义 __toString() 方法后会自动实现该接口。
-
Trait 可以定义私有抽象方法(abstract private method)。类必须实现 trait 定义的该方法。
-
可作为表达式使用 throw。使得可以编写以下用法:
$fn = fn() => throw new Exception('Exception in arrow function');
$user = $session->user ?? throw new Exception('Must have user');
- 参数列表中的末尾逗号为可选。
function functionWithLongSignature(
Type1 $parameter1,
Type2 $parameter2, // <-- 这个逗号也被允许了
) {
- 现在允许 catch (Exception) exception 而无需捕获到变量中。
try {
throw new \Exception('AAA!');
} catch (\Exception) {
}
-
支持 mixed 类型。
mixed 类型接受每个值。等同于联合类型 object|resource|array|string|float|int|bool|null。自 PHP 8.0.0 起可用。
在类型理论中,mixed 是顶级类型。这意味着其它所有类型都是它的子类型。 -
父类中声明的私有方法不在对子类中的方法执行任何继承规则(final private 构造方法除外)。下列示例说明删除了那些限制:
class ParentClass {
private function method1() {}
private function method2() {}
private static function method3() {}
// private final function method4() {}
}
class ChildClass extends ParentClass {
// 现在允许以下所有内容,即使修饰符与父类中的私有方法不同。
public function method1() {}
public static function method2() {}
public function method3() {}
public function method4() {}
}
-
新增 get_resource_id() 函数,返回值跟 (int) $resource 相同。其在更清晰的 API 下提供了相同的功能。
-
新增 InternalIterator 类。
标准库
-
新增 str_contains()、str_starts_with() 和 str_ends_with(),分别检查 haystack 是否包含 needle 或以 needle 开头/结尾。
-
新增 fdiv() 在 IEEE 754 语义下执行浮点除法。认为除以零已经明确定义,将返回 Inf、-Inf 或 NaN。
$b = fdiv(10, 0);
echo $b; // INF
-
新增 get_debug_type() 返回对错误消息有用的类型。与 gettype() 不同的是,它使用规范的类型名称,为对象返回类名,为资源表示资源类型。
-
printf() 和相关函数现在支持 %h 和 %H 格式说明符。它们与 %g 和 %G 相同,但始终使用 "." 作为小数点分隔符,而不是通过 LC_NUMERIC 区域确定。
-
现在,printf() 和相关函数支持使用 "*" 作为宽度或精度,此时宽度/精度将作为参数传递给 printf。这也允许在 %g、%G、%h 和 %H 中使用精度 -1。例如,以下代码可用于重现 PHP 的默认浮点数格式化:
printf("%.*H", (int) ini_get("precision"), $float);
printf("%.*H", (int) ini_get("serialize_precision"), $float);
- 现在,proc_open() 支持伪终端(PTY)描述符。以下将 stdin、stdout 和 stderr 附加到同一个 PTY.
$proc = proc_open($command, [['pty'], ['pty'], ['pty']], $pipes);
- proc_open() 现在支持套接字对描述符。以下将独立的套接字对附加到 stdin、stdout 和 stderr.
$proc = proc_open($command, [['socket'], ['socket'], ['socket']], $pipes);
-
排序函数现在已稳定,这意味着相等的元素比较将保留其原始顺序。
-
array_diff() 和 array_intersect() 及其变体现在可以接受单个数组作为参数。
// 如果 $excludes 为空也可以:
array_diff($array, ...$excludes);
// 如果 $arrays 仅包含单个数组也可以:
array_intersect(...$arrays);
var_dump(array_diff(['a','b']));
- ob_implicit_flush() 的 flag 参数已经从接受 int 变更为接受 bool。
DOM
Filter
Enchant
FPM
Hash
LDAP
OPcache
OpenSSL
正则表达式(兼容 Perl)
反射
SQLite3
Tokenizer
Zip
PHP 8.0 废弃的功能
PHP 核心中废弃的功能
- 如果带有默认值的参数后面跟着一个必要的参数,那么默认值就会无效。这在 PHP 8.0.0 中已被废弃,通常可以通过删除默认值,不影响现有功能:
function test($a = [], $b) {} // 之前
function test($a, $b) {} // 之后
PHP8.2:
Deprecated: Optional parameter $a declared before required parameter $b is implicitly treated as a required parameter
标准库
- 排序比较函数现在返回 true 或者 false 将会引发弃用警告,应替换为返回小于、等于或者大于零的 int 值。
// 替换
usort($array, fn($a, $b) => $a > $b);
// 为
usort($array, fn($a, $b) => $a <=> $b);
Enchant
LibXML
PGSQL/PDO PGSQL
Zip
反射
https://www.php.net/manual/zh/migration80.new-features.php
https://www.php.net/manual/zh/migration80.deprecated.php