PHP 8.4 新特性
PHP 8.4 是一次重大更新,引入了属性钩子(Property Hooks)、不对称可见性(Asymmetric Visibility)、DOM 命名空间、new MyClass()->method() 链式调用、数组化函数等重量级特性,大幅简化了样板代码并提升了语言表达力。
前置知识
阅读本节前,建议先了解:PHP 8.3 新特性
属性钩子(Property Hooks)
基本概念
属性钩子允许在不使用 getter/setter 方法的情况下,对属性的读取和写入进行拦截和自定义。这是 PHP 8.4 最核心的新特性。
php
<?php
declare(strict_types=1);
class User
{
// 使用属性钩子替代 getter/setter
public string $name {
get => strtoupper($this->name);
set => $value;
}
// 仅读钩子(只有 get)
public string $fullName {
get => "{$this->firstName} {$this->lastName}";
}
// 带验证的 set 钩子
public int $age {
set(int $value) {
if ($value < 0 || $value > 150) {
throw new ValueError("Age must be between 0 and 150");
}
$this->age = $value;
}
}
public function __construct(
private string $firstName,
private string $lastName,
) {}
}
$user = new User('John', 'Doe');
echo $user->fullName; // "John Doe"
$user->age = 25; // 正常
$user->age = 200; // 抛出 ValueError虚拟属性(Computed Properties)
php
<?php
declare(strict_types=1);
class Rectangle
{
public float $width;
public float $height;
// 计算属性(没有实际的存储)
public readonly float $area {
get => $this->width * $this->height;
}
public readonly float $perimeter {
get => 2 * ($this->width + $this->height);
}
public readonly bool $isSquare {
get => $this->width === $this->height;
}
public function __construct(float $width, float $height)
{
$this->width = $width;
$this->height = $height;
}
}
$rect = new Rectangle(5, 3);
echo $rect->area; // 15
echo $rect->perimeter; // 16
echo $rect->isSquare; // false钩子中的 this 和 value
php
<?php
declare(strict_types=1);
class Product
{
// $this->name 访问底层存储的值
// $value 是 set 钩子接收的新值
public string $name {
set(string $value) {
$value = trim($value);
if (strlen($value) < 2) {
throw new ValueError("Name must be at least 2 characters");
}
$this->name = ucfirst($value);
}
}
// get 钩子中可以使用 $this->name
public string $displayName {
get => "[PRODUCT] {$this->name}";
}
}不对称可见性
php
<?php
declare(strict_types=1);
// PHP 8.4+ 允许属性的读和写有不同的可见性
class User
{
// public 可读,private 可写
public private(set) string $name;
// public 可读,protected 可写
public protected(set) int $loginCount = 0;
// public 可读,private 可写
public private(set) DateTimeImmutable $createdAt;
public function __construct(string $name)
{
$this->name = $name;
$this->createdAt = new DateTimeImmutable();
}
// 受保护的方法可以修改 loginCount
public function incrementLoginCount(): void
{
$this->loginCount++;
}
}
$user = new User('John');
echo $user->name; // ✅ public,外部可读
// $user->name = 'Jane'; // ❌ private(set),外部不可写
$user->incrementLoginCount(); // ✅ 内部可写
// 子类中
class AdminUser extends User
{
public function resetLoginCount(): void
{
$this->loginCount = 0; // ✅ protected(set),子类可写
}
// ❌ private(set) 子类也不能写
// public function rename(string $name): void {
// $this->name = $name; // Error
// }
}DOM 命名空间
php
<?php
declare(strict_types=1);
// PHP 8.4+ DOM API 有了命名空间化的版本
// 新的 DOM 命名空间:DOM\*
// 旧的 DOM\* 类被移到命名空间下
use DOM\Document;
use DOM\Element\HTMLElement;
use DOM\NodeList;
// 创建文档
$doc = new Document();
$doc->loadHTML('<html><body><h1>Hello World</h1></body></html>');
// 使用新的 DOM API(支持现代 DOM 标准)
$h1 = $doc->querySelector('h1');
echo $h1->textContent; // "Hello World"
// 遍历元素
$elements = $doc->querySelectorAll('p');
foreach ($elements as $element) {
echo $element->textContent . "\n";
}链式调用 new MyClass()->method()
php
<?php
declare(strict_types=1);
// PHP 8.4+ 允许在 new 表达式后直接调用方法
// PHP 8.3 及之前
$result = (new UserRepository())->findById(1);
$config = (new Config())->load();
// PHP 8.4+
$result = new UserRepository()->findById(1);
$config = new Config()->load();
// 链式调用
$users = new UserCollection()
->filter(fn ($u) => $u->isActive())
->map(fn ($u) => $u->getName())
->toArray();
// 嵌套 new
$service = new Service(
new Repository(
new DatabaseConnection('localhost'),
),
);数组化函数
php
<?php
declare(strict_types=1);
// PHP 8.4+ array_find, array_find_key, array_any, array_all
// array_find - 返回第一个满足条件的元素
$users = [
['id' => 1, 'name' => 'John'],
['id' => 2, 'name' => 'Jane'],
['id' => 3, 'name' => 'Bob'],
];
$user = array_find($users, fn ($u) => $u['name'] === 'Jane');
// ['id' => 2, 'name' => 'Jane']
$user = array_find($users, fn ($u) => $u['name'] === 'Unknown');
// null(未找到)
// array_find_key - 返回第一个满足条件的元素的键
$key = array_find_key($users, fn ($u) => $u['name'] === 'Jane');
// 1
// array_any - 检查是否有元素满足条件
$hasActive = array_any($users, fn ($u) => $u['id'] > 1);
// true
// array_all - 检查是否所有元素都满足条件
$allPositive = array_all([1, 2, 3, 4], fn ($n) => $n > 0);
// true
// PHP 7.x 替代方案
// array_find: array_values(array_filter($arr, $fn))[0] ?? null
// array_any: count(array_filter($arr, $fn)) > 0
// array_all: count(array_filter($arr, $fn)) === count($arr)其他改进
闭包创建新实例
php
<?php
declare(strict_types=1);
// PHP 8.4+ 闭包支持创建新的类实例
// 使用 $this->new static() 或 $this->new self()
class Builder
{
public function create(): static
{
return new static();
}
}新的 parse_url() 组件
php
<?php
declare(strict_types=1);
// PHP 8.4+ 支持 URL 组件常量
$url = 'https://example.com:8080/path?query=value#fragment';
$host = parse_url($url, PHP_URL_HOST); // "example.com"
$port = parse_url($url, PHP_URL_PORT); // 8080
$path = parse_url($url, PHP_URL_PATH); // "/path"
$query = parse_url($url, PHP_URL_QUERY); // "query=value"
$fragment = parse_url($url, PHP_URL_FRAGMENT); // "fragment"最佳实践
- 使用属性钩子:替代传统的 getter/setter 模式,减少样板代码
- 使用不对称可见性:实现真正的不可变属性
- 使用链式 new:简化临时对象创建的代码
- 使用数组化函数:更简洁的数组操作
下一节
继续学习:版本迁移指南