Итераторы объектов

PHP 5 предоставляет такой способ объявления объектов, который дает возможность пройти по списку элементов данного объекта, например, с помощью оператора foreach. По умолчанию, в этом обходе (итерации) будут участвовать все видимые свойства объекта.

Пример #1 Итерация простого объекта


Наш Telegram бот: @htmlweb_bot
class MyClass
{
    public $var1 = 'value 1';
    public $var2 = 'value 2';
    public $var3 = 'value 3';
    protected $protected = 'protected var';
    private   $private   = 'private var';
    function iterateVisible() {
       echo "MyClass::iterateVisible:\n";
       foreach($this as $key => $value) {
           print "$key => $value\n";
       }
    }
}
$class = new MyClass();
foreach($class as $key => $value) {
    print "$key => $value\n";
}
echo "\n";
$class->iterateVisible();

Результат выполнения данного примера:


var1 => value 1
var2 => value 2
var3 => value 3

MyClass::iterateVisible:
var1 => value 1
var2 => value 2
var3 => value 3
protected => protected var
private => private var

Как показывает результат, foreach проитерировал все доступные и принадлежащие объекту видимые свойства.

Кроме того, вы можете развить эту концепцию и реализовать встроенный в PHP 5 интерфейс Iterator. Это позволит самому объекту решать как он будет итерироваться и какие данные будут доступны на каждой итерации.

Пример #2 Объект Iteration, реализующий интерфейс Iterator

class MyIterator implements Iterator
{
    private $var = array();
    public function __construct($array)
    {
        if (is_array($array)) {
            $this->var = $array;
        }
    }
    public function rewind()
    {
        echo "перемотка в начало\n";
        reset($this->var);
    }
  
    public function current()
    {
        $var = current($this->var);
        echo "текущий: $var\n";
        return $var;
    }
  
    public function key() 
    {
        $var = key($this->var);
        echo "ключ: $var\n";
        return $var;
    }
  
    public function next() 
    {
        $var = next($this->var);
        echo "следующий: $var\n";
        return $var;
    }
  
    public function valid()
    {
        $key = key($this->var);
        $var = ($key !== NULL && $key !== FALSE);
        echo "верный: $var\n";
        return $var;
    }
}
$values = array(1,2,3);
$it = new MyIterator($values);
foreach ($it as $a => $b) {
    print "$a: $b\n";
}

Результат выполнения данного примера:


перемотка в начало
верный: 1
текущий: 1
ключ: 0
0: 1
следующий: 2
верный: 1
текущий: 2
ключ: 1
1: 2
следующий: 3
верный: 1
текущий: 3
ключ: 2
2: 3
следующий:
верный:

Интерфейс IteratorAggregate может быть использован как альтернатива реализации всех методов интерфейса Iterator. IteratorAggregate требует чтобы был реализован только один метода - IteratorAggregate::getIterator(), который должен возвращать экземпляр класса, реализующий интерфейс Iterator.

Пример #3 Объект Iteration, реализующий интерфейс IteratorAggregate

class MyCollection implements IteratorAggregate
{
    private $items = array();
    private $count = 0;
    // Требование интерфейса IteratorAggregate
    public function getIterator() {
        return new MyIterator($this->items);
    }
    public function add($value) {
        $this->items[$this->count++] = $value;
    }
}
$coll = new MyCollection();
$coll->add('value 1');
$coll->add('value 2');
$coll->add('value 3');
foreach ($coll as $key => $val) {
    echo "ключ/значение: [$key -> $val]\n\n";
}

Результат выполнения данного примера:


перемотка в начало
текущий: value 1
верный: 1
текущий: value 1
ключ: 0
ключ/значение: [0 -> value 1]

следующий: value 2
текущий: value 2
верный: 1
текущий: value 2
ключ: 1
ключ/значение: [1 -> value 2]

следующий: value 3
текущий: value 3
верный: 1
текущий: value 3
ключ: 2
ключ/значение: [2 -> value 3]

следующий:
текущий:
верный:

Замечание:

Больше примеров итераторов можно найти в расширении SPL.

Замечание:

Пользователи PHP 5.5 и более поздних версий также могут изучить генераторы, представляющие собой альтернативный способ определения итераторов.


User Contributed Notes 16 notes



4
php dot net dot nsp at cvogt dot org1 year ago
public function valid()
{
    return ! is_null(key($this->var));
}


0
jille at hexon dot cx1 year ago
 // Wont work!
foreach($collection as $a) {
  foreach($collection as $b) {
    var_dump($a->someFunc($b));
  }
}


0
hlegius at gmail dot com5 years ago
class ArrayAccessImpl implements ArrayAccess {
  private $data = array();
  public function offsetUnset($index) {}
  public function offsetSet($index, $value) {
//    echo ("SET: ".$index."<br />");
    
    if(isset($data[$index])) {
        unset($data[$index]);
    }
    
    $u = &$this->data[$index];
    if(is_array($value)) {
        $u = new ArrayAccessImpl();
        foreach($value as $idx=>$e)
            $u[$idx]=$e;
    } else
        $u=$value;
  }
  public function offsetGet($index) {
//    echo ("GET: ".$index."<br />");
    if(!isset($this->data[$index]))
        $this->data[$index]=new ArrayAccessImpl();
    
    return $this->data[$index];
  }
  public function offsetExists($index) {
//    echo ("EXISTS: ".$index."<br />");
    
    if(isset($this->data[$index])) {
        if($this->data[$index] instanceof ArrayAccessImpl) {
            if(count($this->data[$index]->data)>0)
                return true;
            else
                return false;
        } else
            return true;
    } else
        return false;
  }
}
echo "ArrayAccess implementation that behaves like a multi-level array<hr />";
$data = new ArrayAccessImpl();
$data['string']="Just a simple string";
$data['number']=33;
$data['array']['another_string']="Alpha";
$data['array']['some_object']=new stdClass();
$data['array']['another_array']['x']['y']="LOL @ Whoever said it can't be done !";
$data['blank_array']=array();
echo "'array' Isset? "; print_r(isset($data['array'])); echo "<hr />";
echo "<pre>"; print_r($data['array']['non_existent']); echo "</pre>If attempting to read an offset that doesn't exist it returns a blank object! Use isset() to check if it exists!<br />";
echo "'non_existent' Isset? "; print_r(isset($data['array']['non_existent'])); echo "<br />";
echo "<pre>"; print_r($data['blank_array']); echo "</pre>A blank array unfortunately returns similar results :(<br />";
echo "'blank_array' Isset? "; print_r(isset($data['blank_array'])); echo "<hr />";
echo "<pre>"; print_r($data); echo "</pre> (non_existent remains in the structure. If someone can help to solve this I'll appreciate it)<hr />";
echo "Display some value that exists: ".$data['array']['another_string'];


0
doctorrock83_at_gmail.com6 years ago
interface Iterator<O> {
  boolean hasNext();
  O next();
  void remove();
}


0
chad 0x40 herballure 0x2e com7 years ago
$A = array(TRUE, FALSE, TRUE, TRUE);
while(current($A) !== FALSE) {
  var_dump(current($A));
  next($A);
}


0
markushe at web dot de8 years ago
class Collection implements ArrayAccess,IteratorAggregate
{
    public $objectArray = Array();
    //**these are the required iterator functions    
    function offsetExists($offset)
    {          
        if(isset($this->objectArray[$offset]))  return TRUE;
        else return FALSE;          
    }    
    
    function & offsetGet($offset)
    {   
        if ($this->offsetExists($offset))  return $this->objectArray[$offset];
        else return (false);
    }
    
    function offsetSet($offset, $value)
    {          
        if ($offset)  $this->objectArray[$offset] = $value;
        else  $this->objectArray[] = $value;
    }
    
    function offsetUnset($offset)
    {
        unset ($this->objectArray[$offset]);
    }
    
    function & getIterator()
    {
        return new ArrayIterator($this->objectArray);
    }
    //**end required iterator functions
    public function doSomething()
    {
        echo "I'm doing something";
    }
}


0
PrzemekG_ at poczta dot onet dot pl8 years ago
foreach($MyObject as $key => &$value)
   $value = 'new '.$value;


0
strrev('ed.relpmeur@ekneos');8 years ago
Use the SPL ArrayAccess interface to call an object as array:
http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html


0
phpnet at nicecupofteaandasitdown dot com8 years ago
You should be prepared for your iterator's current method to be called before its next method is ever called. This certainly happens in a foreach loop. If your means of finding the next item is expensive you might want to use something like this
private $item;
        
function next() {
    $this->item = &$this->getNextItem();
    return $this->item;
}
    
public function current() {
     if(!isset($this->item)) $this->next();
    return $this->item;
}


0
knj at aider dot dk8 years ago
if you in a string define classes that implements IteratorAggregate.
you cant use the default;
<?
...
public function getIterator() {
       return new MyIterator(\\$this-><What ever>);
}
..
?>
at least not if you want to use eval(<The string>).
You have to use:
<?
...
public function getIterator() {
      \\$arrayObj=new ArrayObject(\\$this-><What ever>);
      return \\$arrayObj->getIterator();
}
...
?>




Смотрите также:
Описание на ru2.php.net
Описание на php.ru