This is my attempt at implementing https://www.php.net/manual/en/class.iterator.php for an ArrayAccess. Many examples use a container array as a private member variable; but I do not want to use a container array if possible. The main reason why I don't want a container array is because I'd like to access the property (array key) like this $DomainData->domainId
all while having intellisense, etc.
Demo: https://ideone.com/KLPwwY
class DomainData implements ArrayAccess, Iterator
{
private $position = 0;
public $domainId;
public $color;
public function __construct($data = array())
{
$this->position = 0;
foreach ($data as $key => $value) {
$this[$key] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->$offset);
}
public function offsetSet($offset, $value)
{
$this->$offset = $value;
}
public function offsetGet($offset)
{
return $this->$offset;
}
public function offsetUnset($offset)
{
$this->$offset = null;
}
/*****************************************************************/
/* Iterator Implementation */
/*****************************************************************/
public function rewind()
{
$this->position = 0;
}
public function current()
{
return $this[$this->position];
}
public function key()
{
return $this->position;
}
public function next()
{
++$this->position;
}
public function valid()
{
return isset($this[$this->position]);
}
}
Calling it:
$domainData = new DomainData([
"domainId" => 1,
"color" => "red"
]);
var_dump($domainData);
foreach($domainData as $k => $v){
var_dump("domainData[$k] = $v");
}
actual:
object(DomainData)#1 (3) {
["position":"DomainData":private]=>
int(0)
["domainId"]=>
int(1)
["color"]=>
string(3) "red"
}
desired:
object(DomainData)#1 (3) {
["position":"DomainData":private]=>
int(0)
["domainId"]=>
int(1)
["color"]=>
string(3) "red"
}
string(24) "domainData[domainId] = 1"
string(23) "domainData[color] = red"
Let me describe a couple of ways of how you could do this.
ArrayObject
implements all of the interfaces that you want.
class DomainData extends ArrayObject
{
public $domainId;
public $color;
public function __construct($data = array())
{
parent::__construct($data);
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
}
This isn't very nice, though; it copies the keys and values twice, and changing a property doesn't change the underlying array.
If you don't mind giving up on ArrayAccess
, you could get away with only implementing an aggregate iterator.
class DomainData implements IteratorAggregate
{
public $domainId;
public $color;
public function __construct($data = [])
{
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
public function getIterator()
{
return new ArrayIterator(get_object_vars($this));
}
}
A better way would be to use doc blocks for describing your properties (described here), and then use the ARRAY_AS_PROPS
flag to expose the array as properties.
/**
* Magic class
* @property int $domainId
* @property string $color
*/
class DomainData extends ArrayObject
{
function __construct($data = []) {
parent::__construct($data, parent::ARRAY_AS_PROPS);
}
}