简介:
自 PHP 8.0 发布以来,该语言经历了一系列重要的功能迭代与性能改进。对于开发者而言,系统理解这些新特性不仅是技术更新的需要,更是优化代码设计、降低维护成本的有效途径。本分类将按版本梳理 PHP 8.x 的新特性。
8.0官方手册参考指南(含其他特性):www.php.net/releases/8....
一、命名参数
命名参数是 PHP8.0 引入的核心特性之一,它允许你在调用函数 / 方法时,通过参数名称而非参数位置来传递值。彻底解决了传统 "位置参数" 需要严格按顺序传参、默认参数必须按顺序跳过的痛点。这是命名参数最核心的优势,尤其适合参数多、默认值多的函数/方法,大幅提升代码的可读性和灵活性。
语法示例:
php
<?php
/**
* 命名参数语法:参数名: 值
* functionName(parameterName: $value);
* 其中:
* 1.参数名必须是标识符,不能动态指定(如变量存储参数名)
* 2.允许使用保留关键字作为参数名
* 3.命名参数与位置参数混合时,位置参数必须在前
* 4.禁止重复传递,同一个参数不能既按位置又按命名传递(会抛出 `Error`)
* 5.参数名大小写敏感
*/
class Example
{
public function test(string $name, int $age = 18, string $explain = ''): array
{
return [
'name' => $name,
'age' => $age,
'explain' => $explain,
];
}
}
$example = new Example();
echo '<pre>';
// 常规位置参数,存在中间默认参数时无法省略指定参数
var_dump($example->test('小寒', 18, '这是位置参数'));
// 命名传参,可以跳过默认参数:只传需要的参数,同时也可以不按照顺序传参
var_dump($example->test( explain: '这是命名参数',name: '小寒'));
// 混合传参(位置参数必须在前),可以省略指定参数,同时也可以不按照顺序传参
var_dump($example->test('小寒', explain: '这是混合参数', age: 20));
二、注解 (Attributes)
注解(Attributes)是 PHP8 引入的原生语言特性,用于在代码(类、方法、属性、参数等)上附加元数据,且能通过反射 API 安全、便捷地读取这些元数据。彻底替代了传统 "注释式注解"(如 @Route @param 这类写在 PHPDoc 里的伪注解),实现了「结构化元数据 + 类型安全 + 反射可读取」的元编程能力。解决了纯文本注释解析易出错、类型不安全的问题,是 PHP 元编程(如路由、ORM、数据验证)的核心基础。
语法:注解语法由几个关键组件组成。属性声明以 #[ 开始,以 ] 结束。内部可以列出一个或多个注解,注解之间用逗号分隔。注解名称如使用命名空间基础中所述,可以是未限定、限定或完全限定的。注解的参数是可选的,并用圆括号 () 括起来。参数只能是字面值或常量表达式,同时支持位置参数和命名参数语法。
ThinkPHP8 路由注解示例:
php
<?php
declare(strict_types=1);
namespace app\controller\api\v1;
use app\BaseController;
use think\annotation\route\Route;
class Index extends BaseController
{
/**
* 路由文件配置为: Route::get('test','api.v1.index/index');
* 访问地址为: http://localhost/test
*
* 直接使用thinkphp 8 注解实现上述访问:
* 安装扩展:composer require topthink/think-annotation
* 在控制器方法上添加注解: #[Route("GET", "test")],并且注释掉路由文件中的 Route::get('test','api.v1.index/index');
*
* 注:目前(2026-05-07)composer安装的组件在多层子目录下无法解析,例如 namespace app\controller\api\v1 注解会提示类不存在;
* 解决方案:官方github上已修复:https://github.com/top-think/think-annotation/blob/3.0/src/InteractsWithRoute.php #116 行
* 但目前composer 安装暂未同步,需手动替换InteractsWithRoute.php #116行代码
*
*/
#[Route("GET", "test")]
public function index()
{
dd('hello world test-url');
}
}
数据验证与过滤示例:
php
<?php
declare(strict_types=1);
#[Attribute(Attribute::TARGET_PROPERTY)]
class Required
{
/**
* 验证属性是否非空。
*
* 规则:当值为 null 或空字符串时视为无效。
* 如果需要对数组做空检查,可在实现中扩展。
*
* @param mixed $value 要验证的属性值
* @throws Exception 当值为空时抛出异常(示例使用通用 Exception)
*/
public function validate(mixed $value): void
{
if ($value === null || $value === '') {
throw new Exception('字段不能为空');
}
}
}
#[Attribute(Attribute::TARGET_PROPERTY)]
class Email
{
/**
* 验证属性值是否为合法邮箱地址。
*
* 允许空值通过(由 Required 注解控制非空),因此本方法仅在值存在且非空时执行格式校验。
*
* @param mixed $value 要验证的属性值
* @throws Exception 当值存在但格式不符合邮箱规范时抛出异常
*/
public function validate(mixed $value): void
{
if ($value !== null && $value !== '' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new Exception('邮箱格式不正确');
}
}
}
class User
{
#[Required]
public string $name;
#[Required]
#[Email]
public string $email;
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
}
}
function validate(object $obj): void
{
// 创建反射对象,用于在运行时检查传入对象的结构和元数据
$reflection = new ReflectionObject($obj);
// 遍历对象的每个属性(ReflectionProperty 对象)
foreach ($reflection->getProperties() as $property) {
// 从传入的对象实例中读取该属性当前的实际值
$value = $property->getValue($obj);
// 读取该属性上的所有 Attribute(注解)元信息
foreach ($property->getAttributes() as $attribute) {
// 将 Attribute 信息实例化为注解对象(例如 Required、Email 的实例)
// newInstance() 会调用注解类的构造函数并返回对象
$validator = $attribute->newInstance();
// 调用注解对象的 validate 方法,将当前属性值传入执行验证逻辑
// 如果注解认为值不合法,会抛出异常(此处使用 Exception/ValidationException)
$validator->validate($value);
}
}
}
try {
$user = new User('小寒', 'xiaohan@example.com');
validate($user);
echo "验证通过\n";
} catch (\Exception $e) {
echo "验证失败:" . $e->getMessage() . "\n";
}
三、构造器属性提升
构造器属性提升核心作用是简化类的属性声明和初始化流程。在 PHP8.0 之前,定义类属性并通过构造函数赋值需要分三步:声明属性、定义构造函数参数、在构造函数内赋值。而构造器属性提升允许你在构造函数参数中直接声明类属性,PHP 会自动完成「属性定义 + 构造函数赋值」,大幅减少冗余代码。
语法与示例:
php
<?php
declare(strict_types=1);
class Example
{
// 传统写法:需要在类体先声明属性
public array $oldParam;
public function __construct(
array $oldParam = [], // 传统参数:需手动赋值到 $this->oldParam
public string $newParam = 'default value' // 提升属性:自动成为类的公有属性并被赋值
) {
$this->oldParam = $oldParam; // 再赋值
}
}
$example = new Example(['x', 'y'], 'custom value');
echo '<pre>';
var_dump($example->oldParam); // 输出: array(2) { [0]=> string(1) "x" [1]=> string(1) "y" }
var_dump($example->newParam); // 输出: string(12) "custom value"
四、联合类型
联合类型是 PHP8.0 正式引入的核心类型特性,它允许为属性、函数 / 方法参数、返回值 声明「可以接受 / 返回多种不同类型」,解决了 PHP7 及以下版本中只能通过 mixed(过于宽泛)或手动类型检查来支持多类型的痛点,让类型声明更精准、代码更健壮。
语法:联合类型用 | 分隔多个类型,表示「值可以是这些类型中的任意一种」
示例:
php
<?php
declare(strict_types=1);
class Example
{
public function __construct(
public array|string $params = [] // 构造器属性提升 + 联合类型
) {}
}
$example = new Example('single');
var_dump($example->params); // 输出: string(6) "single"
五、match表达式
match 是 PHP8.0 引入的模式匹配表达式(区别于 switch 语句),核心作用是替代传统 switch 语句,解决其「松散比较、需要手动 break、无原生返回值」等痛点。
核心特性:
a. 是表达式(可直接赋值给变量、作为函数返回值),而非语句。
b. 采用严格类型比较(===),而非 switch 的松散比较(==)。
c. 匹配成功后自动返回分支结果,无需 break(无 fall-through 穿透行为)。
d. 支持多值匹配(多个值映射到同一分支)。
e. 可选默认分支(处理未匹配的情况)。
示例:
php
<?php
declare(strict_types=1);
// 定义一个函数,使用 match 表达式来格式化状态
function formatStatus(string $status): string
{
return match ($status) {
'pending' => '等待处理',
'approved' => '已通过',
'rejected' => '已拒绝',
default => '未知状态',
};
}
// 直接使用 match 表达式
$code = 401;
$message = match ($code) {
401, 403, 407 => 'Authentication Error',
404, 410 => 'Resource Gone',
default => 'Something Else',
};
echo '<pre>';
var_dump($message); // 输出: string(16) "Authentication Error"
var_dump(formatStatus('approved')); // 输出: string(12) "已通过"
六、Nullsafe 运算符
Nullsafe 运算符(?->)是 PHP8.0 引入的核心语法糖,专门解决链式调用中某个环节为 null 导致的致命错误。在 PHP8.0 之前,若链式调用的某个对象 / 属性为 null,直接调用其方法 / 属性会抛出 Error错误;而 Nullsafe 运算符会自动检查左侧值是否为 null:
- 若左侧值为
null:终止后续链式调用,直接返回null - 若左侧值非
null:正常执行后续调用(等价于普通->)
核心作用:用极简语法替代多层 isset()/empty() 判空逻辑,大幅简化链式调用的容错代码。
语法示例:
php
<?php
declare(strict_types=1);
// 简单示例:Nullsafe 运算符用于在对象为 null 时安全访问方法或属性。
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
}
echo '<pre>';
$calc = null;
// 这里 $calc 为 null,$calc?->add(1, 2) 不会调用 add,结果为 null
$result = $calc?->add(1, 2);
var_dump($result); // 输出: NULL
// 使用 null 合并运算符提供默认值
$result = $calc?->add(1, 2) ?? '默认值';
var_dump($result); // 输出: string(12) "默认值"
$calc = new Calculator();
// 这里 $calc 不为 null,方法会正常执行
$result = $calc?->add(1, 2);
var_dump($result); // 输出: int(3)
注意事项:
仅检查「左侧值是否为 null」,不检查是否为对象;无「穿透」行为 仅终止当前链式节点,不影响外层;不支持静态方法 / 属性调用;不能用于赋值操作
七、字符串与数字的比较更符合逻辑
PHP8.0 重构了「数字与字符串」的松散相等性比较(==/!=) 规则,核心目标是让比较结果贴合日常认知:
- 数字 vs 纯数字字符串:仍按数字方式比较(保持兼容,如
"123" == 123→true) - 数字 vs 非纯数字字符串: 不再转数字,而是将数字转为字符串,按「字符串逐字符对比 ASCII 值」比较('123abc' == 0 → false)
- 严格比较(
===/!==):规则不变(必须值 + 类型完全一致)
补充说明:PHP8.0 大小比较(</>/<=/>=)规则不变。
php
<?php
declare(strict_types=1);
echo '<pre>';
var_dump('abc' == 0); // false 非数字字符串不能转换为有效数字
var_dump('123abc' == 123); // false 字符串前缀不是纯数字
var_dump('123' == 123); // true 字符串被转换为数字后相等
var_dump('123' === 123); // false 类型不同
var_dump('' == 0); // false 空字符串与数字 0 不视为相等
八、函数类型错误的一致性
PHP8.0 重构了内部函数的参数校验逻辑,核心目标是统一类型错误处理规则,让所有内部函数的类型错误行为保持一致:
a. 错误级别升级:所有内部函数接收到「不符合参数类型声明」的参数时,不再抛出 Warning,而是一致抛出 TypeError 异常(脚本默认终止,可通过 try-catch 捕获);
b. 异常类型统一:类型错误均抛出标准的 TypeError(而非自定义异常),异常信息标准化(包含函数名、参数位置、预期类型、实际类型);
c. 严格遵循参数类型声明:内部函数的参数类型(如 strlen() 预期 string、array_push() 预期 array)被明确固化,不符合则抛异常;
d. 兼容安全的隐式转换:仅当参数可「安全隐式转换」为预期类型时(如数字转字符串),仍允许转换(避免过度破坏兼容性)
示例:
php
<?php
declare(strict_types=1);
echo '<pre>';
// print_r(strlen([123])); // TypeError
/*开启 declare(strict_types=1); 则抛出TypeError, 否则会被转换成字符串 '123',输出 3*/
// print_r(strlen(123));
print_r(strlen('123')); // 3