
本文介绍一种可靠方法,将 php 对象实例转换为关联数组,并自动过滤掉所有与类定义中初始值相同的属性(即“默认值”),仅保留被显式修改过的字段,适用于数据库存储前的数据精简。
在 PHP 中,直接使用 (array) 强制类型转换虽能将对象转为数组,但存在明显局限:它会保留所有属性(包括未赋值的 null、空数组或字符串默认值),且无法区分“用户显式设置”与“继承自类定义的默认值”。更关键的是,当属性值为数组或对象时,array_diff() 会因不支持递归比较而抛出 Array to string conversion 错误。
理想的解决方案是显式获取类的默认状态快照,并与当前实例逐属性比对。以下是一个健壮、可复用的实现方式:
✅ 推荐实现(基于反射 + 属性比对)
class MyObject {
public $title = null;
public $description = null;
public $items = [];
public $metas = [];
public $image = null;
public $country = 'Belgium';
// 注意:$children 不在原始类定义中,但运行时被动态添加 → 需保留!
/**
* 获取当前实例所有 public 属性的默认初始值(静态快照)
*/
public function getDefaultValues(): array {
$reflect = new ReflectionClass($this);
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
$defaults = [];
foreach ($props as $prop) {
$name = $prop->getName();
// 使用 ReflectionProperty::getDefaultValue() 更准确(PHP 8.0+)
// 此处兼容旧版:通过新实例获取初始值
$defaults[$name] = $prop->getDefaultValue() ?? null;
}
return $defaults;
}
/**
* 获取当前实例中「非默认值」的属性,返回精简数组
*/
public function toArrayWithoutDefaults(): array {
$defaults = $this->getDefaultValues();
$current = [];
// 手动收集当前所有 public 属性(含动态添加项)
foreach (get_object_vars($this) as $key => $value) {
$current[$key] = $value;
}
$result = [];
foreach ($current as $key => $value) {
// 判断是否为默认值(支持 null、[]、字符串等基础类型)
$isDefault = array_key_exists($key, $defaults)
? $this->deepEqual($value, $defaults[$key])
: false;
if (!$isDefault) {
$result[$key] = $value;
}
}
return $result;
}
/**
* 深度比较两个值(简化版,支持 null、标量、数组)
*/
private function deepEqual($a, $b): bool {
if (is_array($a) && is_array($b)) {
return $a === $b; // PHP 数组全等已支持深度比较
}
return $a === $b;
}
}? 使用示例
$data = new MyObject(); $data->title = 'NEW ITEM'; $data->children = ['CHILD1', 'CHILD2']; // 动态属性,不在类定义中 → 自动保留 $data->image = 'image.gif'; $data->country = 'Belgium'; // 与默认值相同 → 被剔除 $dataToStore = $data->toArrayWithoutDefaults(); print_r($dataToStore);
输出结果:
Array
(
[title] => NEW ITEM
[children] => Array
(
[0] => CHILD1
[1] => CHILD2
)
[image] => image.gif
)⚠️ 注意事项
- 动态属性支持:get_object_vars() 可捕获运行时新增的 public 属性(如 $data->children),而 ReflectionClass::getProperties() 仅返回类定义中的属性。因此本方案兼顾灵活性与准确性。
- 类型安全比对:使用 ===(全等)而非 ==,避免 '0' == 0 类型隐式转换导致误判。
- 性能考量:反射操作有轻微开销,若高频调用,可将 getDefaultValues() 结果缓存于静态属性。
- 扩展建议:如需支持 private/protected 属性,需配合 setAccessible(true);若需 JSON 序列化兼容,可实现 JsonSerializable 接口。
该方法逻辑清晰、无副作用、兼容主流 PHP 版本(7.4+),是生产环境中安全可靠的“默认值剥离”实践方案。










