3

这是我为 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"
4

3 回答 3

3

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

于 2020-03-19T06:01:13.127 回答
0

例如,为 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);
于 2020-03-23T09:54:52.160 回答
0

请尝试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"
于 2020-03-16T06:30:30.153 回答