update page now

Voting

: min(three, nine)?
(Example: nine)

The Note You're Voting On

Taliesin Nuin public at taliesinnuin dot net
6 years ago
You might be wondering whether implementing an ArrayAccess interface makes the class iterable. It is, after all, an "array". The answer is no, it doesn't. Additionally there are a couple of subtle gotchas if you add both and want it to be an associate array. The below is a class that has both ArrayAccess AND Iterator interfaces. And Countable as well just to be complete.
<?php
//This uses return types which are only valid in PHP 7. They can be removed if you are forced to use an older version of PHP.
//N.b. The offsetSet method contains a function that is only valid from PHP 7.3 onwards.
class HandyClass implements ArrayAccess, Iterator, Countable {
 private $container = array(); //An Array of your actual values.
 private $keys = array(); //We use a separate array of keys rather than $this->position directly so that we can
 private $position; //have an associative array.
 public function __construct() {
 $position = 0;
 $this->container = array( //Arbitrary array for demo. You probably want to set this to empty in practice or
 "a" => 1, //get it from somewhere else, e.g. passing it into the constructor.
 "b" => 2,
 "c" => 3,
 );
 $this->keys = array_keys($this->container);
 }
 public function count() : int { //This is necessary for the Countable interface. It could as easily return
 return count($this->keys); //count($this->container). The number of elements will be the same.
 }
 public function rewind() { //Necessary for the Iterator interface. $this->position shows where we are in our list of
 $this->position = 0; //keys. Remember we want everything done via $this->keys to handle associative arrays.
 }
 public function current() { //Necessary for the Iterator interface.
 return $this->container[$this->keys[$this->position]];
 }
 public function key() { //Necessary for the Iterator interface.
 return $this->keys[$this->position];
 }
 public function next() { //Necessary for the Iterator interface.
 ++$this->position;
 }
 public function valid() { //Necessary for the Iterator interface.
 return isset($this->keys[$this->position]);
 }
 public function offsetSet($offset, $value) { //Necessary for the ArrayAccess interface.
 if(is_null($offset)) {
 $this->container[] = $value;
 $this->keys[] = array_key_last($this->container); //THIS IS ONLY VALID FROM php 7.3 ONWARDS. See note below for alternative.
 } else {
 $this->container[$offset] = $value;
 if(!in_array($offset, $this->keys)) $this->keys[] = $offset;
 }
 }
 public function offsetExists($offset) {
 return isset($this->container[$offset]);
 }
 public function offsetUnset($offset) {
 unset($this->container[$offset]);
 unset($this->keys[array_search($offset,$this->keys)]);
 $this->keys = array_values($this->keys); //This line re-indexes the array of container keys because if someone
 } //deletes the first element, the rewind to position 0 when iterating would
 //cause no element to be found.
 public function offsetGet($offset) {
 return isset($this->container[$offset]) ? $this->container[$offset] : null;
 }
}
?>

Example usages:
<?php
$myClass = new HandyClass();
echo('Number of elements: ' . count($myClass) . "\n\n");
echo("Foreach through the built in test elements:\n");
foreach($myClass as $key => $value) {
 echo("$value\n");
}
echo("\n");
$myClass['d'] = 4;
$myClass['e'] = 5;
echo('Number of elements after adding two: ' . count($myClass) . "\n\n");
unset($myClass['a']);
echo('Number of elements after removing one: ' . count($myClass) . "\n\n");
echo("Accessing an element directly:\n");
echo($myClass['b'] . "\n\n");
$myClass['b'] = 5;
echo("Foreach after changing an element:\n");
foreach($myClass as $key => $value) {
 echo("$value\n");
}
echo("\n");
?>

<< Back to user notes page

AltStyle によって変換されたページ (->オリジナル) /