检查魔法设置属性上是否存在属性

时间:2013-05-24 07:32:35

标签: php properties isset magic-methods

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

property_existsisset之间存在歧义,所以在提出问题之前,我会指出来:

property_exists

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

所以在下面的例子中:

<?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

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神奇添加的属性?

2 个答案:

答案 0 :(得分:7)

我不相信有一种方法可以使用魔术方法改变property_exists()的功能;这是PHP中的list of available magic methods。但是,您应该能够改变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

这通过魔术方法覆盖其功能,有效地修复了isset和null的(恼人的)问题。但是,我们不使用__isset()中的isset(),而是使用array_key_exists(它可以像您期望的那样处理空值)。因此,__ isset()在设置空值时返回预期结果。

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

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

答案 1 :(得分:-1)

问题在于__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));
?>