PHP 终结了动态属性。原因在此(以及该怎么办)

发布: (2025年12月18日 GMT+8 01:44)
4 min read
原文: Dev.to

Source: Dev.to

抱歉,我需要您提供要翻译的具体文本内容。请粘贴文章的正文(除代码块和 URL 之外),我将按照要求将其翻译成简体中文并保留原有的格式。

什么是动态属性?

动态属性是指在对象上添加的、未在其类中声明的属性。

class User {}

$user = new User();
$user->age = 30; // ⚠️ Dynamic property

问题

静默拼写错误 → 隐蔽的 Bug

$user->custmer_email = 'john@example.com'; // Typo!
echo $user->customer_email; // null 😱

缺乏类型安全

$user->age = 30;        // int
$user->age = 'thirty'; // string – no error
$user->age = null;     // also "fine"

IDE 与工具限制

  • 没有自动补全
  • 没有重构支持
  • 静态分析工具无法检测问题

架构衰退

// Anemic domain model
$user->cached_data = $something;
$user->temp_flag   = true;
$user->random_stuff = $whatever;

修复方法:显式声明属性

现代 PHP 写法

class User {
    public string $name;
    public int    $age;
    public bool   $isActive;
}

构造函数属性提升(PHP 8+)

class CreateOrderRequest {
    public function __construct(
        public readonly string $customerId,
        public readonly array  $items,
        public readonly ?string $promoCode = null,
    ) {}
}

Laravel‑特定解决方案

❌ 不要直接添加未定义的属性

$user = User::find(1);
$user->is_verified = true; // Undefined property!

✅ 定义可填充属性和类型转换

class User extends Model {
    protected $fillable = [
        'name',
        'email',
        'is_verified',
    ];

    protected $casts = [
        'is_verified'       => 'boolean',
        'email_verified_at'=> 'datetime',
    ];
}

✅ 使用访问器

use Illuminate\Database\Eloquent\Casts\Attribute;

class User extends Model {
    protected function fullName(): Attribute {
        return Attribute::make(
            get: fn() => "{$this->first_name} {$this->last_name}",
        );
    }
}

逃生口(请谨慎使用!)

#[\AllowDynamicProperties]
class LegacyCode {
    // Opt into old behavior
}

仅在以下情况下使用:

  • 旧版供应商代码
  • 渐进式迁移(并有计划移除)

切勿在新代码中使用。

迁移策略

  1. Run Static Analysis

    ./vendor/bin/phpstan analyze --level 8
    ./vendor/bin/psalm --no-cache
  2. Fix Systematically

    • Critical – 领域模型(立即修复)
    • High – 控制器 / 服务(下一个冲刺)
    • Medium – DTO(增量)
    • Low – 测试工具(如有需要添加属性)
  3. Prevent Regression

    将静态分析添加到 CI:

    # .github/workflows/ci.yml
    - name: Static Analysis
      run: ./vendor/bin/phpstan analyze

版本状态

PHP 版本状态
8.1✅ 允许
8.2⚠️ 已弃用(警告)
9.0❌ 致命错误(已移除)

为什么此更改重要

现代 PHP 正在朝着:

  • 明确优于隐式
  • 默认类型安全
  • 更好的工具支持
  • 便于重构的代码

动态属性与这些目标冲突。

实际示例:前后对比

前 ❌

class ApiResponse {}

$response = new ApiResponse();
$response->data   = $data;
$response->status = 200;

后 ✅

class ApiResponse {
    public function __construct(
        public readonly mixed $data,
        public readonly int   $status,
        public readonly int   $timestamp = time(),
    ) {}
}

$response = new ApiResponse($data, 200);

好处: 类型安全、自解释、不可变且易于重构。

关键要点

  • 明确声明所有属性。
  • 使用类型属性以确保安全。
  • 利用 PHP 8+ 特性(构造函数属性提升、readonly)。
  • 定期运行静态分析。
  • 为遗留代码规划迁移路径。

欲阅读完整指南——包括历史背景、详细迁移策略、高级 Laravel 模式等——请阅读 Medium 上的原文:

The Death of Dynamic Properties in PHP – A Deep Dive into Modern Object Design

Back to Blog

相关文章

阅读更多 »