It bit me today, so putting it here in the hope it will help others:
If you call array_key_exists() on an object of a class that implements ArrayAccess, ArrayAccess::offsetExists() wil NOT be called.
L'interface ArrayAccess
(PHP 5 >= 5.0.0)
Introduction
Interface permettant d'accéder aux objets de la même façon que pour les tableaux.
Sommaire de l'Interface
ArrayAccess
{
/* Méthodes */
}Exemple #1 Exemple simple
<?php
class obj implements arrayaccess {
private $container = array();
public function __construct() {
$this->container = array(
"un" => 1,
"deux" => 2,
"trois" => 3,
);
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$obj = new obj;
var_dump(isset($obj["deux"]));
var_dump($obj["deux"]);
unset($obj["deux"]);
var_dump(isset($obj["deux"]));
$obj["deux"] = "Une valeur";
var_dump($obj["deux"]);
$obj[] = 'Ajout 1';
$obj[] = 'Ajout 2';
$obj[] = 'Ajout 3';
print_r($obj);
?>
L'exemple ci-dessus va afficher quelque chose de similaire à :
bool(true)
int(2)
bool(false)
string(10) "Une valeur"
obj Object
(
[container:obj:private] => Array
(
[un] => 1
[trois] => 3
[deux] => Une valeur
[0] => Ajout 1
[1] => Ajout 2
[2] => Ajout 3
)
)
Sommaire
- ArrayAccess::offsetExists — Indique si une position existe dans un tableau
- ArrayAccess::offsetGet — Position à lire
- ArrayAccess::offsetSet — Position à assigner
- ArrayAccess::offsetUnset — Position à supprimer
Per
19-May-2011 08:16
uramihsayibok, gmail, com
04-Aug-2010 09:34
EDITOR NOTE: You can return by reference in offsetGet as of PHP 5.3.4.
If you find yourself facing the dreaded "Indirect modification of overloaded element of $class has no effect" then don't worry too much: there's a clever solution. While you *CANNOT* return by-reference with offsetGet, you can return objects which _also_ implement ArrayAccess...
Example:
<?php
// sanity and error checking omitted for brevity
// note: it's a good idea to implement arrayaccess + countable + an
// iterator interface (like iteratoraggregate) as a triplet
class RecursiveArrayAccess implements ArrayAccess {
private $data = array();
// necessary for deep copies
public function __clone() {
foreach ($this->data as $key => $value) if ($value instanceof self) $this[$key] = clone $value;
}
public function __construct(array $data = array()) {
foreach ($data as $key => $value) $this[$key] = $value;
}
public function offsetSet($offset, $data) {
if (is_array($data)) $data = new self($data);
if ($offset === null) { // don't forget this!
$this->data[] = $data;
} else {
$this->data[$offset] = $data;
}
}
public function toArray() {
$data = $this->data;
foreach ($data as $key => $value) if ($value instanceof self) $data[$key] = $value->toArray();
return $data;
}
// as normal
public function offsetGet($offset) { return $this->data[$offset]; }
public function offsetExists($offset) { return isset($this->data[$offset]); }
public function offsetUnset($offset) { unset($this->data); }
}
$a = new RecursiveArrayAccess();
$a[0] = array(1=>"foo", 2=>array(3=>"bar", 4=>array(5=>"bz")));
// oops. typo
$a[0][2][4][5] = "baz";
//var_dump($a);
//var_dump($a->toArray());
// isset and unset work too
//var_dump(isset($a[0][2][4][5])); // equivalent to $a[0][2][4]->offsetExists(5)
//unset($a[0][2][4][5]); // equivalent to $a[0][2][4]->offsetUnset(5);
// if __clone wasn't implemented then cloning would produce a shallow copy, and
$b = clone $a;
$b[0][2][4][5] = "xyzzy";
// would affect $a's data too
//echo $a[0][2][4][5]; // still "baz"
?>
max at flashdroid dot com
05-Apr-2010 12:49
Objects implementing ArrayAccess may return objects by references in PHP 5.3.0.
You can implement your ArrayAccess object like this:
class Reflectable implements ArrayAccess {
public function set($name, $value) {
$this->{$name} = $value;
}
public function &get($name) {
return $this->{$name};
}
public function offsetGet($offset) {
return $this->get($offset);
}
public function offsetSet($offset, $value) {
$this->set($offset, $value);
}
...
}
This base class allows you to get / set your object properties using the [] operator just like in Javascript:
class Boo extends Reflectable {
public $name;
}
$obj = new Boo();
$obj['name'] = "boo";
echo $obj['name']; // prints boo
Cintix
28-Oct-2009 07:32
To take full advantages of all array features with ArrayAccess, then you would need to implements Countable and Iterator
Like this.
<?php
class ArrayOfColorModel implements ArrayAccess, Iterator, Countable {
private $container = array();
public function __construct() {
}
public function offsetSet($offset,$value) {
if ($value instanceof ColorModel){
if ($offset == "") {
$this->container[] = $value;
}else {
$this->container[$offset] = $value;
}
} else {
throw new Exception("Value have to be a instance of the Model ColorModel");
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
public function rewind() {
reset($this->container);
}
public function current() {
return current($this->container);
}
public function key() {
return key($this->container);
}
public function next() {
return next($this->container);
}
public function valid() {
return $this->current() !== false;
}
public function count() {
return count($this->container);
}
}
?>
Now you can using it like any other array.
<?php
$array = new ArrayOfColorModel();
foreach ($array as $model) {
var_export($model);
}
// OR
for($i=0;$i<count($array);$i++){
var_export($array[$i]);
}
?>
AryehGregor+php-comment at gmail dot com
08-Jan-2009 10:33
Note that at least in PHP 5.1, objects implementing ArrayAccess cannot return objects by reference. See http://bugs.php.net/bug.php?id=34783 . If you have code like
<?php
$x = &$y[0];
?>
then this will (as far as I can tell) *always* fail unless $y is a real array -- it cannot work if $y is an object implementing ArrayAccess. If your offsetGet() function returns by reference, you get the fatal error "Declaration of MyClass::offsetGet() must be compatible with that of ArrayAccess::offsetGet()". If you try to have it return by value, however, you get the (contradictory) fatal error "Objects used as arrays in post/pre increment/decrement must return values by reference", at least in my version of PHP.
It is therefore not possible to take arbitrary code dealing with arrays and try to substitute an object of your own for an array, even if all of the normal array functions didn't fail as well (which they do, or at least some of them).
