命名参数
PHP 8.0.0 开始引入了命名参数作为现有位置参数的扩展。命名参数允许根据参数名而不是参数位置向函数传参。这使得参数的含义自成体系,参数与顺序无关,并允许任意跳过默认值。
命名参数通过在参数名前加上冒号来传递。允许使用保留关键字作为参数名。参数名必须是一个标识符,不允许动态指定。
示例1:
// 使用顺序传递参数:
array_fill('hello', 'hi', 'hello world'); //hi world
// 使用命名参数:
str_replace(subject:'hello world',search:'hello',replace:'hi'); //hi world
示例2:解包后使用命名参数
function foo($a, $b, $c = 3, $d = 4) {
return $a + $b + $c + $d;
}
var_dump(foo(...[1, 2], d: 40)); // 46
var_dump(foo(...['b' => 2, 'a' => 1], d: 40)); // 46
var_dump(foo(...[1, 2], b: 20)); // Fatal error。命名参数 $b 覆盖之前的参数
注解(Attributes)
注解功能提供了代码中的声明部分都可以添加结构化、机器可读的元数据的能力, 注解的目标可以是类、方法、函数、参数、属性、类常量。 通过 反射 API 可在运行时获取注解所定义的元数据。 因此注解可以成为直接嵌入代码的配置式语言。
注解语法
注解语法包含以下几部分。 首先,注解声明总是以#[
开头,以]
结尾来包围。 内部则是一个或以逗号包含的多个注解。 注解的名称可以是非限定、限定、完全限定的名称。 注解的参数是可以选的,以常见的括号()
包围。 注解的参数可以是字面值或者常量表达式。 它同时接受位置参数和命名参数两种语法。
通过反射API请求注解实例时,注解的名称会被解析到一个类,注解的参数则传入该类的构造器中。 因此每个注解都需要引入一个类。
示例:
namespace MyExample;
use Attribute;
#[Attribute]
class MyAttribute
{
const VALUE = 'value';
private $value;
public function __construct($value = null)
{
$this->value = $value;
}
}
使用反射 API 读取注解
反射 API 提供了getAttributes()
方法, 类、方法、函数、参数、属性、类常量的反射对象可通过它获取相应的注解。 该方法返回了 ReflectionAttribute
实例的数组, 可用于查询注解名称、参数、也可以实例化一个注解。
示例 #1 通过反射 API 读取注解:
#[Attribute]
class MyAttribute
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}
#[MyAttribute(value: 1234)]
class Thing
{
}
function dumpAttributeData($reflection) {
$attributes = $reflection->getAttributes();
foreach ($attributes as $attribute) {
var_dump($attribute->getName());
var_dump($attribute->getArguments());
var_dump($attribute->newInstance());
}
}
dumpAttributeData(new ReflectionClass(Thing::class));
/*
string(11) "MyAttribute"
array(1) {
["value"]=>
int(1234)
}
object(MyAttribute)#3 (1) {
["value"]=>
int(1234)
}
*/
示例 #2 使用反射 API 读取指定的注解
function dumpMyAttributeData($reflection) {
$attributes = $reflection->getAttributes(MyAttribute::class);
foreach ($attributes as $attribute) {
var_dump($attribute->getName());
var_dump($attribute->getArguments());
var_dump($attribute->newInstance());
}
}
dumpMyAttributeData(new ReflectionClass(Thing::class));
声明注解类
虽然没有严格要求,推荐为每个注解创建一个实际的类。 在这个最简单的例子中,通过use
语法从全局命名空间引入#[Attribute]
注解所需要全空的类。
示例 #1 简单的 Attribute 类
namespace Example;
use Attribute;
#[Attribute]
class MyAttribute
{
}
构造器属性提升
PHP 8.0.0 起,构造器的参数也可以相应提升为类的属性。 构造器的参数赋值给类属性的行为很普遍,否则无法操作。 而构造器提升的功能则为这种场景提供了便利。
常规写法:
class Point {
protected int $x;
protected int $y;
public function __construct(int $x, int $y = 0) {
$this->x = $x;
$this->y = $y;
}
}
上面的例子可以用以下方式重写:
class Point {
public function __construct(protected int $x, protected int $y = 0) {
}
}
联合类型
联合类型是PHP8中的一个新特性,它允许一个变量可以拥有多个不同的类型。具体来说,当一个变量的类型是一个联合类型时,它可以是其中任意一个类型。例如:
function foo(string|array $var)
{
// $var可以是一个字符串或者数组
}
Match 表达式
match 表达式基于值的一致性进行分支计算。 match表达式和 switch 语句类似, 都有一个表达式主体,可以和多个可选项进行比较。 与 switch 不同点是,它会像三元表达式一样求值。 与 switch 另一个不同点,它的比较是严格比较( ===)而不是松散比较(==)。 Match 表达式从 PHP 8.0.0 起可用。
示例 #1 match 的基础用法
$food = 'cake';
$return_value = match ($food) {
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
}; //注意: match 表达式必须使用分号 ; 结尾。
var_dump($return_value);
以上示例会输出:
string(19) "This food is a cake"
match 表达式跟 switch 语句相似,但是有以下关键区别:
– match 比较分支值,使用了严格比较 (===), 而 switch 语句使用了松散比较。
– match 表达式会返回一个值。
– match 的分支不会像 switch 语句一样, 落空时执行下个 case。
– match 表达式必须彻底列举所有情况。
示例2:
$condition = 5;
$expressionResult = match ($condition) {
1, 2 => foo(),
3, 4 => bar(),
default => baz(),
};
var_dump($expressionResult);die;
function foo()
{
return 'foo';
}
function bar()
{
return 'bar';
}
function baz()
{
return 'baz';
}
以上示例会输出:
string(3) "baz"
Nullsafe 运算符(?->)
自 PHP 8.0.0 起,类属性和方法可以通过 “nullsafe” 操作符访问: ?->
。 使用上与原来的->
操作符访问类的属性和方法是一致的,特别之处是当对象引用解析为null
时不抛出异常,而是返回null
。 并且如果是链式调用中的一部分,剩余链条会直接跳过。
此操作的结果,类似于在每次访问前使用is_null()
函数判断方法和属性是否存在,但更加简洁。
示例:
$result = $repository?->getUser(5)?->name;
等价于以下代码:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
任意数量的函数参数都可以用一个可变参数替换
例如允许编写下面的代码:
class A {
public function method(int $many, string $parameters, $here) {}
}
class B extends A {
public function method(...$everything) {}
}
str_contains
确定字符串是否包含指定子串。区分大小写。
示例:
$string = 'The lazy fox jumped over the fence';
if (str_contains($string, 'lazy')) {
echo "The string 'lazy' was found in the stringn";
}
if (str_contains($string, 'Lazy')) {
echo 'The string "Lazy" was found in the string';
} else {
echo '"Lazy" was not found because the case does not match';
}
以上示例会输出:
The string 'lazy' was found in the string
"Lazy" was not found because the case does not match
str_starts_with
检查字符串是否以指定子串开头。区分大小写。
示例:
$string = 'The lazy fox jumped over the fence';
if (str_starts_with($string, 'The')) {
echo "The string starts with 'The'n";
}
if (str_starts_with($string, 'the')) {
echo 'The string starts with "the"';
} else {
echo '"the" was not found because the case does not match';
}
以上示例会输出:
The string starts with 'The'
"the" was not found because the case does not match
str_ends_with
检查字符串是否以指定子串结尾。区分大小写。
$string = 'The lazy fox jumped over the fence';
if (str_ends_with($string, 'fence')) {
echo "The string ends with 'fence'n";
}
if (str_ends_with($string, 'Fence')) {
echo 'The string ends with "Fence"';
} else {
echo '"Fence" was not found because the case does not match';
}
MurmurHash3
MurmurHash是一种经过广泛测试且速度很快的非加密哈希函数。MurmurHash3可以产生32位或128位哈希。
MurMurHash3 128 位版本的速度是 MD5 的十倍。MurMurHash3 生成 32 位哈希的用时比生成 128 位哈希的用时要长。原因在于生成 128 位哈希的实现受益于现代处理器的特性。32 位哈希值发生碰撞的可能性就比 128 位的要高得多,当数据量达到十万时,就很有可能发生碰撞。
PHP8.1新增对MurmurHash3
(支持流)的支持。有以下形式提供:
murmur3a, 32-bit hash
murmur3c, 128-bit hash for x86
murmur3f, 128-bit hash for x64
hash 初始状态可以通过键为 seed 的 options 数组传递,例如:
$data = 'hello';
$h = hash("murmur3f", $data, options: ["seed" => 42]);
var_dump($h);
seed 的有效值范围为 0 到系统定义的 UINT_MAX,通常是 4294967295 。