【PHP属性详解:从基础到只读的完全指南】

《PHP属性详解:从基础到只读的完全指南》

属性基础:类的"部件"

属性是类的变量,代表对象的状态或特征。

php 复制代码
class Robot {
    public $name; // 一个简单的属性
}
类型化属性 (PHP 7.4+):给部件"定类型"

避免属性乱装东西。从 PHP 7.4 起,可以明确属性的类型。

php 复制代码
class Robot {
    public string $name;     // 必须是字符串
    public int $age;         // 必须是整数
    public ?float $height;   // 可以是浮点数或 null
    public array $tools;     // 必须是数组
    // 注意:callable 不能作为属性类型
}

好处:代码更健壮,IDE 更智能。

只读属性 readonly (PHP 8.1+):锁定部件

让属性一旦初始化就不可更改。这是实现"不可变对象"的关键。

php 复制代码
class Robot {
    public readonly string $serialNumber; // 序列号,出厂后不能改!

    public function __construct(string $serial) {
        $this->serialNumber = $serial; // ✅ 构造函数内初始化(唯一机会)
    }
}

$robot = new Robot("SN123");
// $robot->serialNumber = "SN456"; // ❌ Fatal error! 不能修改
echo $robot->serialNumber; // ✅ 可以读取

⚠️ 核心规则

  1. 必须初始化readonly 属性必须在构造函数(或 __clone)中赋值一次。
  2. 禁止默认值public readonly string $name = "default";语法错误!只读属性不能有默认值。
  3. 仅限类型化属性:必须先声明类型。
  4. 不支持静态static readonly不支持
readonly 的精髓:引用 vs 内容

readonly 保护的是"引用 "(指向哪里),不是"内容"(里面有什么)。

php 复制代码
class Robot {
    public readonly array $inventory; // 只读数组
    public readonly object $sensor;   // 只读对象
}

$robot = new Robot();
$robot->inventory = ['screwdriver']; // ✅ 初始化
$robot->sensor = new stdClass();     // ✅ 初始化

// ❌ 错误!试图改变"引用"
// $robot->inventory = ['wrench']; // 不行!不能换整个数组!
// $robot->sensor = new stdClass(); // 不行!不能换整个对象!

// ✅ 正确!修改"内容"(内部可变性)
$robot->inventory[] = 'wrench';      // 给 inventory 数组添加新工具 ✅
$robot->sensor->temperature = 25;    // 给 sensor 对象添加属性 ✅

总结readonly 保证"背包"和"传感器"不被替换,但允许往背包里装东西或给传感器升级。

类级别的 readonly (PHP 8.2+):一键锁定

直接标记整个类为 readonly

php 复制代码
readonly class Robot {
    public function __construct(
        public string $name,
        public int $age
    ) {}
}

$robot = new Robot("小助手", 5);
// $robot->name = "大助手"; // ❌ 错误!所有属性只读
// $robot->newProp = "test"; // ❌ 错误!禁止动态属性!
  • 效果 :所有声明的属性 自动 readonly,并禁止创建动态属性
  • 继承 :子类也必须是 readonly 类。
readonly 的"写权限"大变革 (PHP 8.4+)

PHP 8.3 及之前readonly 属性的"写权限"是隐式 private

  • 只有声明该属性的类本身可以在其构造函数中初始化它。
  • 子类无法直接初始化继承的 readonly 属性,必须依赖父类构造函数。
php 复制代码
// PHP 8.3
class ParentRobot {
    public readonly string $name;
}
class ChildRobot extends ParentRobot {
    public function __construct(string $name) {
        // parent::__construct($name); // ❌ 必须调用父类
        // $this->name = $name; // ❌ 子类不能写!
    }
}

PHP 8.4+readonly 属性的"写权限"变为隐式 protected(set)

  • "读权限" (get) 由 public/protected/private 决定。
  • "写权限" (set) 默认为 protected子类也可以直接初始化
php 复制代码
// PHP 8.4+
class ParentRobot {
    public readonly string $name;
    // 父类甚至可以没有构造函数!
}
class ChildRobot extends ParentRobot {
    public function __construct(string $name) {
        $this->name = $name; // ✅ 太棒了!子类可以直接初始化!
    }
}
  • 显式控制 (PHP 8.4+) :可以用 public(set), protected(set), private(set) 精确控制写权限。
克隆时的 readonly (PHP 8.3+)

克隆对象时,readonly 属性也需要在新对象中初始化。使用 __clone() 方法。

php 复制代码
class Robot {
    public readonly ?string $status;

    public function __clone() {
        $this->status = "Cloned"; // ✅ 在克隆时重新初始化
    }
}

$robot1 = new Robot();
$robot1->status = "Active";
$robot2 = clone $robot1;
var_dump($robot2->status); // string(6) "Cloned"
final 常量 (PHP 8.1.0+):锁定常量

在 PHP 8.1 之前,只有方法可以被标记为 final,常量不行。从 PHP 8.1.0 起,常量也可以被标记为 final

  • 目的:防止子类重新定义(覆盖)某个核心常量,确保其值在继承链中保持不变。
  • 效果 :一旦常量被标记为 final,任何尝试在子类中重新定义它的行为都会导致致命错误

例子:

php 复制代码
class MathConstants {
    // 普通常量,子类可以覆盖
    public const PI = 3.14159;

    // final 常量,子类禁止覆盖!
    public final const E = 2.71828;
}

// 错误示例:尝试覆盖 final 常量
// class WrongPhysicsConstants extends MathConstants {
//     public const E = 2.7; // Fatal error: Cannot override final constant MathConstants::E
// }

// 正确示例:只覆盖非 final 常量
class PhysicsConstants extends MathConstants {
    public const PI = 3.14; // ✅ 合法!覆盖非 final 的 PI
    // E 常量会直接继承,值为 2.71828
}

// 测试
echo PhysicsConstants::PI; // 输出: 3.14
echo PhysicsConstants::E;  // 输出: 2.71828

关键点总结: 如果某个常量的值是"基石"或"标准",不希望被子类修改,就用 final 标记它。

相关推荐
JaguarJack1 小时前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay1 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954481 天前
CTF 伪协议
php
BingoGo3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack6 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel