Переход к PHP5 - Итерация по свойствам класса
ОГЛАВЛЕНИЕ
Итерация по свойствам класса
Все переменные класса, доступные в текущем контексте, могут быть перебранны циклом foreach. Такая итерация по свойствам класса может очень пригодится при клонировании объектов. Например, если необходимо создать клон объекта с большим количеством переменных класса, то можно сделать примерно так:
class Node {
private $value;
private $parent;
...
private $type;
function __clone() {
foreach ($that as $propertyName => $propertyValue) {
$this->$propertyName = $propertyValue;
}
}
function setParent($value) { $this->parent = $value; }
function getParent() { return $this->parent; }
function setValue($value) { $this->value = $value; }
function getValue() { return $this->value; }
...
function setType($value) { $this->type = $value; }
function getType() { return $this->type; }
}
$myNode = new Node();
$myNode->setValue(10);
$myNextNode = $myNode->__clone();
print $myNextNode->getValue();
Простой цикл очень хорошо заменяет большое количество присваиваний и избавляет от необходимости синхронизировать присваивания при клонировании со списком всех переменных класса. Очевидным образом, эта итерация не может быть применена к свойствам класса, реализованных через __get()/__set() функции.
Изменение стандартной итерации по свойствам
Стандартными элементами языка, позволящими итерацию, являются массивы. Но их недостатки очевидны - можно по ошибке поместить в массив элемент другого типа. К массиву не добавишь методы проверки содержимого и т.п.
Для введения дополнительных сущностей (классов), позволяющих итерацию по своим элементам, предусмотренно 2 интерфейса: IteratorAggregate и Iterator.
interface IteratorAggregate {
function getIterator(); // возвращает массив или объект
}
IteratorAggregate может использоваться, когда данные для итерации можно предоставить в одной из стандарных конструкций PHP, позволяющих итерацию: массива или объекта, реализующего Iterator.
Пример использования IteratorAggregate с итерацией по элементам массива:
/**
Замечание:
Этот пример в PHP5 beta 3 не работает.
Тем не менее, в документации заявленно, что getIterator()
может возвращать массив или объект, реализующий Iterator.
Так что к release, надеюсь, исправят.
*/
class Control implements IteratorAggregate {
private $controls;
private $name;
function __construct() {
$this->controls = array();
}
function addControl($obj) {
$this->controls[$obj->getName()] = $obj;
}
function setName($value) { $this->name = $value; }
function getName() { return $this->name; }
// эта функция из IteratorAggregate
function getIterator() {
return $this->controls;
}
}
$c1 = new Control();
$c1->setName('userId');
$c2 = new Control();
$c2->setName('userName');
$form = new Control();
$form->addControl($c1);
$form->addControl($c2);
foreach ($form as $ctrl) {
echo $ctrl->getName() . '
';
}
Этот вариант является только надстройкой над стандартной функциональностью PHP и может использоваться когда до начала итерации можно получить все элементы, участвующие в итрации. Если же этого сделать нельзя (в случае получения записей из базы данных или чтении строк из большого файла), используется другой механизм расширения итерации - реализация интерфейса Iterator.
interface Iterator {
function rewind(); // переводит итерацию к первому элементу
function next(); // подготавливает к выводу следующий элемент
function key(); // возвращает ключ текущего элемента
function current(); // возвращает текущий элемент
function hasMore(); // возвращает true, если есть еще элементы, иначе false
}
Следующий пример показывает итерацию до нахождения нужного элемента:
class Control {
private $name;
function setName($value) { $this->name = $value; }
function getName() { return $this->name; }
}
class Controls implements Iterator {
private $controls;
private $controlNames;
private $num;
function __construct() {
$this->controls = array();
}
function addControl($obj) {
$this->controls[$obj->getName()] = $obj;
}
// функция использует возможности итерации класса для
// поиска контрола с заданным именем
function findControl($name) {
foreach ($this as $control) {
if ($control->getName() == $name)
return $control;
}
return null;
}
// следующие функции из Iterator
function rewind() {
$this->controlNames = array_keys($controls);
$this->num = 0;
}
function next() {
$this->num++;
}
function key() {
return $this->controlNames($this->num);
}
function current() {
return $this->controls[$this->key()];
}
function hasMore() {
return $this->num < count($this->controlNames);
}
}
$c1 = new Control();
$c1->setName('userId');
$c2 = new Control();
$c2->setName('userName');
$formControls = new Controls();
$formControls->addControl($c1);
$formControls->addControl($c2);
$userId = $formControls->findControl('userId');
$userName = $formControls->findControl('userName');