Проверьте, существует ли свойство в магически заданных свойствах

Есть много вопросов о предмете, особенно этот, но это мне не помогает.

Существует двусмысленность между property_exists и isset, поэтому, прежде чем задавать свой вопрос, я укажу:

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

isset и property_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 с волшебными добавленными свойствами?

Ответ 1

Я не верю, что есть способ изменить функциональность 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

Это эффективно устраняет (раздражающую) проблему с isset и nulls путем переопределения ее функциональности с помощью магического метода. Вместо использования isset() внутри __isset() мы используем array_key_exists (который обрабатывает нули, как и следовало ожидать). Таким образом, __isset() возвращает ожидаемый результат, когда задано значение null.

Это имеет недостаток, а именно, что переопределенная функциональность не дает тех же результатов, что и функция isset() по умолчанию. Таким образом, если этот объект необходимо использовать прозрачно с другими объектами (возможно, stdClass), то isset() вернет true для нулевых значений в объектах этого класса и false для нулевых значений в обычных объектах.

В зависимости от ваших потребностей это может быть или не быть жизнеспособным решением. Если вышеуказанная проблема является помехой, другой вариант может заключаться в определении интерфейса с помощью свойства keyIsSet() и применении этого интерфейса ко всем объектам, которые будут проверяться. Затем используйте $obj-> keyIsSet ("ключ"), а не isset ($obj-> $ key). Не так элегантно, но немного лучше.

Ответ 2

Проблема заключается в реализации функций __set и __get, я их модифицировал и работает как для isset, так и property_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));
?>