Skip to content

PHP 8.0 新特性

PHP 8.0 是 PHP 语言的一次重大更新,引入了大量新特性,包括 JIT 编译器、联合类型、命名参数、match 表达式、属性(Attributes)、构造器属性提升、Nullsafe 运算符等。这些特性显著提升了 PHP 的表达能力和性能。

前置知识

阅读本节前,建议先了解:PHP 7.4 新特性 的基础知识,以及 类型系统

JIT 编译器(Just-In-Time)

JIT 概述

JIT 编译器在运行时将 PHP 字节码编译为机器码,跳过了解释执行的步骤,显著提升 CPU 密集型任务的性能。

ini
; php.ini
opcache.enable=1
opcache.enable_cli=1
opcache.jit=1255
opcache.jit_buffer_size=128M

JIT 配置模式

text
JIT 模式由 4 位数字组成:CRTO
C - CPU 特定的优化 (0=禁用, 1=AVX)
R - 寄存器分配 (0=不使用, 1=使用)
T - 触发器 (0=函数调用时, 1=首次执行时, 2=分析后, 4=首次调用时编译整个函数体, 5=脚本加载时)
O - 优化级别 (0=无优化, 1=最小优化, 2=基于类型推断的优化, 3=基于类型推断和调用图的优化, 4=函数内联, 5=优化已编译代码)

常用模式:
1255 - 最优性能(推荐用于生产环境)
1205 - 适合函数调用频繁的场景
tracing - 函数级别的 JIT
function - 脚本级别的 JIT

JIT 性能测试

php
<?php
declare(strict_types=1);

// 斐波那契数列 - JIT 显著提升的场景
function fibonacci(int $n): int
{
    if ($n <= 1) {
        return $n;
    }

    return fibonacci($n - 1) + fibonacci($n - 2);
}

$start = microtime(true);
echo fibonacci(35) . "\n";  // 9227465
$end = microtime(true);

echo "Time: " . ($end - $start) . " seconds\n";
// 无 JIT: ~4-5 秒
// 有 JIT: ~0.3-0.5 秒(10x 提升)

命名参数(Named Arguments)

基本语法

php
<?php
declare(strict_types=1);

// 传统位置参数
function createUser(string $name, string $email, string $role = 'user', bool $active = true): array
{
    return compact('name', 'email', 'role', 'active');
}

// PHP 8.0+ 命名参数
createUser(
    name: 'John',
    email: 'john@example.com',
    role: 'admin',
);

// 可以跳过可选参数
createUser(
    name: 'Jane',
    email: 'jane@example.com',
    active: false,  // 跳过 $role,直接设置 $active
);

// 参数顺序无关
createUser(
    role: 'editor',
    name: 'Bob',
    email: 'bob@example.com',
);

与内置函数配合

php
<?php
declare(strict_types=1);

// PHP 7.x - 必须记住参数顺序
array_slice($array, 0, 10, true);
array_fill(0, 5, 'default');
htmlentities($string, ENT_QUOTES, 'UTF-8');

// PHP 8.0+ - 使用命名参数更清晰
array_slice(array: $array, offset: 0, length: 10, preserve_keys: true);
array_fill(start_index: 0, num: 5, value: 'default');
htmlentities(string: $string, flags: ENT_QUOTES, encoding: 'UTF-8');

// 跳过不需要的参数
setcookie(
    name: 'session_id',
    value: 'abc123',
    httponly: true,     // 直接设置第 6 个参数
    secure: true,       // 直接设置第 7 个参数
    samesite: 'Strict', // 直接设置第 8 个参数
);

参数传播

展开运算符 ... 在 PHP 8.0+ 支持命名参数传播:

php
$params = ['role' => 'admin', 'active' => false];
createUser(name: 'John', email: 'john@example.com', ...$params);

联合类型(Union Types)

基本用法

php
<?php
declare(strict_types=1);

// PHP 8.0 联合类型
function processInput(int|string $input): string
{
    if (is_int($input)) {
        return "Integer: {$input}";
    }

    return "String: {$input}";
}

echo processInput(42);       // "Integer: 42"
echo processInput('hello');   // "String: hello"

// 联合类型支持 null(等同于 ?Type)
function getUserId(int|null $id): ?int
{
    return $id;
}

// 更复杂的联合类型
class OrderService
{
    public function findByStatus(Status|string|int $status): array
    {
        // int: 按 ID 查找
        // string: 按状态名查找
        // Status: 枚举查找
        return match (true) {
            $status instanceof Status => $this->findByEnum($status),
            is_int($status) => $this->findById($status),
            is_string($status) => $this->findByStatusName($status),
        };
    }
}

类型限制

php
<?php
declare(strict_types=1);

// ❌ 不允许的联合类型
function invalid1(void|int $x): void {}  // void 不能与其他类型联合
function invalid2(bool|true $x): void {} // true 是 bool 的子类型
function invalid3(int|self $x): void {}   // self 仅限于对象类型

// ✅ 允许的联合类型
function valid1(int|null $x): void {}     // 可以
function valid2(string|array $x): void {} // 可以
function valid3(DateTimeInterface|null $x): void {} // 接口联合

Match 表达式

基本用法

php
<?php
declare(strict_types=1);

// PHP 7.x switch
switch ($status) {
    case 'active':
        $color = 'green';
        break;
    case 'pending':
        $color = 'yellow';
        break;
    case 'inactive':
        $color = 'red';
        break;
    default:
        $color = 'gray';
}

// PHP 8.0+ match 表达式
$color = match ($status) {
    'active' => 'green',
    'pending' => 'yellow',
    'inactive' => 'red',
    default => 'gray',
};

Match vs Switch

php
<?php
declare(strict_types=1);

// 1. Match 使用严格比较(===)
$result = match ($value) {
    0 => 'zero',
    '0' => 'string zero',  // 与 0 不同
    true => 'boolean true',
};

// 2. Match 可以返回值
$statusLabel = match ($statusCode) {
    200, 201, 204 => 'Success',
    301, 302 => 'Redirect',
    400 => 'Bad Request',
    404 => 'Not Found',
    500 => 'Server Error',
    default => 'Unknown',
};

// 3. Match 支持条件表达式
$discount = match (true) {
    $totalAmount > 1000 => 0.15,
    $totalAmount > 500 => 0.10,
    $totalAmount > 100 => 0.05,
    default => 0.0,
};

// 4. Match 支持解构
$result = match ($point) {
    [0, 0] => 'Origin',
    [0, $y] => "On Y axis at {$y}",
    [$x, 0] => "On X axis at {$x}",
    [$x, $y] => "Point at ({$x}, {$y})",
};

// 5. Match 不会 fall-through(无需 break)
// 6. Match 如果没有匹配且没有 default,会抛出 UnhandledMatchError

属性(Attributes)

内置属性

php
<?php
declare(strict_types=1);

use ReturnTypeWillChange;
use JetBrains\PhpStorm\Deprecated;
use SensitiveParameter;

// 参数属性
#[Attribute]
class MyAttribute
{
    public function __construct(
        public readonly string $value,
        public readonly int $priority = 0,
    ) {}
}

// 使用属性
#[MyAttribute(value: 'important', priority: 10)]
class UserController
{
    #[Route('/api/users', methods: ['GET'])]
    #[RateLimit(limit: 100, window: '1 minute')]
    public function index(): array
    {
        return [];
    }

    #[Route('/api/users/{id}', methods: ['GET'])]
    #[Cache(ttl: 300)]
    public function show(int $id): array
    {
        return [];
    }

    #[Deprecated(reason: 'Use findUser() instead', replacement: 'findUser()')]
    public function getUser(int $id): ?User
    {
        return $this->findUser($id);
    }

    #[ReturnTypeWillChange]
    public function jsonSerialize(): array
    {
        return [];
    }
}

自定义属性

php
<?php
declare(strict_types=1);

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Cache
{
    public function __construct(
        public readonly int $ttl = 3600,
        public readonly ?string $key = null,
        public readonly array $tags = [],
    ) {}
}

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class RateLimit
{
    public function __construct(
        public readonly int $limit = 60,
        public readonly string $window = '1 minute',
    ) {}
}

// 读取属性
$reflectionMethod = new ReflectionMethod(UserController::class, 'index');
$attributes = $reflectionMethod->getAttributes();

foreach ($attributes as $attribute) {
    $instance = $attribute->newInstance();
    // $instance 是 Cache 或 RateLimit 对象
    if ($instance instanceof Cache) {
        echo "Cache TTL: {$instance->ttl}";
    }
}

构造器属性提升(Constructor Property Promotion)

php
<?php
declare(strict_types=1);

// PHP 7.x 写法
class UserOld
{
    private string $name;
    private string $email;
    private int $age;

    public function __construct(string $name, string $email, int $age)
    {
        $this->name = $name;
        $this->email = $email;
        $this->age = $age;
    }
}

// PHP 8.0+ 构造器属性提升
class User
{
    public function __construct(
        private readonly string $name,
        private readonly string $email,
        private readonly int $age,
        private readonly ?string $phone = null,
    ) {}

    public function getName(): string
    {
        return $this->name;
    }

    public function getEmail(): string
    {
        return $this->email;
    }
}

// 支持的类型修饰符
class Order
{
    public function __construct(
        public readonly int $id,         // public + readonly
        private string $status,          // private
        protected float $amount,          // protected
        private array $items = [],       // 默认值
    ) {}
}

Nullsafe 运算符

php
<?php
declare(strict_types=1);

// PHP 7.x - 多层 null 检查
$country = null;
if ($session !== null) {
    $user = $session->getUser();
    if ($user !== null) {
        $address = $user->getAddress();
        if ($address !== null) {
            $country = $address->getCountry();
        }
    }
}

// PHP 8.0+ Nullsafe 运算符
$country = $session?->getUser()?->getAddress()?->getCountry();

// 方法链调用
$result = $repository?->findBy(['status' => 'active'])?->first()?->getName();

// Nullsafe 不影响直接赋值
$name = $user?->getName();  // 如果 $user 为 null,$name = null

// ❌ 不能用于写入
$user?->name = 'John';  // 语法错误

字符串相关新函数

php
<?php
declare(strict_types=1);

// str_contains - 检查字符串是否包含子串
str_contains('Hello World', 'World');     // true
str_contains('Hello World', 'world');     // false (大小写敏感)

// str_starts_with - 检查是否以指定字符串开头
str_starts_with('Hello World', 'Hello');  // true
str_starts_with('Hello World', 'World');  // false

// str_ends_with - 检查是否以指定字符串结尾
str_ends_with('Hello World', 'World');    // true
str_ends_with('Hello World', 'Hello');    // false

// PHP 7.x 替代方案
strpos('Hello World', 'World') !== false;      // str_contains
substr('Hello World', 0, 5) === 'Hello';       // str_starts_with
substr('Hello World', -5) === 'World';         // str_ends_with

Throw 表达式

php
<?php
declare(strict_types=1);

// PHP 7.x - throw 是语句,只能在独立行使用
function validateAge(int $age): void
{
    if ($age < 0 || $age > 150) {
        throw new InvalidArgumentException('Invalid age');
    }
}

// PHP 8.0+ - throw 是表达式,可以在更多地方使用

// 三元运算符中
$value = $age > 0 ? $age : throw new InvalidArgumentException('Age must be positive');

// Null 合并运算符中
$config = $options['timeout'] ?? throw new RuntimeException('Timeout not configured');

// 箭头函数中
$users = array_filter($input, fn ($user) => $user->isActive()
    ?: throw new InvalidUserException('Inactive user'),
);

// Match 表达式中
$status = match ($code) {
    200 => 'OK',
    404 => throw new NotFoundException(),
    default => 'Unknown',
};

注意事项

向后兼容性

PHP 8.0 包含一些不兼容的变更:

  • @ 错误抑制运算符不再静默 fatal errors
  • 默认错误报告级别改为 E_ALL
  • 比较运算符对数字与非数字字符串的比较方式有变化
  • 移除了多个已废弃的功能

最佳实践

  1. 使用命名参数:提高代码可读性,特别是在调用内置函数时
  2. 使用 match 替代 switch:更安全、更简洁
  3. 使用构造器属性提升:减少样板代码
  4. 使用 Nullsafe 运算符:简化 null 检查链
  5. 使用属性替代注解:标准化的元数据方式
  6. 使用联合类型:更精确的类型声明

下一节

继续学习:PHP 8.1 新特性

参考链接