Replace Map_Iterator with a generator.

Generators (PHP 5.5+) make this kind of code structure much easier to
build.
This commit is contained in:
Sam Minnee 2017-01-17 12:51:37 +13:00 committed by Guy Sartorelli
parent 03b929dd33
commit 2ead3746d6
No known key found for this signature in database
GPG Key ID: F313E3B9504D496A
2 changed files with 42 additions and 207 deletions

View File

@ -260,13 +260,48 @@ class Map implements ArrayAccess, Countable, IteratorAggregate
#[\ReturnTypeWillChange] #[\ReturnTypeWillChange]
public function getIterator() public function getIterator()
{ {
return new Map_Iterator( $keyField = $this->keyField;
$this->list->getIterator(), $valueField = $this->valueField;
$this->keyField,
$this->valueField, foreach($this->firstItems as $k => $v) {
$this->firstItems, yield $k => $v;
$this->lastItems }
);
foreach($this->list as $record) {
if(isset($this->firstItems[$record->$keyField])) {
continue;
}
if(isset($this->lastItems[$record->$keyField])) {
continue;
}
yield $this->extractValue($record, $this->keyField) => $this->extractValue($record, $this->valueField);
}
foreach($this->lastItems as $k => $v) {
yield $k => $v;
}
}
/**
* Extracts a value from an item in the list, where the item is either an
* object or array.
*
* @param array|object $item
* @param string $key
* @return mixed
*/
protected function extractValue($item, $key)
{
if (is_object($item)) {
if (method_exists($item, 'hasMethod') && $item->hasMethod($key)) {
return $item->{$key}();
}
return $item->{$key};
} else {
if (array_key_exists($key, $item)) {
return $item[$key];
}
}
} }
/** /**

View File

@ -1,200 +0,0 @@
<?php
namespace SilverStripe\ORM;
use Iterator;
/**
* Builds a map iterator around an Iterator. Called by Map
*/
class Map_Iterator implements Iterator
{
/**
* @var Iterator
**/
protected $items;
protected $keyField, $titleField;
protected $firstItemIdx = 0;
protected $endItemIdx;
protected $firstItems = [];
protected $lastItems = [];
protected $excludedItems = [];
/**
* @param Iterator $items The iterator to build this map from
* @param string $keyField The field to use for the keys
* @param string $titleField The field to use for the values
* @param array $firstItems An optional map of items to show first
* @param array $lastItems An optional map of items to show last
*/
public function __construct(Iterator $items, $keyField, $titleField, $firstItems = null, $lastItems = null)
{
$this->items = $items;
$this->keyField = $keyField;
$this->titleField = $titleField;
$this->endItemIdx = null;
if ($firstItems) {
foreach ($firstItems as $k => $v) {
$this->firstItems[] = [$k, $v];
$this->excludedItems[] = $k;
}
}
if ($lastItems) {
foreach ($lastItems as $k => $v) {
$this->lastItems[] = [$k, $v];
$this->excludedItems[] = $k;
}
}
}
/**
* Rewind the Iterator to the first element.
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->firstItemIdx = 0;
$this->endItemIdx = null;
$rewoundItem = $this->items->rewind();
if (isset($this->firstItems[$this->firstItemIdx])) {
return $this->firstItems[$this->firstItemIdx][1];
} else {
if ($rewoundItem) {
return $this->extractValue($rewoundItem, $this->titleField);
} else {
if (!$this->items->valid() && $this->lastItems) {
$this->endItemIdx = 0;
return $this->lastItems[0][1];
}
}
}
}
/**
* Return the current element.
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
return $this->lastItems[$this->endItemIdx][1];
} else {
if (isset($this->firstItems[$this->firstItemIdx])) {
return $this->firstItems[$this->firstItemIdx][1];
}
}
return $this->extractValue($this->items->current(), $this->titleField);
}
/**
* Extracts a value from an item in the list, where the item is either an
* object or array.
*
* @param array|object $item
* @param string $key
* @return mixed
*/
protected function extractValue($item, $key)
{
if (is_object($item)) {
if (method_exists($item, 'hasMethod') && $item->hasMethod($key)) {
return $item->{$key}();
}
return $item->{$key};
} else {
if (array_key_exists($key, $item ?? [])) {
return $item[$key];
}
}
}
/**
* Return the key of the current element.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function key()
{
if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
return $this->lastItems[$this->endItemIdx][0];
} else {
if (isset($this->firstItems[$this->firstItemIdx])) {
return $this->firstItems[$this->firstItemIdx][0];
} else {
return $this->extractValue($this->items->current(), $this->keyField);
}
}
}
/**
* Move forward to next element.
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function next()
{
$this->firstItemIdx++;
if (isset($this->firstItems[$this->firstItemIdx])) {
return $this->firstItems[$this->firstItemIdx][1];
} else {
if (!isset($this->firstItems[$this->firstItemIdx - 1])) {
$this->items->next();
}
if ($this->excludedItems) {
while (($c = $this->items->current()) && in_array($c->{$this->keyField}, $this->excludedItems ?? [], true)) {
$this->items->next();
}
}
}
if (!$this->items->valid()) {
// iterator has passed the preface items, off the end of the items
// list. Track through the end items to go through to the next
if ($this->endItemIdx === null) {
$this->endItemIdx = -1;
}
$this->endItemIdx++;
if (isset($this->lastItems[$this->endItemIdx])) {
return $this->lastItems[$this->endItemIdx];
}
return false;
}
}
/**
* Checks if current position is valid.
*
* @return boolean
*/
#[\ReturnTypeWillChange]
public function valid()
{
return (
(isset($this->firstItems[$this->firstItemIdx])) ||
(($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) ||
$this->items->valid()
);
}
}