
本文详细介绍了在php中如何通过编程方式区分对象的已声明属性(在类定义中明确指定)和动态属性(在对象实例化后添加)。我们将利用`get_class_vars()`和`get_object_vars()`函数,结合数组操作,高效识别并分离这两种属性类型,为代码分析和调试提供便利。
在PHP面向对象编程中,对象的属性可以分为两种主要类型:声明属性(Declared Properties)和动态属性(Dynamic Properties)。声明属性是在类定义中明确定义的,它们是类结构的一部分。而动态属性则是在对象实例化之后,在运行时动态添加到对象上的,它们并不在类的原始定义中。理解并能够区分这两种属性对于代码分析、调试以及某些高级反射操作至关重要。
区分声明属性与动态属性的挑战
考虑以下PHP代码示例:
class Foo {
public $bar; // 这是一个声明属性
}
$obj = new Foo;
$obj->baz = 1; // 这是一个动态属性在此示例中,$bar是Foo类的一个声明属性,而$baz是在$obj实例上动态创建的属性。我们如何通过编程方式识别出$bar是声明属性,而$baz是动态属性呢?
解决方案:利用PHP内置函数
PHP提供了两个核心函数,它们是解决这个问题的关键:
立即学习“PHP免费学习笔记(深入)”;
- get_class_vars(string $class_name): 此函数返回由类$class_name声明的所有静态属性的默认值。对于非静态属性,它返回其在类定义中的默认值(如果没有默认值则为null)。重要的是,它只返回在类定义中明确声明的属性。
- get_object_vars(object $object): 此函数返回由对象$object的所有可访问属性组成的关联数组。这包括所有声明属性(无论其可见性如何,只要当前作用域可访问)以及所有动态属性。
通过结合使用这两个函数,我们可以计算出声明属性和动态属性的差异。
实现步骤
- 获取类声明的属性:使用get_class_vars()函数获取指定类中所有声明的属性。
- 获取对象的所有属性:使用get_object_vars()函数获取特定对象实例上的所有属性,包括声明属性和动态属性。
- 计算差异:将第二步得到的所有属性与第一步得到的声明属性进行比较,其差集即为动态属性。
示例代码
下面是具体的实现代码:
baz = 1; // 动态属性 $obj->dynamicProperty = 'hello'; // 另一个动态属性 // 1. 获取类声明的属性 // 注意:get_class_vars() 只能获取 public 属性的默认值, // 或者在类内部调用时获取所有可见属性的默认值。 // 对于本例,我们主要关心其键名。 $declaredProperties = get_class_vars(get_class($obj)); echo "--- 声明属性 (get_class_vars) ---\n"; print_r($declaredProperties); // 2. 获取对象的所有属性 (包括声明属性和动态属性) // get_object_vars() 在当前作用域下,会返回所有可访问的属性。 // 如果在类外部调用,它通常只返回 public 属性。 // 但其键名仍然包含了所有声明的 public 属性。 $allObjectProperties = get_object_vars($obj); echo "\n--- 对象所有属性 (get_object_vars) ---\n"; print_r($allObjectProperties); // 3. 计算动态属性 // 通过比较两个数组的键名差异,我们可以识别出动态属性。 $dynamicProperties = array_diff_key($allObjectProperties, $declaredProperties); echo "\n--- 动态属性 ---\n"; print_r($dynamicProperties); ?>
输出结果分析
运行上述代码,你将得到类似以下的输出:
--- 声明属性 (get_class_vars) ---
Array
(
[bar] =>
)
--- 对象所有属性 (get_object_vars) ---
Array
(
[bar] =>
[baz] => 1
[dynamicProperty] => hello
)
--- 动态属性 ---
Array
(
[baz] => 1
[dynamicProperty] => hello
)解释:
- get_class_vars(get_class($obj)) 返回了一个数组,其中包含Foo类中声明的public属性$bar。请注意,它不包含protected或private属性,因为get_class_vars()在类外部只能访问public属性的默认值。然而,对于识别键名来说,这已经足够了。
- get_object_vars($obj) 返回了对象$obj上所有可访问的属性,包括声明的$bar以及动态添加的$baz和$dynamicProperty。
- array_diff_key($allObjectProperties, $declaredProperties) 函数比较了两个数组的键名,并返回在$allObjectProperties中存在但在$declaredProperties中不存在的键及其对应的值。这正是我们所寻找的动态属性。
注意事项
- get_class() 的使用:在get_class_vars()函数中,我们使用了get_class($obj)而不是直接硬编码类名'Foo'。这样做的好处是使代码更具通用性。无论$obj是哪个类的实例,get_class()都会动态获取其类名,从而确保get_class_vars()始终作用于正确的类定义。
- 可见性限制:get_class_vars()和get_object_vars()在类外部调用时,通常只能访问public属性。如果需要在类内部或通过反射来获取所有可见性(包括protected和private)的属性,情况会更复杂,可能需要使用PHP的反射API(ReflectionClass和ReflectionProperty)。然而,对于区分声明的public属性和动态属性,上述方法是有效且简洁的。
- 默认值:get_class_vars()返回的是类声明属性的默认值。如果属性没有显式设置默认值,则其值为null。这不影响我们通过键名来识别属性的目的。
总结
通过巧妙地结合使用get_class_vars()和get_object_vars()这两个PHP内置函数,我们可以高效且准确地识别出对象中的动态属性。这种方法对于需要对对象结构进行运行时分析、调试或实现特定功能(如序列化、ORM等)的场景非常有用。理解PHP中属性的生命周期和可见性规则是掌握这一技巧的基础。











