Comments 39
Это лучше в доку по php запостить чем сюда, если, конечно, это кусок еще не перевели на русский.
+4
Я бы хотел добавить пару комментариев к вашему коду:
Класс Iteratable:
Стоить заменить этот фрагмент на
Это обеспечит единый интерфейс для сброса итератора. Да и вообще, код избыточен (так как задано свойство protected $_position = 0;).
Класс ArrayAccessable
Мне больше нравится следующий подход:
Это позволяет использовать более красивое создание объекта:
Я для себя эту проблему решил созданием методов, которые являются обертками над функциями для работы с массивами. Например:
Класс Iteratable:
public function __construct()
{
$this->_position = 0;
}
public function rewind()
{
$this->_position = 0;
}
Стоить заменить этот фрагмент на
public function __construct()
{
$this->rewind();
}
public function rewind()
{
$this->_position = 0;
}
Это обеспечит единый интерфейс для сброса итератора. Да и вообще, код избыточен (так как задано свойство protected $_position = 0;).
Класс ArrayAccessable
public function __construct($array = null)
{
if (!is_null($array)) {
$this->_container = $array;
}
}
Мне больше нравится следующий подход:
protected container;
public function __construct()
{
$argv = func_get_args();
$argc = func_num_args();
if (argc == 0)
{
$this->container = array();
return;
}
if ( $argc == 1
AND is_array(argv[0]))
{
$this->container = argv[0];
}
else
{
$this->container = argv;
}
}
Это позволяет использовать более красивое создание объекта:
// создадим контейнер из нескольких элементов
$arrayObject = new ArrayAccessable('a', 'b', 'c', 'd', 'e' => 'assoc');
// создадим контейнер из массива
$array = array('a', 'b', 'c', 'd', 'e' => 'assoc');
$arrayObject = new ArrayAccessable($array)
Единственный недостаток заключается в том, что функции типа array_pop() не будут с ним работать.
Я для себя эту проблему решил созданием методов, которые являются обертками над функциями для работы с массивами. Например:
/**
* Метод возвращает контейнер со списком всех ключей объекта
*
* @return Map Контейнер со списком ключей объекта
*/
public function get_keys()
{
return new self(array_keys($this->container));
}
+3
>>Я для себя эту проблему решил созданием методов, которые являются обертками над функциями для работы с массивами. Например:
я стремился к более нативной реализации.
я стремился к более нативной реализации.
+1
Ваш пример нельзя сократить с
$tmp = $array();
array_pop($tmp);
$array($tmp);
до $last = array_pop($array());
это вызовет ошибку уровня E_STRICT. Данный подход, разумеется, имеет место, но методы-обертки позволяют использовать более короткий код $last = $array->pop();
и цепочки методов$set_fields = $query_params->get_keys->implode(' = ?, ') . ' =? ';
. Кроме того, вы вольны сами выбирать интерфейс объекта, что позволяет избавиться от legacy-кода SPL (который очень хорошо заметен в функциях работы со строками), и избавляетесь от одного из параметров вызова, который необходимо передавать в функцию.0
Увы, но ассоциативные массивы так создать не получится. Я про код:
0
а вы уверенны, что
сработает?
мне на
$arrayObject = new ArrayAccessable('a', 'b', 'c', 'd', 'e' => 'assoc');
сработает?
мне на
var_dump('a' => 'b');
выдаетPHP Parse error: syntax error, unexpected T_DOUBLE_ARROW in Command line code on line 1
Parse error: syntax error, unexpected T_DOUBLE_ARROW in Command line code on line 1
0
Да, не сработает. Это я скопировал пример массива из статьи. С одномерными массивами работает. Ждем сокращенного синтаксиса для объявления массивов в PHP 5.4
0
в статье, кстати, new SemiArray(array('a', 'b', 'c', 'd', 'e' => 'assoc'));
многомерный массив сработает на ура.
многомерный массив сработает на ура.
0
пардон, $array = new ArrayAccessable(array('a', 'b', 'c', 'd', 'e' => 'assoc'));
0
Естественно, так как параметр конструктора напрямую передается в свойство класса. В моем примере для одномерных массивов можно использовать синтаксис
$array = new ArrayAccessable('a', 'b', 'c', 'd', 'e');
для многомерных $array = new ArrayAccessable(array('a', 'b', 'c', 'd', 'e' => 'assoc'));
0
Кажется 100% обкатывалась эта тема. Много воды… лучше бы привели просто примеры использования.
-3
оно то как бы массив, но как только чуть глубже начать с этим работать начинается ад
$a = new ArrayAccessable;
$a['x'] = array('foo' => 'bar');
$a['x']['foo'] = 1;
это только то что сверху
очень полезного применения я этим интерфейсам не нашел, для чего то простого не плохо, что-то посерьезней — костыль на костыле, костылем погоняет
т.е. эти интерфейсы мало того что хуже по функциональности стандартных структур данных, так они даже не нормальное ООП
$a = new ArrayAccessable;
$a['x'] = array('foo' => 'bar');
$a['x']['foo'] = 1;
это только то что сверху
очень полезного применения я этим интерфейсам не нашел, для чего то простого не плохо, что-то посерьезней — костыль на костыле, костылем погоняет
т.е. эти интерфейсы мало того что хуже по функциональности стандартных структур данных, так они даже не нормальное ООП
0
Если переопределить offsetGet и current не как в статье, то все будет ок. habrahabr.ru/blogs/personal/48697/
0
ArrayObject — как вам мой вариант? :)
+2
Outro-то я и не заметил :) Но образовательные цели тоже сомнительны, в php не так много стандартных интерфейсов чтобы посвящать им целые статьи.
-2
Afaik, он значительно медлительнее, чем собственная реализация на php
+1
Не, на самом деле это не так, «своя реализация» чего-бы то ни было на php — всегда медленнее чем встроенный вариант на уровне си, и последний раз когда я проверял — ArrayObject не был исключением, хотя это тоже может варьироваться от версии к версии, может и были с ним когда-то какие-нибудь проблемы.
0
Из того, что мне лично не нравится в это классе:
0. В NetBeans в отладчике не видно, что содержится в контейнере.
1. Слишком мало встроенных методов, при добавлении новых начинают возникать некоторые проблемы, приводящие к костылям:
2. Разработчики PHP не отходят от старых традиций. Метод, который стоило бы назвать push(), как в других языках, назвали append().
0. В NetBeans в отладчике не видно, что содержится в контейнере.
1. Слишком мало встроенных методов, при добавлении новых начинают возникать некоторые проблемы, приводящие к костылям:
/**
* Метод возвращает первый элемент контейнера
*
* @return mixed|null Первый элемент контейнера. Если контейнер пуст - null
*/
public function get_first()
{
foreach($this as $value)
{
return $value;
}
}
/**
* Метод возвращает последний элемент контейнера
*
* @return mixed|null Последний элемент контейнера. Если контейнер пуст - null
*/
public function get_last()
{
if ($this->count() == 0)
{
return;
}
$iterator = $this->getIterator();
// перейдем к последнему элементу
$iterator->seek($this->count() - 1);
return $iterator->current();
}
Замечу, что использование метода getArrayCopy() приводит к тормозам, в зависимости от размера контейнера.2. Разработчики PHP не отходят от старых традиций. Метод, который стоило бы назвать push(), как в других языках, назвали append().
0
Уже достаточно долго пользуемся данной мулькой. В целом в некоторых моментах она очень полезна и удобна.
Очень сильно упрощает код во View скриптах:
— легко позволяет подкладывать фикстурные массивы вместо объекта, в тестах и отладке просто незаменим.
— удобная работа с кешами. В контроллере берем из кеша массив данных и отдаем вьюхе, если в кеше все устарело — берем объект, кешируем его в виде массива и отдаем вьюхе. Девелопер нафиг вырубает кеш и спокойно работает, на продакшене врубаем кеш и девелопер вообще не заморачивается.
В контроллерах и моделях редко этим делом пользуемся.
— так как offsetGet не позволяет нормально передавать ссылку на данные, а тупо копирует и возвращает данные (из-за этого возникают проблемы с $a['abc']['var'] = 1; Т.е. берутся данные по ключу 'abc', возвращается копия массива, а затем к этой копии применяется установка значения 'var' = 1. Можно лечит возвратом не массива как это в примере этой статьи, а объекта подложки, который внутри себя хранит ссылку на единый источник данных. Т.е. все вложенные массивы, необходимо подменить своим классом. Вобщем страшный костыль.)
Так что в целом MUST USE в задачах где объекты нужны в режиме READ ONLY.
Очень сильно упрощает код во View скриптах:
— легко позволяет подкладывать фикстурные массивы вместо объекта, в тестах и отладке просто незаменим.
— удобная работа с кешами. В контроллере берем из кеша массив данных и отдаем вьюхе, если в кеше все устарело — берем объект, кешируем его в виде массива и отдаем вьюхе. Девелопер нафиг вырубает кеш и спокойно работает, на продакшене врубаем кеш и девелопер вообще не заморачивается.
В контроллерах и моделях редко этим делом пользуемся.
— так как offsetGet не позволяет нормально передавать ссылку на данные, а тупо копирует и возвращает данные (из-за этого возникают проблемы с $a['abc']['var'] = 1; Т.е. берутся данные по ключу 'abc', возвращается копия массива, а затем к этой копии применяется установка значения 'var' = 1. Можно лечит возвратом не массива как это в примере этой статьи, а объекта подложки, который внутри себя хранит ссылку на единый источник данных. Т.е. все вложенные массивы, необходимо подменить своим классом. Вобщем страшный костыль.)
Так что в целом MUST USE в задачах где объекты нужны в режиме READ ONLY.
0
ArrayObject не содержит Итераторов, замените в конце ссылку на ua2.php.net/manual/en/class.arrayiterator.php
0
Вы, кстати, почитайте другие статьи по теме, и немножечко поменяйте код, ато и недо массив и пере класс получается (http://habrahabr.ru/blogs/personal/48697/) =)
И еще одно замечание =) Фореч забирает при переборе эеземпляр отданого ему обьекта/массива, чтоб чтоб обьект ну уж точно вел себя как массив нужно еще переопределить метод getIterator (да, его нет в интерфейсах), тогда он будет поддерживать и вложенный проход по одному и тому же обьекту.
И еще одно замечание =) Фореч забирает при переборе эеземпляр отданого ему обьекта/массива, чтоб чтоб обьект ну уж точно вел себя как массив нужно еще переопределить метод getIterator (да, его нет в интерфейсах), тогда он будет поддерживать и вложенный проход по одному и тому же обьекту.
0
getIterator это из другого интерфейса просто — IteratorAggregate, который то же наследуется от Traversable
0
Traversable — пустой интерфейс (маркер), он лишь говорит о том что по обьекту можно пройтись, а каким способом уже зависит от примененного интерфейса. Если Вы зайдете в статью что я привел, там в каментах человек жалуется что вложенные циклы не работают с обьектом так как с массивом.
0
Я к тому, что кроме Iterator есть IteratorAggregate.
Если вы про комментарий «Не поддерживается несколько обходов одновременно», то это очевидно. Для такой поддержки можно использовать IteratorAggregate и каждый раз возвращать новый объект итератора. Тот же ArrayObject замечательно справляется с поставленной задачей (он как раз используется getIterator)
Если вы про комментарий «Не поддерживается несколько обходов одновременно», то это очевидно. Для такой поддержки можно использовать IteratorAggregate и каждый раз возвращать новый объект итератора. Тот же ArrayObject замечательно справляется с поставленной задачей (он как раз используется getIterator)
0
Странная реализация методов rewind() и next(). Как Вы полагаете заставит их работать на ассоциированных массивах? У меня не получилось:
$array = new SemiArray(array('a'=>'a', 'b'=>'b', 'c'=>'c', 'd'=>'d', 'e'=>'assoc'));
$array->rewind();
echo $array->current(); // Notice: Undefined offset: 0
// В массиве же нет элемента с индексом 0
$array = new SemiArray(array('a'=>'a', 'b'=>'b', 'c'=>'c', 'd'=>'d', 'e'=>'assoc'));
$array->rewind();
echo $array->current(); // Notice: Undefined offset: 0
// В массиве же нет элемента с индексом 0
0
Наверное, по этой причине встроенный ArrayObject не содержит итераторов. Но не смотря на отсутствие итераторов, его можно прогнать через foreach:
$array = new ArrayObject(array('a'=>'a', 'b'=>'b', 'c'=>'c', 'd'=>'d', 'e'=>'assoc'));
foreach ($array as $k=>$v) { // Отработает правильно
echo $k. '='. $v .'';
}
$array->current(); // Fatal error: Call to undefined method ArrayObject::current()
Либо это магия, либо костыль на костыле.
$array = new ArrayObject(array('a'=>'a', 'b'=>'b', 'c'=>'c', 'd'=>'d', 'e'=>'assoc'));
foreach ($array as $k=>$v) { // Отработает правильно
echo $k. '='. $v .'';
}
$array->current(); // Fatal error: Call to undefined method ArrayObject::current()
Либо это магия, либо костыль на костыле.
0
Как не содержит?
ArrayObject implements IteratorAggregate,...
0
Я имел ввиду, что не содержит описанного в статье итератора Iterator и нет методов rewind(), current() и next(). И показанные методы не позволяют работать с ассоциированными массивами (а это даже не отмечено в статье).
Вместо этого, получается некоторая магия когда я использую foreach. Например, если я использую описанные здесь класс semiArray добавив вот это:
public function next()
{
echo 'next()'; // Это
++$this->_position;
}
Я вижу, что конструкция foreach для каждой итерации вызывает метод next(). Однако, встроенный ArrayObject не имеет метода next(), но foreach как-то работает! Получается, что foreach подстраивается под переданный ему тип вместо того, что бы использовать полиморфизм. Имеено такое поведение foreach мне кажется костылём.
Вместо этого, получается некоторая магия когда я использую foreach. Например, если я использую описанные здесь класс semiArray добавив вот это:
public function next()
{
echo 'next()'; // Это
++$this->_position;
}
Я вижу, что конструкция foreach для каждой итерации вызывает метод next(). Однако, встроенный ArrayObject не имеет метода next(), но foreach как-то работает! Получается, что foreach подстраивается под переданный ему тип вместо того, что бы использовать полиморфизм. Имеено такое поведение foreach мне кажется костылём.
0
Встроенный ArrayObject использует IteratorAggregate вместо Iterator, поэтому там нет всяких next.
foreach умеет работать с 2мя типами: массив и объект реализующий Traversable (наследниками от которого являются Iterator & IteratorAggregate)
foreach умеет работать с 2мя типами: массив и объект реализующий Traversable (наследниками от которого являются Iterator & IteratorAggregate)
0
/*
foreach умеет работать с 2мя типами: массив и объект реализующий Traversable (наследниками от которого являются Iterator & IteratorAggregate)
*/
Прочитав этом, можно сделать вывод, что Traversable заставляет оба класса реализовывать одни и те же методы (предоставить api), которое потом используются в foreach. И это было бы логично.
Но это не так. Traversable — интерфейс не содержащий ни одного метода. Это что же за интерфейс такой? А foreach использует разные методы для объектов $semiArray и $arrayObject. Т.е. нет никакого полиморфизма. Разве это не костыль?
P.S. Я бы написал так:
foreach умеет работать с 3мя типами: 1) массив, 2) объект реализующий Iterator и 3) объект реализующий IteratorAggregate.
foreach умеет работать с 2мя типами: массив и объект реализующий Traversable (наследниками от которого являются Iterator & IteratorAggregate)
*/
Прочитав этом, можно сделать вывод, что Traversable заставляет оба класса реализовывать одни и те же методы (предоставить api), которое потом используются в foreach. И это было бы логично.
Но это не так. Traversable — интерфейс не содержащий ни одного метода. Это что же за интерфейс такой? А foreach использует разные методы для объектов $semiArray и $arrayObject. Т.е. нет никакого полиморфизма. Разве это не костыль?
P.S. Я бы написал так:
foreach умеет работать с 3мя типами: 1) массив, 2) объект реализующий Iterator и 3) объект реализующий IteratorAggregate.
0
Если корректно сделать обёртку, то будет работать и ассоциативный массив.
+1
Это правильно. Но в «обучающей» статье почему-то этого нет. Ассоциированные массивы не работают и об этом в статье умалчивается, хотя в разделе «тестируем» есть ассоциированный массив:
$array = new SemiArray(array('a', 'b', 'c', 'd', 'e' => 'assoc'));
Не внимательный читатель может сделать вывод, будто описанный в статье класс может работать с ассоциированными массивами.
$array = new SemiArray(array('a', 'b', 'c', 'd', 'e' => 'assoc'));
Не внимательный читатель может сделать вывод, будто описанный в статье класс может работать с ассоциированными массивами.
0
Для сравнения примерно такая-же функциональность в Python:
Итератор
В простейшем случае даже
Доступ по индексу:
Доступ по ключу
длина
Итератор
def Iterator():
for elem in ['item1', 'item2', 'item3']:
yield elem
В простейшем случае даже
iterator=(elem for elem in ['item1', 'item2', 'item3'])
Доступ по индексу:
class ArrayAccess(object):
def __getitem__(self, key):
return self.container[key]
Доступ по ключу
class ArrayAccess(object):
def __getitem__(self, key):
return self.container[key]
def __setitem__(self, key, value):
self.container[key]=value
def __delitem__(self, key, value):
del self.container[key]
длина
class Countable(object):
def __len__(self):
return len(self.container)
0
UFO just landed and posted this here
Я тоже привык использовать isset и empty. Надо просто не забывать, что в PHP нет строгой типизации. А в большинстве случаев функциональности этих двух конструкций вполне хватает.
Еще полезная функция array_filter без коллбэка, лишний цикл иногда можно не писать :)
Еще полезная функция array_filter без коллбэка, лишний цикл иногда можно не писать :)
0
И еще немного про isset и unset.
Можно писать так isset($array[1]) && isset($array[1][2]) => isset($array[1], $array[1][2]) => isset($array[1][2])
и аналогично unset($array[1]); unset($array[2]); => unset($array[1], $array[2]);
Можно писать так isset($array[1]) && isset($array[1][2]) => isset($array[1], $array[1][2]) => isset($array[1][2])
и аналогично unset($array[1]); unset($array[2]); => unset($array[1], $array[2]);
0
Sign up to leave a comment.
Iterator, ArrayAccess, Countable: Объект как массив