Создание объектно-ориентированного аналога массива в PHP. Часть 2
В этой статье мы продолжаем улучшать созданный в первой части класс ObjectsArr.
В прошлый раз мы описали базовую функциональность класса, позволяющую управлять своим содержимым. Но в таком виде его использование не является удобным. В этой статье мы это исправим.
Добавляя данные в массив, мы привыкли использовать конструкцию $objArray[key] = value и врядли мы нас устроит текущая альтернатива — $objArray->add(value, key). Так же в текущей реализации невозможно просмотреть массив с помощью конструкции foreach, поэтому если мы добавляем элемент без ключа (ключ будет присвоен автоматически), то мы фактически «теряем» элемент.
К счастью, разработчики PHP позаботились об этом, и предоставили в наше распоряжение различные предопределенные интерфейсы, которые позволяют выполнять несвойственные объектам операции.
Наш класс ObjectsArr будет реализовывать интерфейсы: ArrayAccess, IteratorAggregate и Countable. Также будет создан новый класс ObjectsArrInrerator, реализующий интерфейс Iterator.
Теперь обо всем по порядку:
Интерфейс ArrayAccess — позволяет обращаться к объекту, используя принцип обращения к обычному массиву.
Интерфейс Countable — позволяет применять функцию count к объекту.
Класс ObjectsArrInrerator в связке с интерфейсом IteratorAggregate — позволяет просматривать массив, используя оператор foreach.
Реализация интерфейса ArrayAccess
//обновляем строку инициализации класса
class ObjectsArr implements ArrayAccess {
//добавляем новые методы
public function offsetSet($key, $value) {
$this->add($value, $key);
}
public function offsetUnset($key) {
$this->del($key);
}
public function offsetGet($key) {
return $this->get($key);
}
public function offsetExists($key) {
return $this->exist($key);
}
//остальное описание класса …
}
Как вы можете видеть, новые методы являются оберткой уже существующих, но именно с помощью них можно обращаться к объекту, как к массиву. В частности теперь возможно:
- использовать
$objArray[key] = valueвместо$objArray->add(value, key) - использовать
unset($objArray[key])вместо$objArray->del(key) - использовать
$objArray[key]вместо$objArray->get(key) - использовать
isset($objArray[key])илиempty($objArray[key])вместо$objArray->exist (key)
Реализация интерфейса Countable
//обновляем строку инициализации класса
class ObjectsArr implements ArrayAccess, Countable {
//добавляем новые методы
public function count() {
return $this->length();
}
//остальное описание класса …
}
Как и в предыдущем случае, теперь можно использовать count($objArray) вместо $objArray->length().
Разработка класса ObjectsArrInrerator и реализация интерфейса IteratorAggregate
Новый класс, который будет использоваться при просмотре объекта в foreach:
class ObjectsArrInrerator implements Iterator {
private $_list;
private $_current;
private $_keys;
function __construct(ObjectsArr $obj) {
$this->_list = $obj;
$this->_keys = $this->_list->keys();
}
function rewind() {
$this->_current = 0;
}
function valid() {
return $this->_current < $this->_list->length();
}
function key() {
return $this->_keys[$this->_current];
}
function next() {
$this->_current++;
}
function current() {
return $this->_list->get($this->_keys[$this->_current]);
}
}
Готово! Эти методы объяснять думаю не стоит. Названия у них говорят сами за себя. Они будут использоваться конструкцией foreach при просмотре объекта. Если Вам интересна последовательность их вызова, добавьте в каждый из них echo с названием метода, и увидите как foreach «смотрит» наш объект.
И последнее на сегодня:
//обновляем строку инициализации класса
class ObjectsArr implements ArrayAccess, Countable, IteratorAggregate {
//добавляем новые методы
function getIterator() {
return new ObjectsArrInrerator (clone $this);
}
//остальное описание класса …
}
Метод getIterator будет вызываться при попытке просмотреть объект через foreach.
В PHP существует сразу встроенный аналог только что созданного нами класса. Это класс ArrayObject. Но попытка изменения объекта в ходе перебора его элементов, может привести к плачевным последствиям. Об этом я написал в комментарии, к предыдущей статье. Избежать такого поведения позволяет передача интератору, не самого просматриваемого объекта, а его копии — return new ObjectsArrInrerator(clone $this).
Тестирование класса ObjectsArr
Осталось только протестировать наш класс. Для тестирования я взял тот же пример, что и в предыдущей статье, чтобы наглядно было видно как изменился наш класс.
$clientsList = new ObjectsArr();
try {
$clientsList['ivan'] = new Client('Ваня'); //Добавляем нового клиента
$clientsList['alexander'] = new Client('Саша'); //Добавляем нового клиента
$clientsList['petr'] = new Client('Петя'); //Добавляем нового клиента
$objIvan = $clientsList['ivan']; //Извлекаем клиента
echo $objIvan; // выведет -> Клиент - "Ваня"
foreach ($clientsList as $key => $value) { //Просмотр всей коллекции объектов
echo $key . ' -> ' . $value . "\r\n";
}
unset($clientsList['ivan']); //удаляем клиента
$clientsList['petr'] = new Client('Петр Иванович'); /*перехват исключения, т.к. ключ "petr" уже используется
выведет -> Ключ "petr" уже используется. */
} catch (Exception $e) {
echo $e->getMessage();
}
На данный момент все. Мы реализовали еще одно из предъявленных к классу ObjectsArr требований, а именно:
возможность перебора всей коллекции элементов
Использование класса так же стало более удобным. Теперь его трудно отличить от обычного массива.
В следующей — заключительной статье мы добавим механизм заполнения массива при первом обращении к нему.
Похожие темы
RSS-лента комментариев к этой статье | Обратная ссылка (trackback link)

Прошу прощения за комментарий не по теме, но не могли бы ли вы настроить свой блог таким образом, чтобы он не пихал в RSS весь пост? Спасибо.
А можно весь пост, пожалуйста.
Вообще на мой взгляд удобно в ридере читать весь пост сразу. Да и минусом укороченного поста является то, что wordpress урезает все теги, и в rss кидается только чистый текст, никак не отформатированный. Поэтому оставим как есть.
@Skaizer
Это ваше право, конечно, но в случае аггрегации своего фида в группе — я говорю о Планете developers.org.ua (надо заметить, что в целом непонятно каким боком вы относитесь к украинским разработчикам), это выглядит неуважением к читателям.
Вообще, основная цель RSS данного блога — удобство для наших посетителей читать посты в своем ридере, а не агрегация фида на подобных developers.org.ua сайтах.
И вы правильно заметили, к украинским разработчикам никаким боком мы не относимся
, но не видим ничего плохого в том, что они находят наш блог интересным.
И кстати, в разделе "Планета" не одни наши посты передаются в полном виде, на мой взгляд проще решить проблему на уровне импорта в систему, скажем, ставить ссылку "Читать далее" после 3-го или 4-го предложения в RSS фиде, а не импортировать его целиком. Т.е. это уже задача разработчиков developers.org.ua.
Сергей, ты дурак чтоль? Удобнее читать полный пост. Неудобно — не читай.