为没有容器数组的 ArrayAccess 实现 Iterator 接口

Implement Iterator interface for ArrayAccess without container array

这是我为 ArrayAccess 实现 https://www.php.net/manual/en/class.iterator.php 的尝试。许多示例使用容器数组作为私有成员变量;但如果可能的话,我不想使用容器数组。我不想要容器数组的主要原因是因为我想像这样 $DomainData->domainId 访问 属性 (数组键),同时具有智能感知等

演示: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"

请尝试 get_object_vars() php 函数根据范围获取给定对象的可访问非静态属性。

函数在foreach循环之前添加。有效。

$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"

让我描述一下您可以如何做到这一点的几种方法。

带有自定义代码的 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;
    }
  }
}

虽然这不是很好;它复制键和值两次,并且更改 属性 不会更改基础数组。

在 get_object_vars()

上实现 IteratorAggregate

如果您不介意放弃 ArrayAccess,您可以只实施 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

更好的方法是使用文档块来描述您的属性(描述为 here),然后使用 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 中时,您会看到:

例如为 ArrayAccess 实现 Iterator 接口

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