PHP 5.6+. There is a class.

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); } } 

Working with class properties occurs through magic methods and the internal $_data , if a property is not declared in a class — a fairly standard approach. And everything would be great if it were not for the strange approach to the operator [] = in PHP: we are testing

 $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(); 

That is, arrays contained within $_data not returned by reference.

The question is - what are the ways to get around this, and use the operator [] = to fill the internal arrays, except how to massively use ArrayObject instead of array ? We encountered the same problem, but these solutions do not work for storing data of arbitrary types in the internal array (like $_data ) - I would like to store data in an array (or ArrayObject) as it is.

In using ArrayObject don’t like not triggering the is_array () method on it, maybe it’s possible to override the is_array behavior?

If you use &__get instead of __get - is it possible to return a pointer to the element of the $_data ? Otherwise, it will just be Only variable references should be returned by reference .

  • you need to start with the class PropertyContainer implements ArrayAccess and then - here - DiGiTAL
  • @DiGiTAL do not understand how ArrayAccess will help. After all, I am satisfied with the access to the PropertyContainer object through -> . And ArrayAccess gives us the opportunity to redefine access through square brackets, and not through -> - Goncharov Alexander
  • is_array theoretically possible to redefine the behavior of is_array , but what does not suit you then ($var instanceof ArrayObject) ? - DiGiTAL
  • one
    and since you're doing them in public anyway, then you can do it like this - DiGiTAL
  • @DiGiTAL is long. I need it for an abstract class - which means programmers will use this massively. And they don’t want to add hemorrhoids and extra pillows) вот так можно , yes, but then the opportunity to intercept getters / setters is lost. - Goncharov Alexander

1 answer 1

Option 1: If you do not need support for not declared properties:

 public function &__get($name) { if( array_key_exists($name, $this->_data) ) $val = &$this->_data[$name]; else $val = null; return $val; } 

In this version there is a bug:

 $cont = new PropertyContainer(['a' => 1, 'b' => [0]]); $cont->d[] = 1; // d не будет добавлено, вообще ничего не произойдёт. 

Option 2: The bug of the first option is fixed, but _data will be _data if there are attempts to get undeclared properties.

 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; } 
  • Cool, it works! I do not understand, like I tried the same thing, it did not work, I copied the code, it worked, thanks! - Goncharov Alexander