Longhorn PHP 2026 - Call For Papers

Objektiteration

PHP bietet eine Möglichkeit Objekte so zu definieren, dass es möglich ist eine Liste von Elementen zu durchlaufen, z.B. mit einem foreach-Statement. Standardmäßig werden alle sichtbaren Eigenschaften für die Iteration benutzt.

Beispiel #1 Einfache Objektiteration

<?php
class MyClass
{
public
$var1 = 'Wert 1';
public
$var2 = 'Wert 2';
public
$var3 = 'Wert 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();

?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

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

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

Wie die Ausgabe zeigt, durchläuft das foreach alle sichtbaren Eigenschaften, auf die zugegriffen werden kann.

add a note

User Contributed Notes 18 notes

up
22
php dot net dot nsp at cvogt dot org
13 years ago
there is still an open bug about using current() etc. with iterators
https://bugs.php.net/bug.php?id=49369
up
21
wavetrex A(nospam)T gmail DOT com
18 years ago
By reading the posts below I wondered if it really is impossible to make an ArrayAccess implementation really behave like a true array ( by being multi level )

Seems like it's not impossible. Not very preety but usable

<?php

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'];

?>

(in the two links mentioned below by artur at jedlinski... they say you can't use references, so I didn't used them.
My implementation uses recursive objects)

If anyone finds a better (cleaner) sollution, please e-mail me.
Thanks,
Wave.
up
8
hlegius at gmail dot com
17 years ago
Iterator interface usign key() next() rewind() is MORE slow than extends ArrayIterator with ArrayIterator::next(), ArrayIterator::rewind(), etc.,
up
6
strrev('ed.relpmeur@ekneos');
21 years ago
Use the SPL ArrayAccess interface to call an object as array:

http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html
up
6
chad 0x40 herballure 0x2e com
20 years ago
The example code given for valid() will break if the array contains a FALSE value. This code prints out a single "bool(true)" and exits the loop when it gets to the FALSE:

<?php
$A = array(TRUE, FALSE, TRUE, TRUE);
while(current($A) !== FALSE) {
  var_dump(current($A));
  next($A);
}
?>

Instead, the key() function should be used, since it returns NULL only at the end of the array. This code displays all four elements and then exits:

<?php
$A = array(TRUE, FALSE, TRUE, TRUE);
while(!is_null(key($A))) {
  var_dump(current($A));
  next($A);
}
?>
up
4
markushe at web dot de
20 years ago
Just something i noticed:
It seems, that when you are implementing the interface Iterator, yout method key() has to return a string or integer.

I was trying to return a object an got this error:
Illegal type returned from MyClass::key()
up
5
elias at need dot spam
21 years ago
The MyIterator::valid() method above ist bad, because it
breaks on entries with 0 or empty strings, use key() instead:

<?php
public function valid()
{
    return ! is_null(key($this->var));
}
?>

read about current() drawbacks:
http://php.net/current
up
4
just_somedood at yahoo dot com
20 years ago
To clarify on php at moechofe's post, you CAN use the SPL to overide the array operator for a class.  This, with the new features of object, and autoloading (among a buch of other things) has me completely sold on PHP5.  You can also find this information on the SPL portion of the manual, but I'll post it here as well so it isn't passed up.  The below Collection class will let you use the class as an array, while also using the foreach iterator:

<?php 

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";
    }
}

?>

I LOVE the new SPL stuff in PHP!  An example of usage is below:

<?php
class Contact
{
    protected $name = NULL;

    public function set_name($name)
    {
        $this->name = $name;
    }
    
    public function get_name()
    {
        return ($this->name);
    }
}

$bob = new Collection();
$bob->doSomething();
$bob[] = new Contact();
$bob[5] = new Contact();
$bob[0]->set_name("Superman");
$bob[5]->set_name("a name of a guy");

foreach ($bob as $aContact)
{
     echo $aContact->get_name() . "\r\n";
}
?>

Would work just fine.  This makes code so much simpler and easy to follow, it's great.  This is exactly the direction I had hoped PHP5 was going!
up
4
knj at aider dot dk
21 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();
}
...
?>
up
1
mike at eastghost dot com
3 years ago
The days of lovely care-to-the-wind typecasting are coming to close.  Finding this devilish bug took us entirely too long.

PHP-8.2.1 was throwing errors seemingly uncaught (they were eventually seen amassing in  / var / log / apache / DOMAIN-ssl-err.log  ) due to mismatch between return types of the necessary interface methods in our 'implements \Iterator' class (which had worked fine for many years, until our leap up to 8.2.1) versus the interface methods required by PHP.

Particularly:

next()
=====
ours:
public function next() {...}

PHP-8.2.1's
public function next() : void {...}

valid()
======
ours:
public function valid() {...}

PHP-8.2.1's:
public function valid() : bool {...}

key()
====
ours:
public function key() {...}

PHP-8.2.1's:
public function key() : mixed {...}

rewind()
========
ours:
public function rewind() {...}

PHP-8.2.1's:
public function rewind() : void {...}

current()
=======
ours:
public function current() {...}

PHP-8.2.1's:
public function current() : mixed {...}

We added the missing / now all-important return types to our function/method declarations and everything instantly worked again.

This extreme stringency is not made clear enough, IMHO, in the Iterator manual page.
up
4
baldurien at bbnwn dot eu
19 years ago
Beware of how works iterator in PHP if you come from Java!

In Java, iterator works like this :
<?php
interface Iterator<O> {
  boolean hasNext();
  O next();
  void remove();
}
?>
But in php, the interface is this (I kept the generics and type because it's easier to understand)

<?php
interface Iterator<O> {
  boolean valid();
  mixed key();
  O current();
  void next();
  void previous();
  void rewind();
}
?>

1. valid() is more or less the equivalent of hasNext()
2. next() is not the equivalent of java next(). It returns nothing, while Java next() method return the next object, and move to next object in Collections. PHP's next() method will simply move forward.

Here is a sample with an array, first in java, then in php :

<?php
class ArrayIterator<O> implements Iterator<O> {
  private final O[] array;
  private int index = 0;

  public ArrayIterator(O[] array) {
     this.array = array;
  }
  
  public boolean hasNext() {
    return index < array.length;
  }  

  public O next() {
     if ( !hasNext()) 
       throw new NoSuchElementException('at end of array');
     return array[index++];
  }

  public void remove() {
    throw new UnsupportedOperationException('remove() not supported in array');
  }
}
?> 

And here is the same in php (using the appropriate function) :

<?php
/**
 * Since the array is not mutable, it should use an internal 
 * index over the number of elements for the previous/next 
 * validation.
 */
class ArrayIterator implements Iterator {
  private $array;
  public function __construct($array) {
    if ( !is_array($array)) 
      throw new IllegalArgumentException('argument 0 is not an array');
    $this->array = array;
    $this->rewind();
  }
  public function valid() {
    return current($this->array) !== false;
    // that's the bad method (should use arrays_keys, + index)
  }
  public function key() {
     return key($this->array);
  }
  public function current() {
    return current($this->array);
  }
  public function next() {
    if ( $this->valid()) 
      throw new NoSuchElementException('at end of array');
    next($this->array);
  }
  public function previous()  {
    // fails if current() = first item of array
    previous($this->array);
  }
  public function rewind() {
     reset($this->array);
  }
}
?>

The difference is notable : don't expect next() to return something like in Java, instead use current(). This also means that you have to prefetch your collection to set the current() object. For instance, if you try to make a Directory iterator (like the one provided by PECL), rewind should invoke next() to set the first element and so on. (and the constructor should call rewind())

Also, another difference :

<?php
class ArrayIterable<O> implements Iterable<O> {
  private final O[] array;

  public ArrayIterable(O[] array) {
     this.array = array;
  }  

  public Iterator<O> iterator() {
     return new ArrayIterator(array);
  }
}
?>

When using an Iterable, in Java 1.5, you may do such loops :

<?php
for ( String s : new ArrayIterable<String>(new String[] {"a", "b"})) {
  ...
}
?>
Which is the same as :

<?php
Iterator<String> it = new ArrayIterable<String>(new String[] {"a", "b"});
while (it.hasNext()) {
  String s = it.next();
  ...
}
?>
While in PHP it's not the case :
<?php
foreach ( $iterator as $current ) {
  ...
}
?>
Is the same as :

<?php
for ( $iterator->rewind(); $iterator->valid(); $iterator->next()) {
  $current = $iterator->current();
  ...
}
?>

(I think we may also use IteratorAggregate to do it like with Iterable).

Take that in mind if you come from Java.

I hope this explanation is not too long...
up
3
rune at zedeler dot dk
19 years ago
The iterator template from knj at aider dot dk does not yield correct results.
If you do
<?
reset($a);
next($a);
echo current($a);
?>
where $a is defined over the suggested template, then the first element will be output, not the second, as expected.
up
2
artur at jedlinski dot pl
19 years ago
One should be aware that ArrayAccess functionality described by "just_somedood at yahoo dot com" below is currently broken and thus it's pretty unusable.

Read following links to find more:
http://bugs.php.net/bug.php?id=34783
http://bugs.php.net/bug.php?id=32983
up
1
celsowm
6 years ago
I've created a dinamic version of grzeniufication code to allow un-, serialize more than one property:

<?php

class Person implements \Serializable {

    public $id;
    public $name;
    public $birthDate;
    public $surname;

    public function serialize() {
        return serialize((array) $this);
    }

    public function unserialize($serialized): void {

        foreach (unserialize($serialized) as $p => $v) {
            $this->{$p} = $v;
        }
    }

}
up
1
phpnet at nicecupofteaandasitdown dot com
21 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;
}
up
1
doctorrock83_at_gmail.com
18 years ago
Please remember that actually the only PHP iterating structure that uses Iterator is foreach().

Any each() or list() applied to an Object implementing iterator will not provide the expected result
up
0
ermak3862 at gmail dot com
19 hours ago
-- Grundaufbau:
SELECT spalte1, spalte2
FROM tabelle
WHERE bedingung;

-- Bedeutung:
-- SELECT = Was soll angezeigt werden?
-- FROM = Aus welcher Tabelle?
-- WHERE = Welche Bedingung / Verbindung gilt?

-- Alle Datensätze anzeigen:
SELECT *
FROM Kunde;

-- Bestimmte Spalten anzeigen:
SELECT k_name, k_vorname
FROM Kunde;

-- Mit Bedingung filtern:
SELECT k_name, k_vorname
FROM Kunde
WHERE k_ort = 'Mannheim';

-- Zahlen ohne Anführungszeichen:
WHERE k_nummer = 4711;

-- Texte mit einfachen Anführungszeichen:
WHERE kt_bezeichnung = 'Pink Floyd';

-- Sortieren aufsteigend:
ORDER BY r_datum ASC;

-- Sortieren absteigend:
ORDER BY r_datum DESC;

-- Beispiel: Rechnungen eines Kunden:
SELECT r_nummer, r_datum
FROM Rechnung
WHERE r_k_nummer = 4711
ORDER BY r_datum DESC;

-- Tabellen verbinden: Primärschlüssel = Fremdschlüssel
SELECT ...
FROM Tabelle1, Tabelle2
WHERE Tabelle1.id = Tabelle2.fremd_id;

-- Beispiel Kunde + Rechnung:
SELECT k_name, r_nummer
FROM Kunde, Rechnung
WHERE k_nummer = r_k_nummer;

-- Mehrere Tabellen verbinden:
SELECT ...
FROM Kunde, Rechnung, Rechnungspositionen, Konzertticket
WHERE k_nummer = r_k_nummer
  AND r_nummer = rp_r_nummer
  AND rp_kt_id = kt_id;

-- Datumsbereich:
WHERE r_datum BETWEEN '2024-01-01' AND '2024-12-31';

-- Rechnen:
SELECT rp_menge * kt_preis AS Umsatz
FROM Rechnungspositionen, Konzertticket
WHERE rp_kt_id = kt_id;

-- Summe berechnen:
SELECT SUM(rp_menge * kt_preis) AS Umsatz
FROM Rechnungspositionen, Konzertticket
WHERE rp_kt_id = kt_id
  AND kt_bezeichnung = 'Pink Floyd';

-- Anzahl zählen:
SELECT COUNT(k_nummer) AS Kundenanzahl
FROM Kunde;

-- Gruppieren: immer bei „je/pro“
SELECT k_ort, COUNT(k_nummer) AS Kundenanzahl
FROM Kunde
GROUP BY k_ort;

-- Gruppen filtern:
SELECT k_ort, COUNT(k_nummer) AS Kundenanzahl
FROM Kunde
GROUP BY k_ort
HAVING COUNT(k_nummer) > 20;

-- WHERE oder HAVING?
-- WHERE = filtert einzelne Datensätze
-- HAVING = filtert Gruppen nach COUNT/SUM

-- Beispiel richtig:
HAVING COUNT(k_nummer) > 20;

SELECT DISTINCT k_nummer, k_name
FROM Kunde;

-- Wenn gezählt oder summiert wird, oft besser GROUP BY:
SELECT k_nummer, k_name, SUM(rp_menge) AS Anzahl
FROM Kunde, Rechnung, Rechnungspositionen, Konzertticket
WHERE k_nummer = r_k_nummer
  AND r_nummer = rp_r_nummer
  AND rp_kt_id = kt_id
  AND kt_bezeichnung = 'Pink Floyd'
GROUP BY k_nummer, k_name;

-- Umsatz + Anzahl:
SELECT SUM(rp_menge * kt_preis) AS Umsatz,
       SUM(rp_menge) AS Anzahl
FROM Rechnungspositionen, Konzertticket
WHERE rp_kt_id = kt_id
  AND kt_bezeichnung = 'Pink Floyd';

-- KiTa-Gebühren-Muster:
SELECT SUM(b_anzahl_monate * ba_kostenbeitrag) AS Gebuehr
FROM Kind, Betreuung, Betreuungsangebot
WHERE k_id = b_k_id
  AND b_ba_id = ba_id
  AND k_name = 'Gramm'
  AND k_vorname = 'Anna';

-- Reha-/Training-Muster:
SELECT p_name, tp_datum
FROM Patient, Trainingsplan, Uebung
WHERE p_id = tp_p_id
  AND ue_id = tp_ue_id
  AND ue_bez = 'Beinpresse'
  AND tp_datum BETWEEN '2025-01-01' AND '2025-03-31';

-- Update: Daten ändern:
UPDATE Kunde
SET k_name = 'Müller-Lüdenscheidt'
WHERE k_nummer = 2143;

-- Wichtig bei UPDATE:
-- Niemals WHERE vergessen, sonst werden alle Datensätze geändert.
-- Typische Wörter:
-- „alle“ → SELECT ... FROM ...
-- „mit der Nummer“ → WHERE id = ...
-- „sortieren“ → ORDER BY
-- „Umsatz“ → SUM(menge * preis)
-- „Anzahl“ → COUNT oder SUM(menge)
-- „je/pro“ → GROUP BY
-- „mehr als“ bei Gruppen → HAVING
-- „nur einmal“ → DISTINCT oder GROUP BY
-- „ändern“ → UPDATE ... SET ... WHERE ...
-- Vorgehen:
-- 1. Was soll ausgegeben werden? → SELECT
-- 2. In welchen Tabellen stehen die Spalten? → FROM
-- 3. Wie hängen die Tabellen zusammen? → WHERE id = fremd_id
-- 4. Welche Bedingungen gibt es? → AND ...
-- 5. Wird gezählt/summiert? → COUNT/SUM
-- 6. Kommt je/pro vor? → GROUP BY
-- 7. Kommt mehr als bei Anzahl/Summe vor? → HAVING
up
-1
PrzemekG_ at poczta dot onet dot pl
20 years ago
If you want to do someting like this:
<?php
foreach($MyObject as $key => &$value)
   $value = 'new '.$value;
?>
you must return values by reference in your iterator object:
<?php
class MyObject implements Iterator
{
/* ...... other iterator functions ...... */
/* return by reference */
public function &current()
{
   return $something;
}
?>

This won't change values:
<?php
foreach($MyObject as $key => $value)
   $value = 'new '.$value;
?>

This will change values:
<?php
foreach($MyObject as $key => &$value)
   $value = 'new '.$value;
?>

I think this should be written somewhere in the documentations, but I couldn't find it.
To Top