Ok, maintenant que je comprends mieux ce que vous voulez dire, je vais essayer de expliquer ici.
L'implémentation d'un itérateur (via l'interface Iterator
ou IteratorAggregate
) ne fera qu'ajouter des fonctionnalités lors de l'itération. Qu'est-ce que cela signifie, est-ce que cela affecte uniquement la possibilité d'utiliser foreach
. Il ne modifie ni ne modifie aucune autre utilisation de l'objet. Aucun d'entre eux ne supporte directement "l'écriture" de l'itérateur. Je dis directement, puisque vous pouvez toujours écrire sur l'objet de base pendant l'itération, mais pas à l'intérieur du foreach.
Cela signifie que:
foreach ($it as $value) {
$it->foo = $value;
}
fonctionne très bien (car il est écrit à l'objet original).
Alors que
foreach ($it as &$value) {
$value = 'bar';
}
Très probablement ne fonctionnera pas car il est en train d'écrire à travers le (Il peut être en mesure de faire qui ne sont pas directement pris en charge., Mais pas la conception de base) iterator. Pour voir pourquoi, regardons ce qui se passe dans les coulisses avec le foreach ($obj as $value)
. Il est fondamentalement identique à:
Pour Iterator
cours:
$obj->rewind();
while ($obj->valid()) {
$value = $obj->current();
// Inside of the loop here
$obj->next();
}
Pour IteratorAggregate
cours:
$it = $obj->getIterator();
$it->rewind();
while ($it->valid()) {
$value = $it->current();
// Inside of the loop here
$it->next();
}
Maintenant, voyez-vous pourquoi l'écriture ne sont pas pris en charge? $iterator->current()
ne renvoie pas de référence. Vous ne pouvez donc pas écrire directement en utilisant foreach ($it as &$value)
. Maintenant, si vous voulez faire cela, vous devez modifier l'itérateur pour renvoyer une référence de current()
. Cependant, cela casserait l'interface (puisque cela changerait la signature des méthodes). Donc ce n'est pas possible. Cela signifie donc que l'écriture dans l'itérateur n'est pas possible. Toutefois, sachez que cela n'affecte que l'itération elle-même. Cela n'a rien à voir avec l'accès à l'objet depuis n'importe quel autre contexte ou dans n'importe quel autre manoir. $obj->bar
sera exactement le même pour un objet itérateur que pour un objet non itérateur. Maintenant, il y a une différence entre Iterator
et IteratorAggregate
. Revenez sur les équivalences while
, et vous pourrez peut-être voir la différence.Supposons que nous l'ayons fait:
foreach ($objFoo as $value) {
$objFoo->_array = array();
print $value . ' - ';
}
Que se passerait-il? Avec un Iterator
, il imprime seulement la première valeur. C'est parce que l'itérateur opère directement sur l'objet pour chaque itération. Et puisque vous avez changé ce sur quoi l'objet itère (le tableau interne), l'itération change.
Maintenant, si $objFoo
est un IteratorAggregate
, il affichera toutes les valeurs qui existaient au début de l'itération. C'est parce que vous avez fait une copie du tableau lorsque vous avez retourné le new ArrayIterator($this->_array);
. Vous pouvez donc continuer à parcourir tout l'objet tel qu'il était au début de l'itération.
Notez cette différence de clé. Chaque itération d'un Iterator
dépendra de l'état de l'objet à ce moment-là. Chaque itération d'un IteratorAggregate
dépendra de l'état de l'objet au début de l'itération. * Cela a-t-il un sens?
Maintenant, en ce qui concerne votre erreur spécifique. Cela n'a rien à voir avec les itérateurs. Vous essayez d'accéder (écrire réellement) à une variable en dehors de l'itération. Cela n'implique donc pas du tout l'itérateur (sauf que vous essayez de vérifier les résultats en itérant).
Vous essayez de traiter l'objet sous la forme d'un tableau ($objFoo['reeb'] = 'roob';
). Maintenant, pour les objets normaux, ce n'est pas possible. Si vous voulez faire cela, vous devez implémenter le ArrayAccess
interface. Notez que vous n'avez pas besoin d'avoir un itérateur défini pour utiliser cette interface. Tout ce qu'il fait est de fournir la possibilité d'accéder à des éléments spécifiques d'un objet en utilisant une syntaxe de type tableau.
note J'ai dit tableau comme. Vous ne pouvez pas le traiter comme un tableau en général. Les fonctions sort
ne fonctionneront jamais. Cependant, il existe quelques autres interfaces que vous pouvez implémenter pour rendre un objet plus semblable à un tableau. L'un est le Countable
interface qui permet d'utiliser la fonction count()
sur un objet (count($obj)
).
Pour un exemple dans le noyau de combiner tous ces dans une classe, consultez le ArrayObject
class. Chaque interface de cette liste offre la possibilité d'utiliser une caractéristique spécifique du cœur. Le IteratorAggregate
permet d'utiliser foreach
. Le ArrayAccess
fournit la possibilité d'accéder à l'objet avec une syntaxe de type tableau. L'interface Serializable
fournit la capacité de serialize
et deserialize
les données dans un manoir spécifique. L'interface Countable
permet de compter l'objet comme un tableau. Par conséquent, ces interfaces n'affectent en aucune manière la fonctionnalité principale de l'objet. Vous pouvez toujours faire tout ce que vous pourriez faire pour un objet normal (tel que $obj->property
ou $obj->method()
). Ce qu'ils font cependant, c'est fournir la possibilité d'utiliser l'objet comme un tableau à certains endroits dans le noyau. Chacun fournit la fonctionnalité dans une portée stricte (ce qui signifie que vous pouvez utiliser n'importe quelle combinaison de ceux que vous souhaitez.Vous n'avez pas besoin d'être en mesure d'accéder à l'objet comme un tableau pour pouvoir le count()
). C'est le vrai pouvoir ici.
Oh, et assigning the return value of new
by reference is deprecated, donc il n'y a pas besoin de le faire ...
Alors, comme pour votre problème, voici une façon de le contourner. J'ai simplement implémenté l'interface ArrayAccess
dans votre classe afin que $objFoo['reeb'] = 'roob';
fonctionne.
class foo implements ArrayAccess, IteratorAggregate {
public $_array = array('foo'=>'bar', 'baz'=>'quux');
public function getIterator() {
return new ArrayIterator($this->_array);
}
public function offsetExists($offset) {
return isset($this->_array[$offset]);
}
public function offsetGet($offset) {
return isset($this->_array[$offset]) ? $this->_array[$offset] : null;
}
public function offsetSet($offset, $value) {
$this->_array[$offset] = $value;
}
public function offsetUnset($offset) {
if (isset($this->_array[$offset]) {
unset($this->_array[$offset]);
}
}
}
Ensuite, vous pouvez essayer votre code existant:
$objFoo = & new foo();
foreach ($objFoo as $key => $value) {
echo "key: $key, value: $value\n";
}
$objFoo['reeb'] = "roob";
foreach ($objFoo as $key => $value) {
echo "key: $key, value: $value\n";
}
Et il devrait fonctionner correctement:
key: foo, value: bar
key: baz, value: quux
key: foo, value: bar
key: baz, value: quux
key: reeb, value: roob
Vous pouvez aussi "réparer" en changeant la propriété $objFoo->_array
directement (juste comme OOP régulier). Il suffit de remplacer la ligne $objFoo['reeb'] = 'roob';
par $objFoo->_array['reeb'] = 'roob';
. Cela va accomplir la même chose ....
- Notez qu'il s'agit du comportement par défaut. Vous pouvez pirater un itérateur interne (l'itérateur retourné par
IteratorAggreagate::getIterator
) qui dépend de l'état des objets d'origine. Mais ce n'est pas la façon dont il est généralement fait
Je suis confus. Je n'ai pas essayé d'écrire pendant l'itération; la boucle 'foreach' a été fermée avant' $ objFoo ['reeb'] = "roob"; ', mais cela donne quand même une erreur. Il aurait dû être réinitialisé à ce moment-là, n'est-ce pas? – user151841
C'était. La chose est que vous essayez d'écrire à l'objet de la classe 'foo' comme un tableau. Cela ne fonctionne que si vous implémentez l'interface 'ArrayAccess' (qui active la syntaxe' $ obj [$ key] '). Sinon, il n'est pas activé. L'erreur que vous avez commise est parce que vous essayez de traiter '$ objFoo' comme un tableau, mais comme il n'a pas l'interface' ArrayAccess', il ne sait pas quoi faire, il s'agit donc d'erreurs fatales. Ajoutez l'interface 'ArrayAccess' à' foo' et implémentez les 4 méthodes requises. Ensuite, ça marchera bien ... Je vais éditer ma réponse avec un exemple ... – ircmaxell
Alors, dans quels contextes pouvez-vous écrire à un 'IteratorAggregate'? – user151841