ZetCode

PHP readonly 属性

最后修改于 2025 年 4 月 16 日

PHP readonly 关键字创建不可变类属性,这些属性只能初始化一次。在 PHP 8.1 中引入,只读属性有助于在面向对象的代码中强制实现不可变性。它们可以防止在初始化后进行修改,使对象更具可预测性。

基本定义

一个 readonly 属性只能在对象初始化期间设置一次。初始化后,该属性不能被修改。只读属性必须具有类型声明。它们可以在构造函数中或直接初始化。

只读属性在初始化后不能被取消设置。它们提供了一种创建不可变对象的简单方法。这对于值对象和 DTO(数据传输对象)很有用,在这些情况下,属性不应该在创建后更改。

语法:readonly type $propertyName。只读修饰符位于类型声明之前。属性可以是 public、protected 或 private。

基本只读属性

此示例演示了类中的一个简单只读属性。

basic_readonly.php
<?php

declare(strict_types=1);

class User {
    public readonly string $name;
    
    public function __construct(string $name) {
        $this->name = $name;
    }
}

$user = new User('John');
echo $user->name; // Outputs: John

$name 属性被标记为只读,并在构造函数中初始化。一旦设置,它就不能被更改。尝试稍后修改它会导致错误。这确保了名称在对象创建后保持不变。

具有默认值的只读属性

此示例显示了使用默认值初始化的只读属性。

readonly_default.php
<?php

declare(strict_types=1);

class Config {
    public readonly string $environment = 'production';
}

$config = new Config();
echo $config->environment; // Outputs: production

$environment 属性使用默认值进行初始化。由于它是只读的,因此此值以后不能更改。默认值必须是编译时常量。这种模式对于配置值很有用。

在提升构造函数中的只读属性

此示例演示了将 readonly 与构造函数属性提升一起使用。

readonly_promoted.php
<?php

declare(strict_types=1);

class Product {
    public function __construct(
        public readonly string $name,
        public readonly float $price
    ) {}
}

$product = new Product('Laptop', 999.99);
echo "{$product->name}: {$product->price}"; // Outputs: Laptop: 999.99

构造函数参数被提升为只读属性。这种简洁的语法结合了属性声明和初始化。属性在构造后保持不变。这是值对象的一种常见模式。

尝试修改只读属性

此示例显示了尝试修改只读属性时会发生什么情况。

readonly_modify.php
<?php

declare(strict_types=1);

class Account {
    public readonly int $id;
    
    public function __construct(int $id) {
        $this->id = $id;
    }
}

$account = new Account(123);
// $account->id = 456; // Fatal error: Cannot modify readonly property

代码创建一个具有只读 $id 的 Account。如果取消注释,注释行将导致致命错误。这演示了只读强制的不可变性。当尝试修改时,错误发生在运行时。

所有属性都为只读的只读类

此示例显示了一个类,其中所有属性都被标记为只读。

readonly_class.php
<?php

declare(strict_types=1);

readonly class Point {
    public function __construct(
        public float $x,
        public float $y,
        public float $z
    ) {}
}

$point = new Point(1.0, 2.0, 3.0);
echo "Point coordinates: {$point->x}, {$point->y}, {$point->z}";

整个 Point 类被标记为只读,默认情况下使所有属性不可变。这从 PHP 8.2 开始可用。这是一种创建不可变值对象的简洁方法。所有属性都必须是类型化和只读的。

具有联合类型的只读属性

此示例演示了将 readonly 与联合类型属性一起使用。

readonly_union.php
<?php

declare(strict_types=1);

class Document {
    public readonly string|int $id;
    
    public function __construct(string|int $id) {
        $this->id = $id;
    }
}

$doc1 = new Document('ABC123');
$doc2 = new Document(456);
echo "Document IDs: {$doc1->id}, {$doc2->id}";

$id 属性接受字符串或整数值。只读修饰符与联合类型一起使用。一旦初始化,无论其类型如何,该属性都不能被更改。这提供了灵活性,同时保持了不可变性。

继承中的只读属性

此示例显示了只读属性在继承场景中的行为方式。

readonly_inheritance.php
<?php

declare(strict_types=1);

class Base {
    public readonly string $baseProp;
    
    public function __construct() {
        $this->baseProp = 'base';
    }
}

class Child extends Base {
    public readonly string $childProp;
    
    public function __construct() {
        parent::__construct();
        $this->childProp = 'child';
    }
}

$obj = new Child();
echo "{$obj->baseProp}, {$obj->childProp}"; // Outputs: base, child

父类和子类都可以具有只读属性。子类必须初始化其自己的只读属性。父属性通过父构造函数初始化。只读属性通过继承层次结构保持其不可变性。

最佳实践

来源

PHP readonly 属性文档

本教程通过实际示例介绍了 PHP 只读属性,展示了它们在各种场景中的用法,以创建不可变对象。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 PHP 教程