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

问题描述:

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

There is a lot of SO questions about the subject, notably this one, but it does not help me.

property_existsisset之间存在歧义,因此在提出问题之前,我先指出一下:

There is an ambiguity between property_exists and isset so before asking my question, I'm going to pointing it out:

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

property_exists checks if an object contains a property without looking at its value, it only looks at its 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,则不会设置.

isset

isset checks if a value exists in a property, considering that is is not set if a value equals false and 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,因为属性是使用魔术方法添加的.

isset and property_exists's behaviour on magically added properties

A property can exist with a null value, so I can't use __isset magic method to know if a property exist or not. I also can't use property_exists as properties are added using magic methods.

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

Here is a sample, but this is just a sample because in my app, properties magically set are stored outside the object.

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()以使用您喜欢的任何逻辑.

I don't believe there's a way to alter the functionality of property_exists() using magic methods; here's a list of available magic methods in PHP. However, you should be able to alter isset() to use any logic you like.

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()返回预期结果.

This effectively fixes the (annoying) problem with isset and nulls by overriding its functionality through the magic method. Instead of using isset() within __isset() however, we use array_key_exists (which handles nulls as you would expect). Thus __isset() returns the expected result when a null value is set.

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

This has a downside, namely that the overridden functionality does not produce the same results as default isset() functionality. So, if this object needs to be used transparently with other (perhaps stdClass) objects, then isset() will return true for null values in objects of this class, and false for null values in normal objects.

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

Depending on your needs then this may or may not be a viable solution. If the above issue is a hindrance, then another option might be to define an interface with a keyIsSet() property, and apply that interface to all objects to be tested. Then use $obj->keyIsSet('key') rather than isset($obj->$key). Not as elegant, but a bit better oo.