Страницы

Поиск по вопросам

вторник, 24 декабря 2019 г.

Магические свойства __get и __set и оператор [] = , хранение массивов

#php #массивы


PHP 5.6+. Имеется класс. 

class PropertyContainer {
    protected $_data;

    public $somePublicArray = [0];

    public function __construct($data = [])
    {
        $this->_data = $data;
    }

    public function __get($name)
    {
        return array_key_exists($name, $this->_data) ? $this->_data[$name] : null;
    }

    public function __set($name, $value)
    {
        $this->_data[$name] = $value;
    }

    public function __unset($name)
    {
        unset($this->_data[$name]);
    }

    public function __isset($name)
    {
        return array_key_exists($name, $this->_data);
    }
}


Работа со свойствами класса происходит через магические методы и внутренний массив
$_data, если свойство не объявлено в классе - довольно стандартный подход. И всё бы
здорово, если бы не странный подход к оператору [] = в PHP: проводим тест

$cont = new PropertyContainer(['a' => 1, 'b' => [0], 'c' => new ArrayObject ([0])]);

$cont->a++; /*$cont->a = 2 OK*/
$cont->b[] = 1; /*$cont->b = [0] FAIL*/
$cont->somePublicArray[] = 1; /*$cont->somePublicArray = [0,1] OK*/
$cont->c[] = 1; /*$cont->c = ArrayObject [0,1] OK*/

var_dump($cont, is_array($cont->c));
die();


То есть массивы, содержащиеся внутри $_data возвращаются не по ссылке. 

Вопрос - какие есть способы это обойти, и использовать оператор [] = для заполнения
внутренних массивов, кроме как массово использовать ArrayObject вместо array? С такой
же проблеммой сталкивались , но эти решения не работают для хранения данных произвольных
типов во внутреннем массиве (вроде $_data)- хотелось бы храннить данные в массиве (или
ArrayObject) как есть. 

В использовании ArrayObject не нравится не срабатывания метода is_array() на нём,
может быть можно переопределить поведение is_array  ?

Если использовать &__get вместо __get - можно ли вернуть указатель на элемент массива
$_data ? Иначе будет просто варнинг Only variable references should be returned by
reference .
    


Ответы

Ответ 1



Вариант 1: Если не нужна поддержка не объявляенных свойств: public function &__get($name) { if( array_key_exists($name, $this->_data) ) $val = &$this->_data[$name]; else $val = null; return $val; } В этом варианте есть баг: $cont = new PropertyContainer(['a' => 1, 'b' => [0]]); $cont->d[] = 1; // d не будет добавлено, вообще ничего не произойдёт. Вариант 2: Баг первого варианта исправлен, но _data будет захламлятся если есть попытки получать необъявляенные свойства. public function &__get($name) { if( ! array_key_exists( $name, $this->_data ) ){ $undefined; //It is possible to set default // value for every new parameter //Default is undefined. $this->_data[ $name ] = $undefined; } $val = &$this->_data[$name]; return $val; }

Комментариев нет:

Отправить комментарий