php arrays reference arrayaccess

ArrayAccess en PHP-asignando a compensación por referencia



arrays reference (2)

A lo que se refiere el manual se les llama "modificaciones indirectas". Considere el siguiente script:

$array = new ArrayObject; $array[''foo''] = array(); $array[''foo''][''bar''] = ''foobar'';

En el script anterior $array[''foo''] = array(); activará un offsetSet(''foo'', array()) . $array[''foo''][''bar''] = ''foobar''; por otro lado activará un offsetGet(''foo'') . ¿Porque? La última línea se evaluará aproximadamente de esta manera debajo del capó:

$tmp =& $array[''foo'']; $tmp[''bar''] = ''foobar'';

Entonces $array[''foo''] se obtiene primero por ref y luego se modifica. Si su offsetGet regresa por ref, esto tendrá éxito. Si no, obtendrá algún error de modificación indirecta.

Lo que quiere, por otro lado, es exactamente lo contrario: no obtenga un valor por referencia, sino que se lo asigne. Esto teóricamente requeriría una firma de offsetSet($key, &$value) , pero en la práctica esto simplemente no es posible .

Por cierto, las referencias son difíciles de entender. Obtendrá muchos comportamientos no obvios y esto es especialmente cierto para las referencias de elementos de la matriz (tienen reglas especiales). Te recomiendo que los evites por completo.

Primero, una cita del manual de ole en ArrayAccess::offsetSet() :

Esta función no se llama en las asignaciones por referencia y, de lo contrario, se realizan cambios indirectos en las dimensiones de la matriz sobrecargadas con ArrayAccess (indirectamente en el sentido de que no se realizan cambiando la dimensión directamente, sino cambiando una sub-dimensión o subpropiedad o asignando la dimensión de la matriz por referencia a otra variable). En su lugar, se ArrayAccess::offsetGet() . La operación solo será exitosa si ese método regresa por referencia, lo cual solo es posible desde PHP 5.3.4 .

Estoy un poco confundido por esto. Parece que esto sugiere que (a partir de 5.3.4 ) se puede definir offsetGet() para devolver por referencia en una clase de implementación, por lo tanto, manejar las asignaciones por referencia.

Entonces, ahora un fragmento de prueba:

( Hacer caso omiso de la ausencia de validación y comprobación isset() )

class Test implements ArrayAccess { protected $data = array(); public function &offsetGet($key) { return $this->data[$key]; } public function offsetSet($key, $value) { $this->data[$key] = $value; } public function offsetExists($key) { /* ... */ } public function offsetUnset($key) { /* ... */ } } $test = new Test(); $test[''foo''] = ''bar''; $test[''foo''] = &$bar; // Fatal error: Cannot assign by reference to // overloaded object in var_dump($test, $bar);

Ok, eso no funciona. Entonces, ¿a qué se refiere esta nota manual?

Razón
Me gustaría permitir la asignación por referencia a través del operador de matriz a un objeto que implementa ArrayAccess , como se muestra en el fragmento de ejemplo. He investigado esto antes y no pensé que fuera posible, pero habiendo vuelto a esto debido a la incertidumbre, descubrí esta mención en el manual. Ahora estoy confundido.

Actualización : Cuando llegué a Publicar su pregunta , me di cuenta de que esto probablemente solo se refiere a la asignación por referencia a otra variable, como $bar = &$test[''foo'']; . Si ese es el caso, entonces disculpas; aunque saber cómo, si es posible, asignar por referencia al objeto sobrecargado sería genial.

Más detalles : a lo que se reduce, me gustaría tener los siguientes alias de métodos:

isset($obj[$key]); // $obj->has_data($key); $value = $obj[$key]; // $obj->get_data($key); $obj[$key] = $value; // $obj->set_data($key, $value); $obj[$key] = &$variable; // $obj->bind_data($key, $variable); // also, flipping the operands is a syntactic alternative $variable = &$obj[$key]; // $obj->bind_data($key, $variable); unset($obj[$key]); // $obj->remove_data($key);

En lo que se refiere a los métodos admitidos de ArrayAccess , no hay problema con los métodos compatibles de ArrayAccess . La funcionalidad de enlace es donde estoy perdido y estoy empezando a aceptar que las limitaciones de ArrayAccess y PHP son simplemente prohibitivas.


Esto no funciona con ArrayAccess , podría agregarse una función pública que le permita establecer una referencia a un desplazamiento (seguro, esto se ve diferente al uso de sintaxis de matriz, por lo que no es una respuesta suficiente):

class Test implements ArrayAccess{ protected $_data = array(); public function &offsetGet($key){ return $this->_data[$key]; } ... public function offsetSetReference($key, &$value) { $this->_data[$key] = &$value; } } $test = new Test(); $test[''foo''] = $var = ''bar''; $test->offsetSetReference(''bar'', $var); $var = ''foo''; echo $test[''bar'']; # foo $alias = &$test[''bar'']; $alias = ''hello :)''; echo $var; # hello :)

Probablemente esa función se olvidó cuando se implementó por primera vez ArrayAccess .

Editar: pasarlo como "una asignación de referencia":

class ArrayAccessReferenceAssignment { private $reference; public function __construct(&$reference) { $this->reference = &$reference; } public function &getReference() { $reference = &$this->reference; return $reference; } } class Test implements ArrayAccess{ ... public function offsetSet($key, $value){ if ($value instanceof ArrayAccessReferenceAssignment) { $this->offsetSetReference($key, $value->getReference()); } else { $this->_data[$key] = $value; } }

Que luego funciona perfectamente porque lo implementaste. Probablemente sea una interfaz más agradable que la variante más explícita de offsetSetReference anterior:

$test = new Test(); $test[''foo''] = $var = ''bar''; $test[''bar''] = new ArrayAccessReferenceAssignment($var); $var = ''foo''; echo $test[''bar'']; # foo $alias = &$test[''bar'']; $alias = ''hello :)''; echo $var; # hello :)