ArrayIterator: offsetUnset() setzt den Index auf Anfang

Kürzlich bin ich über ein Verhalten des ArrayIterator gestolpert, dass mir so nicht bewusst war und erstmal für Verwirrung gesorgt hat: ArrayIterator::offsetUnset() setzt den Index des Iterators zurück auf das erste Element. Das führt dazu, dass die Iteration mehrmals durchläuft, wenn offsetUnset() innerhalb der Iteration durchgeführt wird. Hier ein Beispiel zur Verdeutlichung:

$array = [
	'one'   => '1',
	'two'   => '2',
	'three' => '3',
	'four'  => '4'
];
$iterator = new ArrayIterator( $array );
while ( $iterator->valid() ) {
	var_dump( $iterator->key() );
	if ( 'three' === $iterator->key() ) {
		$iterator->offsetUnset( 'three' );
	}

	$iterator->next();
}
string(3) "one"
string(3) "two"
string(5) "three"
string(3) "two"
string(4) "four"

Bei jedem Durchlauf wird der Schlüssel ausgegeben und mit next() der Index auf das nächste Element gesetzt. In dem Moment wo offsetUnset() aufgerufen wird, setzt sich auch der Index auf das erste Element zurück. Da im Anschluss wieder next() aufgerufen wird, sieht man den Schlüssel one nur einmal. Es spielt hier übrigens keine Rolle, ob der Iterator mit einem while oder einem foreach Konstrukt durchlaufen wird.

$iterator = new ArrayIterator( $array );
foreach ( $iterator as $key => $value ) {
	var_dump( $key );
	if ( 'three' === $key ) {
		$iterator->offsetUnset( $key );
	}
}

Ist semantisch exakt das gleiche.

Die einzige – mir bekannte – Möglichkeit, Iterationsschritte nicht zu wiederholen ist es, einen eigenen Index zu verwenden. Dabei setzt man den Index des Iterators nach dem offsetUnset() Aufruf manuell wieder auf die aktuelle Position. Die entsprechende Methode heißt ArrayIterator::seek():

$iterator = new ArrayIterator( $array );
$i = 0;
while ( $iterator->valid() ) {
	var_dump( $iterator->key() );
	if ( 'three' === $iterator->key() ) {
		$iterator->offsetUnset( 'three' );
		$iterator->seek( $i - 1 );
	}

	$iterator->next();
	$i++;
}

Jetzt wird jeder Iterationsschritt nur noch einmal aufgerufen.

Der Index des Iterators ist nicht zu verwechseln mit dem internen Index des Arrays selbst. Letzteren verändert der Iterator nicht: key( $array ) wird in jeder Iterationsstufe "one" zurückgeben.

Eine foreach Schleife und unset() direkt auf das Array angewendet, verhält sich im Gegensatz zu ArrayIterator etwas… gewohnter:

foreach ( $array as $key => $item ) {

	var_dump( $key );
	if ( 'three' === $key )
		unset( $array[ $key ] );
}
string(3) "one"
string(3) "two"
string(5) "three"
string(4) "four"

Quizfrage: Was würde man sehen, wenn man im letzten Beispiel var_dump( $key ) durch var_dump( key( $array ) ) ersetzen würde. Hinweis: es hat nichts mit dem unset() Aufruf zu tun.

Kommentare

Es wurden noch keine Kommentarte zu diesem Artikel geschrieben.

Fragen, Ideen oder Kritik? – Hier ist Platz dafür!

Dein Kommentar

Um ein Kommentar abzugeben, reicht der Text im Kommentarfeld. Die Angabe eines Namens wäre nett, ist aber nicht erforderlich.

Du darfst folgenden HTML-Code verwenden, musst aber nicht:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>