Pregunta

Estoy en lo correcto al pensar que sólo proporciona IteratorAggregate tipo array leer acceso a un objeto? Si necesito escribir en el objeto de matriz-como-, entonces necesito el uso Iterator?

Demostración de un objeto IteratorAggregate causando un error grave cuando se trata de añadir un nuevo elemento de la siguiente manera:

<?

class foo implements IteratorAggregate {

    public $_array = array('foo'=>'bar', 'baz'=>'quux');

    public function getIterator() {
        return new ArrayIterator($this->_array);
    }

} // end class declaration


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

$ php test.php
key: foo, value: bar
key: baz, value: quux

Fatal error: Cannot use object of type foo as array in /var/www/test.php on line 20

Call Stack:
    0.0009      57956   1. {main}() /var/www/test.php:0
¿Fue útil?

Solución

Para el uso de lo que vale la pena, teniendo en cuenta su clase de ejemplo, se podría haber hecho solo de la ArrayObject (que implementa IteratorAggregate y ArrayAccess como queremos).

class Foo extends ArrayObject
{
    public function __construct()
    {
        parent::__construct(array('foo' => 'bar', 'baz' => 'quux'));
    }
}

$objFoo = new Foo();

foreach ( $objFoo as $key => $value ) {
    echo "$key => $value\n";
}

echo PHP_EOL;

$objFoo['foo'] = 'badger';
$objFoo['baz'] = 'badger';
$objFoo['bar'] = 'badger';

foreach ( $objFoo as $key => $value ) {
    echo "$key => $value\n";
}

Con el bienestar de salida, como se esperaba:

foo => bar
baz => quux

foo => badger
baz => badger
bar => badger

Otros consejos

Ok, ahora que entiendo mejor lo que quiere decir, voy a tratar de explicar aquí.

La implementación de un iterador (ya sea a través de la Iterator o interfaz IteratorAggregate) sólo añadir funcionalidad cuando la iteración. Lo que esto significa, es que sólo afecta a la capacidad de uso foreach. No cambia ni altera ningún otro uso del objeto. Ninguno de los cuales apoyar directamente a la "escritura" para el iterador. Yo digo directamente, ya que todavía se puede escribir en el objeto de base, mientras que la iteración, pero no dentro del foreach.

Esto significa que:

foreach ($it as $value) {
    $it->foo = $value;
}

Will funcionan bien (ya que es la escritura al objeto original).

Mientras

foreach ($it as &$value) {
    $value = 'bar';
}

Lo más probable es que no trabajo, ya que está tratando de escribir a través del repetidor (que no está soportado directamente. Puede ser capaz de hacer, pero no es el diseño de la base).

Para ver por qué, Echemos un vistazo a lo que está pasando detrás de las escenas con que foreach ($obj as $value). Básicamente es idéntica a:

Para las clases Iterator:

$obj->rewind();
while ($obj->valid()) {
    $value = $obj->current();
    // Inside of the loop here
    $obj->next();
}

Para las clases IteratorAggregate:

$it = $obj->getIterator();
$it->rewind();
while ($it->valid()) {
    $value = $it->current();
    // Inside of the loop here
    $it->next();
}

Ahora, ¿por qué no se admite la escritura? $iterator->current() no devuelve una referencia. Así no se puede escribir en él directamente utilizando foreach ($it as &$value).

Ahora, si usted quiere hacer eso, que había necesidad de alterar el iterador para devolver una referencia de current(). Sin embargo, eso sería romper la interfaz (dado que cambiaría la firma métodos). Así que eso no es posible. Por lo que significa que la escritura a la iterador no es posible.

Sin embargo, se dan cuenta que sólo afecta a la iteración en sí. No tiene nada que ver con el acceso al objeto de cualquier otro contexto o en cualquier otro Manor. $obj->bar será exactamente el mismo para un objeto iterador como lo es para un objeto no iterador.

Ahora, llega una diferencia entre el Iterator y IteratorAggregate. Mira hacia atrás en las equivalencias while, y usted puede ser capaz de ver la diferencia. Supongamos que hicimos esto:

foreach ($objFoo as $value) {
    $objFoo->_array = array();
    print $value . ' - ';
}

¿Qué pasaría? Con una Iterator, sólo se imprime el primer valor. Eso es porque el repetidor opera directamente sobre el objeto para cada iteración. Y puesto que ha cambiado lo que se repite el objeto sobre el (la matriz interna), los cambios de iteración.

Ahora, si $objFoo es un IteratorAggregate, sería imprimir todos los valores que existían al inicio de la iteración. Esto se debe a que hizo una copia de la matriz cuando regresó el new ArrayIterator($this->_array);. Para que pueda seguir para iterar sobre todo el objeto como lo fue al comienzo de la iteración.

Tenga en cuenta que la diferencia clave. Cada iteración de un Iterator será dependiente del estado del objeto en ese punto en el tiempo. Cada iteración de un IteratorAggregate dependerá del estado del objeto al comienzo de la iteración. * ¿Tiene sentido?

Ahora, en cuanto a su error específico. No tiene nada que ver con iteradores en absoluto. Usted está tratando de acceso (escritura en realidad) una variable de fuera de la iteración. Por lo que no implica el iterador en absoluto (con la excepción de que usted está tratando de comprobar los resultados de la iteración).

Usted está tratando de tratar el objeto como una matriz ($objFoo['reeb'] = 'roob';). Ahora, para los objetos normales, eso no es posible. Si quieres hacer eso, es necesario implementar la ArrayAccess interfaz . Tenga en cuenta que usted no tiene que tener un iterador definido con el fin de utilizar esa interfaz. Todo lo que hace es proporcionar la capacidad de elementos de acceso específicas de un objeto utilizando una matriz como sintaxis.

Nota que dicha matriz similares. No se puede tratar como una matriz en general. sort funciones no funcionarán. Sin embargo, hay algunas otras interfaces quese puede poner en práctica para hacer que un objeto más variedad similares. Una es la Countable interfaz que permite la capacidad de utilizar la función count() sobre un objeto (count($obj)).

Para un ejemplo en el núcleo de la combinación de todos estos objetivos en una clase, visita nuestra clase ArrayObject . Cada interfaz en esa lista proporciona la capacidad de utilizar una característica específica del núcleo. El IteratorAggregate proporciona la capacidad de uso foreach. El ArrayAccess proporciona la capacidad de acceder al objeto con una sintaxis de array-similares. La interfaz Serializable proporciona la capacidad de serialize y deserialize los datos en una casa específica. La interfaz Countable permite para contar el objeto como una matriz.

Así que dichas interfaces no afectan a la funcionalidad del núcleo del objeto de ninguna manera. Todavía se puede hacer nada con ella que se podía hacer a un objeto normal (como $obj->property o $obj->method()). Lo que hacen sin embargo es proporcionar la capacidad de utilizar el objeto como una matriz en ciertos lugares en el núcleo. Cada uno proporciona la funcionalidad en un ámbito estricto (Lo que significa que se puede utilizar cualquier combinación de ellos que le gustaría. No es necesario ser capaz de acceder al objeto como una matriz para ser capaz de count() él). Ese es el verdadero poder aquí.

Ah, y asignar el valor de retorno de new por referencia está en desuso , por lo que no hay necesidad de hacer eso ...

Así que, en cuanto a su problema específico, aquí está una manera de evitarlo. Simplemente implementado la interfaz ArrayAccess en su clase para que $objFoo['reeb'] = 'roob'; funcionará.

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]);
        }
    }
}

A continuación, se puede probar su código existente:

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

Y que debería funcionar bien:

key: foo, value: bar
key: baz, value: quux
key: foo, value: bar
key: baz, value: quux
key: reeb, value: roob

Usted podría también "solución" que cambiando la propiedad $objFoo->_array directamente (al igual que la POO regular). Basta con sustituir el $objFoo['reeb'] = 'roob'; línea con $objFoo->_array['reeb'] = 'roob';. Eso va a lograr lo mismo ....

  • Tenga en cuenta que este es el comportamiento predeterminado. Se podía cortar juntos un iterador interno (el iterador devuelto por IteratorAggreagate::getIterator) que no dependen del estado de los objetos originales. Pero no es así como se hace típicamente
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top