Достаточно интересная и не самая редкая задача. Допустим, есть несколько массивов с разными значениями, например: массив городов, возрастов, полов и т.п. Нужно вызвать некую функцию передав ей в качестве аргументов все возможные комбинации значений городов + возрастов + полов. Фактически это задача циклической итерации всех значений всех массивов. Неискушённый программист наверняка бы решил эту задачу с помощью вложенных циклов:
foreach($city as $_city) { foreach($age as $_age) { foreach($gender as $_gender) { $response = request($_city, $_age, $_gender); } } }
Неплохо. Однако, такое решение не гибкое и максимально грустное. При увеличении количества перебираемых массивов код будет становиться всё ужаснее, а также будет усложняться его поддержка и отладка. Привожу, возможно не самое красивое и не самое гибкое решение, что называется в лоб. Присылайте в комменты свои варианты либо ссылки на более хорошие, интересные и универсальные решения.
$city = [1, 2, 10, 37, 42, 49, 60, 61, 72, 73, 95, 99, 104, 110, 119, 123, 143, 151, 158]; $age = range(14, 60); $gender = [1, 2];
Для начала объявляем исходные значения. Ну чтобы было.
do { $_city = current($city); $_age = current($age); $_gender = current($gender); $response = request($_city, $_age, $_gender); step($city, $age, $gender); while(true);
Таким образом берём текущие значения массива и передаём их как аргументы в функции. Затем вызываем функцию step, которая хитрым образом передвигает внутренние указатели массивов.
function step(array &$city, array &$age, array &$gender) { if(cycle($age) ) { return; } if(cycle($gender) ) { return; } cycle($city, false); }
В моём случае функция step является лишь фасадом функции cycle, которой передаются массивы с данными.
function cycle(array &$arr, $is_cycle = true) { if(empty($arr) || false === key($arr)) { return; } next($arr); if(key($arr) ) { return true; } if($is_cycle) { reset($arr); } else { throw new Exception('Finish!'); } }
Пожалуй, самая интересная часть скрыта в функции cycle. Функция next перемещает внутренний указатель массива на следующий элемент, затем функция key возвращает текущий индекс элемента, либо false в случае, если внутренний указатель вышел за границы массива. Если выход за границы массива произошёл, то указатель перемещается на первый элемент с помощью функции reset.
Если функция cycle возвращает true, то указатели других массивов трогать не нужно, в противном случае нужно так же переместить указатель следующего массива. Аргумент $is_cycle нужен для обозначения последнего массива, после полной итерации которого работу нужно завершить. В данном случае с помощью выброса исключения.
Можно инкапсулировать это решение и оформить его в качестве класса-итератора. Но не в этот раз =)