3

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"

3 Answers 3

3
+50

Let me describe a couple of ways of how you could do this.

ArrayObject with custom code

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.

Implement IteratorAggregate on get_object_vars()

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

ArrayObject with property flag and doc blocks

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

When loaded inside PhpStorm, you'd see this: phpstorm editor

Sign up to request clarification or add additional context in comments.

Comments

0

Please try get_object_vars() php function to Gets the accessible non-static properties of the given object according to scope.

The function adds before foreach loop. It's working.

$domainData = get_object_vars($domainData);
foreach($domainData as $k => $v){
    var_dump("domainData[$k] = $v");
}

=> Output

string(24) "domainData[domainId] = 1"
string(23) "domainData[color] = red"

Comments

0

Implement Iterator interface for ArrayAccess for example

<?php

/**
 * Class Collection
 * @noinspection PhpUnused
 */
class Collection implements ArrayAccess, IteratorAggregate, JsonSerializable, Countable
{
    /**
     * @var array $collection
     */
    private array  $collection;

    /**
     * @inheritDoc
     */
    public function offsetExists($offset): bool
    {
        return isset($this->collection[$offset]);
    }

    /**
     * @inheritDoc
     */
    public function offsetGet($offset)
    {
        return $this->collection[$offset];
    }

    /**
     * @inheritDoc
     */
    public function offsetSet($offset, $value)
    {
        if (empty($offset)) {
            return $this->collection[] = $value;
        }

       return $this->collection[$offset] = $value;

    }

    /**
     * @inheritDoc
     */
    public function offsetUnset($offset): void
    {
        unset($this->collection[$offset]);
    }



    /**
     * @inheritDoc
     */
    public function jsonSerialize()
    {
        return serialize($this->collection);
    }

    /**
     * @inheritDoc
     */
    public function count()
    {
        return count($this->collection);
    }

    /**
     * @return array
     */
    public function __debugInfo()
    {
        return $this->collection;
    }


    /**
     * @return mixed
     */
    public function first()
    {
        return $this->collection[0];
    }

    /**
     * @inheritDoc
     */
    public function getIterator()
    {
        return new ArrayIterator($this->collection);
    }

    /** @noinspection MagicMethodsValidityInspection */
    public function __toString()
    {
        return json_encode($this->collection, JSON_THROW_ON_ERROR, 512);
    }

    /**
     * @return mixed
     */
    public function last()
    {
        return $this->collection[$this->count()-1];
    }

}

for example using

<?php

$collections = new Collection();

$collections[] =12;

$collections[] = 14;
$collections[] = 145;
$collections[] =4;


print_r($collections);


echo $collections;

echo $collections->last();

echo $collections->first();

foreach ($collections as $collection)
{
    echo $collection;
}

count($collections);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.