检查一个属性是否存在于魔法设置的属性中


Check if a property exists on magically set properties

关于这个主题有很多SO问题,特别是这个问题,但它对我没有帮助。

property_existsisset之间存在歧义,所以在提出我的问题之前,我要指出它:

property_exists

property_exists检查对象是否包含属性而不查看其值,它只查看其可见性。

那么在下面的例子中:

<?php
class testA
{
  private $a = null;
}
class testB extends testA
{
}
$test = new testA();
echo var_dump(property_exists($test, 'a')); // true
// parent's private property becomes invisible for its child
$test = new testB();
echo var_dump(property_exists($test, 'a')); // false

收取

isset检查属性中是否存在一个值,考虑到如果一个值等于 false null,则不设置该值。

<?php
$var = null;
echo var_dump(isset($var)); // false
$var = '';
echo var_dump(isset($var)); // true
$var = false;
echo var_dump(isset($var)); // true
$var = 0;
echo var_dump(isset($var)); // true
$var = '0';
echo var_dump(isset($var)); // true

issetproperty_exists对神奇添加属性的行为

属性可以与null值一起存在,所以我不能使用__isset魔术方法来知道属性是否存在。我也不能使用property_exists,因为属性是使用魔法方法添加的。

这是一个示例,但这只是一个示例,因为在我的应用程序中,属性被神奇地设置为存储在对象之外。

class test {
    private $data = array();
    public function __get($key) {
        echo "get $key'n";
        return array_key_exists($key, $data) ? $data[$key] : null;
    }
    public function __set($key, $value) {
        echo "set $key = $value'n";
        $this->data[$key] = $value;
    }
    public function __isset($key) {
       echo sprintf("isset $key ( returns %b )", isset($this->data[$key]));
       return isset($this->data[$key]);
    }
}
$test = new test();
$test->x = 42;
isset($test->x); // 1
$test->y = null;
isset($test->y); // 0
property_exists($test, 'y'); // 0

我的问题是:

是否有一个神奇的方法或SPL接口来实现property_exist神奇地添加属性?

我不相信有一种方法可以使用魔法方法来改变property_exists()的功能;下面是PHP中可用的魔法方法列表。但是,您应该能够修改isset()以使用您喜欢的任何逻辑。

class test {
    private $data = array();
    public function __get($key) {
        echo "get $key'n";
        return array_key_exists($key, $this->data) ? $this->data[$key] : null;
    }
    public function __set($key, $value) {
        echo "set $key = $value'n";
        $this->data[$key] = $value;
    }
    public function __isset($key) {
       echo sprintf("isset $key ( returns %b )", array_key_exists($key, $this->data));
       return array_key_exists($key, $this->data);
    }
}
$test = new test();
$test->x = 42;
isset($test->x); // 1
$test->y = null;
isset($test->y); // 1

通过magic方法重写isset和null的功能,有效地解决了(恼人的)问题。然而,我们没有在__isset()中使用isset(),而是使用array_key_exists(如你所期望的那样处理null)。因此__isset()在设置空值时返回预期的结果。

这有一个缺点,即重写的功能不会产生与默认的isset()功能相同的结果。因此,如果这个对象需要透明地与其他(可能是stdClass)对象一起使用,那么isset()将对该类对象中的空值返回true,对普通对象中的空值返回false。

根据你的需要,这可能是一个可行的解决方案,也可能不是。如果上述问题是一个障碍,那么另一个选择可能是定义一个带有keyIsSet()属性的接口,并将该接口应用于所有要测试的对象。然后使用$obj->keyIsSet('key')而不是isset($obj->$key)。没有那么优雅,但是稍微好一点。

问题是__set__get函数的实现,我修改了它们,它适用于issetproperty_exists

<?php
class test {
    private $data = array();
    public function __get($key) {
        echo "get $key'n";
        return $$key;
    }
    public function __set($key, $value) {
        echo "set $key = $value'n";
        $this->$key = $value;
    }
    public function __isset($key) {
       echo sprintf("isset $key ( returns %b )", isset($this->$key));
       return isset($this->$key);
    }
}
$test = new test();
var_dump(property_exists($test, 'x'));
var_dump(isset($test->x));
$test->x = 42;
var_dump(property_exists($test, 'x'));
var_dump(isset($test->x));
?>