Создание объектно-ориентированного аналога массива в PHP. Часть 2

Stepler, 28.12.2008

В этой статье мы продолжаем улучшать созданный в первой части класс 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 рассылку или рассылку по электронной почте. Так же вы можете следить за нами в Twitter.

Категории: PHP | Комментировать

Комментарии (6)

  1. Сергей / 28.12.2008 в 22:59

    Прошу прощения за комментарий не по теме, но не могли бы ли вы настроить свой блог таким образом, чтобы он не пихал в RSS весь пост? Спасибо.

  2. Александр Анатольевич / 29.12.2008 в 02:56

    А можно весь пост, пожалуйста.

  3. Skaizer / 29.12.2008 в 10:06

    Вообще на мой взгляд удобно в ридере читать весь пост сразу. Да и минусом укороченного поста является то, что wordpress урезает все теги, и в rss кидается только чистый текст, никак не отформатированный. Поэтому оставим как есть.

  4. Сергей / 29.12.2008 в 13:19

    @Skaizer
    Это ваше право, конечно, но в случае аггрегации своего фида в группе — я говорю о Планете developers.org.ua (надо заметить, что в целом непонятно каким боком вы относитесь к украинским разработчикам), это выглядит неуважением к читателям.

  5. Skaizer / 29.12.2008 в 14:08

    Вообще, основная цель RSS данного блога — удобство для наших посетителей читать посты в своем ридере, а не агрегация фида на подобных developers.org.ua сайтах.

    И вы правильно заметили, к украинским разработчикам никаким боком мы не относимся ;-) , но не видим ничего плохого в том, что они находят наш блог интересным.

    И кстати, в разделе "Планета" не одни наши посты передаются в полном виде, на мой взгляд проще решить проблему на уровне импорта в систему, скажем, ставить ссылку "Читать далее" после 3-го или 4-го предложения в RSS фиде, а не импортировать его целиком. Т.е. это уже задача разработчиков developers.org.ua.

  6. Евгенйи / 21.07.2010 в 11:04

    Сергей, ты дурак чтоль? Удобнее читать полный пост. Неудобно — не читай.

Оставить комментарий

480×60
480×60