You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP Typed属性User::$created_at需DateTime却传入string报错如何解决

根因

你遇到的报错核心原因有两点:

  1. PHP的__set()魔术方法仅在给**不可访问属性(不存在、protected或private修饰的属性)**赋值时才会触发,如果你在User类中将$created_at声明为public强类型属性,直接赋值时不会走__set逻辑。
  2. PDO::FETCH_CLASS模式会直接给类的属性赋值,不会触发类的魔术方法,数据库取出的字符串格式时间直接写入要求DateTime类型的属性,就触发了类型错误。

解决方案

方案1:修改属性可见性(改动最小,无需修改Repository代码)

将User类中$created_at属性改为protected/private,同时提供对应的getter方法读取属性,此时PDO赋值时会自动触发__set方法完成时间转换:

// User.php
class User
{
    // 改为protected,禁止外部直接访问
    protected DateTime $created_at;
    // 其他属性同理可按需求调整可见性

    // 添加getter方法读取时间
    public function getCreatedAt(): DateTime
    {
        return $this->created_at;
    }

    // 你原有__set方法保持不变即可
    public function __set($name, $value)
    {
        if ($name == 'created_at')
            $this->$name = new DateTime($value);
        else
            $this->$name = $value;
    }
}

方案2:手动映射查询结果(可控性最高,适合复杂属性转换场景)

修改UserRepository的findAll方法,先取出数组格式的查询结果,再手动实例化User对象赋值,确保触发__set逻辑:

// UserRepository.php
public function findAll() : array {
    $stmt = $this->getPDO()->query("SELECT * FROM users");
    $users = [];
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $user = new User();
        foreach ($row as $key => $value) {
            $user->$key = $value;
        }
        $users[] = $user;
    }
    return $users;
}

方案3:使用FETCH_FUNC自定义实例化回调

如果不想写循环遍历,也可以用PDO::FETCH_FUNC模式传入自定义实例化逻辑:

// UserRepository.php
public function findAll() : array {
    return $this->getPDO()->query("SELECT * FROM users")->fetchAll(PDO::FETCH_FUNC, function (array $row) {
        $user = new User();
        foreach ($row as $key => $value) {
            $user->$key = $value;
        }
        return $user;
    });
}

内容的提问来源于stack exchange,提问作者sisko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 11:21:00