Страницы

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

вторник, 27 ноября 2018 г.

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

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: Если не нужна поддержка не объявляенных свойств:
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; }

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

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