如何让通过__set存入$attributes的模型属性返回时正常序列化为JSON?
问题原因
PHP 将对象序列化为 JSON 时,默认仅会输出对象的公共可见属性。你的 $attributes 是 protected 修饰的,且 __get/__set 魔术方法仅在直接访问对象的未定义属性时触发,JSON 序列化过程不会调用这些魔术方法,因此最终得到空对象。
__toString 方法无效的原因是:只有当对象被当作字符串直接使用(比如 echo $object)时才会触发该方法,而将对象放入数组中进行 JSON 编码时,并不会调用它。
解决方案
1. 实现 JsonSerializable 接口(最直接有效)
通过实现 PHP 内置的 JsonSerializable 接口,让对象自己定义 JSON 序列化的内容:
class Model implements JsonSerializable { protected $attributes = []; public function __set($key, $val){ $this->attributes[$key] = $val; } public function __get($key){ return $this->attributes[$key] ?? throw new Exception("attribute not found"); } // 定义 JSON 序列化时返回的内容 public function jsonSerialize() { return $this->attributes; } }
此时再执行你的测试代码,JSON 编码就会输出 $attributes 中的所有键值对,符合预期。
2. 参考 Laravel Eloquent 的实现思路
Laravel 的 ActiveRecord 模型核心也是通过 JsonSerializable 接口控制序列化输出,同时做了更多封装:
- 模型的
jsonSerialize方法会调用toArray,后者会处理属性的类型转换、隐藏/可见属性过滤、关联模型序列化等逻辑 - 通过
fillable/guarded属性限制可批量赋值的字段 - 封装
getAttribute/setAttribute方法,在读写属性时自动处理类型转换(比如日期转 Carbon 实例、数组转 JSON 等)
如果想实现类似的 ActiveRecord 风格,可以参考这个思路,逐步扩展属性的读写控制、序列化规则等功能。
内容的提问来源于stack exchange,提问作者Avi
相关产品推荐
相关产品推荐

