Реализовать интерфейс Iterator для ArrayAccess без контейнерного массива - PullRequest
3 голосов
/ 15 марта 2020

Это моя попытка реализовать https://www.php.net/manual/en/class.iterator.php для ArrayAccess. Во многих примерах массив-контейнер используется в качестве закрытой переменной-члена; но я не хочу использовать контейнерный массив, если это возможно. Основная причина, по которой я не хочу использовать контейнерный массив, заключается в том, что я хотел бы получить доступ к свойству (ключ массива) следующим образом: $DomainData->domainId все время с intellisense, et c.

Демо: 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]);
    }
}

Называется:

$domainData = new DomainData([
    "domainId" => 1,
    "color" => "red"
]);

var_dump($domainData);

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

актуально:

object(DomainData)#1 (3) {
  ["position":"DomainData":private]=>
  int(0)
  ["domainId"]=>
  int(1)
  ["color"]=>
  string(3) "red"
}

желательно:

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 ]

3 голосов
/ 19 марта 2020

Позвольте мне описать несколько способов, как вы могли бы сделать это.

ArrayObject с пользовательским кодом

ArrayObject реализует все необходимые интерфейсы .

class DomainData extends ArrayObject
{
  public $domainId;
  public $color;

  public function __construct($data = array())
  {
    parent::__construct($data);
    foreach ($data as $key => $value) {
      $this->$key = $value;
    }
  }
}

Хотя это не очень приятно; он дважды копирует ключи и значения, и изменение свойства не меняет базовый массив.

Реализация IteratorAggregate для get_object_vars ()

Если вы не возражаете отказаться от ArrayAccess вы можете обойтись только с помощью реализации агрегатного итератора .

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 со свойством flag и делать c blocks

Лучшим способом было бы использовать выполните c блоки для описания ваших свойств (описано здесь ), а затем используйте флаг ARRAY_AS_PROPS, чтобы представить массив как свойства.

/**
 * Magic class
 * @property int $domainId
 * @property string $color
 */
class DomainData extends ArrayObject
{
  function __construct($data = []) {
    parent::__construct($data, parent::ARRAY_AS_PROPS);
  }
}

При загрузке в PhpStorm вы увидел бы это: phpstorm editor

0 голосов
/ 23 марта 2020

Реализовать интерфейс Iterator для ArrayAccess, например,

<?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];
    }

}

, например, используя

<?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);
0 голосов
/ 16 марта 2020

Пожалуйста, попробуйте функцию get_object_vars() php, чтобы получить доступные не статичные c свойства данного объекта в соответствии с областью действия.

Функция добавляет до foreach l oop. Это работает.

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

=> Вывод

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