php обход массива foreach
Учебник по PHP 4
Цикл foreach для обхода массивовОбход массива в цикле в PHP4 можно организовать при помощи цикла foreach, который имеет следующий синтаксис: Смысл этого цикла прост: при проходе каждого элемента массива в переменную $key помещается индекс этого элемента, а в переменную $value – его значение. Имена этих двух переменных совершенно произвольны. Как видно из синтаксиса, переменная $key необязательна и может быть опущена:
Если Вам нужна частная профессиональная консультация от авторов многих книг Кузнецова М.В. и Симдянова И.В., добро пожаловать в наш Консультационный Центр SoftTime. foreachКонструкция foreach предоставляет простой способ перебора массивов. Foreach работает только с массивами и объектами, и будет генерировать ошибку при попытке использования с переменными других типов или неинициализированными переменными. Существует два вида синтаксиса: Первый цикл перебирает массив, задаваемый с помощью array_expression. На каждой итерации значение текущего элемента присваивается переменной $value и внутренний указатель массива увеличивается на единицу (таким образом, на следующей итерации цикла работа будет происходить со следующим элементом). Второй цикл будет дополнительно соотносить ключ текущего элемента с переменной $key на каждой итерации. Когда оператор foreach начинает исполнение, внутренний указатель массива автоматически устанавливается на первый его элемент Это означает, что нет необходимости вызывать функцию reset() перед использованием цикла foreach. Так как оператор foreach опирается на внутренний указатель массива, его изменение внутри цикла может привести к непредсказуемому поведению. Для того, чтобы напрямую изменять элементы массива внутри цикла, переменной $value должен предшествовать знак &. В этом случае значение будет присвоено по ссылке. Указатель на $value возможен, только если на перебираемый массив можно ссылаться (т.е. если он является переменной). Следующий код не будет работать: Оператор foreach не поддерживает возможность подавления сообщений об ошибках с помощью префикса ‘@’. Вы могли заметить, что следующие конструкции функционально идентичны: Следующие конструкции также функционально идентичны: Вот еще несколько примеров, демонстрирующие использование оператора: /* Пример 1: только значение */ /* Пример 2: значение (для иллюстрации массив выводится в виде значения с ключом) */ $i = 0 ; /* только для пояснения */ /* Пример 3: ключ и значение */ /* Пример 4: многомерные массивы */ /* Пример 5: динамические массивы */ Распаковка вложенных массивов с помощью list()В PHP 5.5 была добавлена возможность обхода массива массивов с распаковкой вложенного массива в переменные цикла, передав list() в качестве значения. Результат выполнения данного примера: Результат выполнения данного примера: О тонкостях работы foreach в PHPВ недавнем дайджесте интересных ссылок о PHP я обнаружил ссылку на комментарий Никиты Попова на StackOverflow, где он подробно рассказывает о механизме «под капотом» управляющей конструкции foreach. Внимание: этот текст подразумевает наличие базовых знаний о функциональности zval’ов в PHP, в частности вы должны знать что такое refcount и is_ref. Прежде чем приступить, пара слов о массивах и их обходе, важная для понимания контекста. Как работает обход массивовМассивы в PHP являются упорядоченными хеш-таблицами (элементы хеша объединены в двусвязный список) и foreach обходит массив, следуя указанному порядку. Таким образом, внешние указатели массива могут быть использованы только когда вы полностью уверены, что при обходе никакого пользовательского кода выполняться не будет. А такой код может оказаться в самом неожиданном месте, типа обработчика ошибок или деструктора. Вот почему в большинстве случаев PHP приходится использовать внутренний указатель вместо внешнего. Если бы это было иначе, PHP мог бы упасть из-за segmentation fault, как только пользователь начнет делать что-нибудь необычное. Проблема внутреннего указателя в том, что он является частью HashTable. Так что, когда вы изменяете его, HashTable меняется вместе с ним. И коль скоро обращение к массивам в PHP делается по значению (а не по ссылке), вы вынуждены копировать массив, чтобы в цикле обходить его элементы. Простой пример, показывающий важность копирования (кстати, не такая большая редкость), это вложенная итерация: Здесь вы хотите чтобы оба цикла были независимым, а не хитро перебрасывались одним указателем. Итак, мы дошли до foreach. Обход массива в foreachТеперь вы знаете, для чего foreach приходится создавать копию массива, прежде чем обойти его. Но это явно не вся история. Сделает PHP копию или нет, зависит от нескольких факторов: Итак, это первая часть тайны: функция копирования. Вторая часть это то, как текущая итерация выполняется, и она тоже довольно странная. «Обычный» образец итерации, который вы уже знаете (и который часто используется в PHP — отдельно от foreach) выглядит примерно так (псевдокод): итерация foreach выглядит немного иначе: Такой режим работы foreach также является причиной, по которой внутренний указатель массива переходит к следующему элементу, если текущий удалён, а не к предыдущему (как вы могли бы ожидать). Всё сделано так, чтобы отлично работать с foreach (но, очевидно, со всем остальным будет работать не так хорошо, пропуская элементы). Последствия для кодаПервое следствие вышеописанного поведения в том, что foreach копирует итерируемый массив в многих случаях (медленно). Но отриньте страх: я пробовал удалить требование копирования и не смог увидеть ускорения работы нигде, кроме искусственных бенчмарков (в которых итерация происходила в два раза быстрее). Похоже, люди просто не итерируют достаточно много. Второе следствие в том, что обычно не должно быть других следствий. Поведение foreach, в основном, вполне понятно пользователю и просто работает как следует. Вас не должно волновать, как происходит копирование (и происходит ли оно вообще), и в какой конкретно момент времени перемещается указатель. И третье следствие — и тут мы как раз подходим к вашим проблемам — в том, что иногда мы видим очень странное поведение, которое трудно понять. Это происходит конкретно тогда, когда вы пытаетесь модифицировать сам массив, который вы обходите в цикле. Большую коллекцию поведения в пограничных случаях, которые появляются, когда вы модифицируете массив в ходе итерации, можно найти в тестах PHP. Вы можете начать с этого теста, после чего изменять 012 на 013 в адресе, и так далее. Вы увидите, как поведение foreach будет проявляться в разных ситуациях (всякие комбинации ссылок и.т.д.). А сейчас вернёмся к вашим примерам: Та же ситуация, что и в первом тесте. Но эти примеры недостаточно убедительны. Поведение начинает быть по настоящему непредсказуемым, когда вы используете current в цикле: Теперь попробуем сделать небольшое изменение: Здесь у нас is_ref=1, так что массив не копирован (так как и выше). Но сейчас когда есть is_ref, массив больше не нужно разделять, передавая по ссылке к current. Теперь current и foreach работают с одним массивом. Вы видите массив сдвинутым на единицу как раз из-за того, как foreach обращается с указателем. То же самое вы увидите, когда будете делать обход массива по ссылкам: Еще одна небольшая вариация, здесь мы присвоим наш массив еще одной переменной: Итерация объектовПри итерации объектов имеет смысл рассмотреть два случая: Объект не Traversable (вернее, не определен внутренний обработчик get_iterator)В этом случае итерация происходит почти так же, как у массивов. Та же семантика копирования. Единственное отличие: foreach запустит некий дополнительный код, чтобы пропустить свойства, недоступные в текущей области видимости. Еще пара интересных фактов: Объект TraversableВ этом случае всё, что сказано выше, не будет применяться никоим образом. Также PHP не будет копировать и не будет применять никакие трюки вроде увеличения указателя до прохода цикла. Я думаю что режим прохода по обходимому (Traversable) объекту куда более предсказуем и не требует дальнейшего описания. Замена итерируемого объекта во время циклаДругой необычный случай, который я не упомянул — PHP допускает возможность замены итерируемого объекта во время цикла. Вы можете начать с одним массивом и продолжить, заменив его на полдороге другим. Или начать с массивом, в затем заменить его объектом: Как видите, PHP просто начал обходить другую сущность, как только произошла замена. Изменение внутреннего указателя массива во время итерацииПоследняя деталь поведения foreach, которую я не упомянул (потому что может быть использована для получения по настоящему странного поведения): что может случиться если попытаться изменить внутренний указатель массива во время прохода цикла. Тут вы можете получить не то, что ожидали: если вызывать next или prev в теле цикла (в случае передачи по ссылке), вы увидите, что внутренний указатель переместился, но это никак не повлияло на поведение итератора. Причина в том, что foreach делает бекап текущей позиции и хеша текущего элемента в HashPointer после каждого прохода цикла. На следующей проходе foreach проверит, не менялась ли позиция внутреннего указателя и попытается восстановить ее, используя этот хеш. Давайте посмотрим что означает «попытается». Первый пример показывает, как изменение внутреннего указателя не меняет режим foreach: Теперь давайте попробуем сделать unset элементу, к которому обратится foreach при первом проходе (ключ 1): Тут вы увидите, что счетчик сброшен, так как не удалось найти элемент с подходящим хешом. Имейте в виду, хеш — всего лишь хеш. Случаются коллизии. Попробуем теперь так: Работает так, как мы и ожидали. Мы удалили ключ EzFY (тот, где как раз был foreach), так что был сделан сброс. Также мы добавили дополнительный ключ, поэтому в конце мы видим 4. И вот тут приходит неведомое. Что произойдёт, если заменить ключ FYFY с FYFZ? Давайте попробуем: Сейчас цикл перешёл непосредственно к новому элементу, пропуская всё остальное. Это потому что ключ FYFY имеет коллизию с EzFY (вообще-то, все ключи из этого массива тоже). Более этого, элемент FYFY находится по тому же адресу в памяти, что и элемент EzFY который только что был удален. Так что для PHP это будет та же самая позиция с тем же хешом. Позиция «восстановлена» и происходит переход к концу массива. Php обход массива foreachYou can also use the alternative syntax for the foreach cycle: I cannot stress this point of the documentation enough! Here is a simple example of exactly why this must be done: Even though it is not mentioned in this article, you can use «break» control structure to exit from the «foreach» loop. WARNING: Looping through «values by reference» for «extra performance» is an old myth. It’s actually WORSE! ?> Which do you think is faster? Lots of people think the answer is two() because it uses «reference to value, which it doesn’t have to copy each value when it loops». Well, that’s totally wrong! Here’s what actually happens: Alright, so what’s the second version doing? The beloved «iterate values by reference»? — This function takes an array as argument ($arr). Therefore: STOP using the old, mythological «&$val» iteration method! It’s almost always BAD! With worse performance, and risks of bugs and quirks as is demonstrated in the manual. You can always manually write array assignments explicitly, without references, like this: Используем foreach для прохода по всему массиву PHPЧасто нужно пройти по всем элементам массива PHP и провести какую-нибудь операцию над каждым элементом. Например, вы можете вывести каждое значение в таблицу HTML или задать каждому элементу новое значение. В данном уроке мы рассмотрим конструкцию foreach при организации цикла по индексированным и ассоциированным массивам. Цикл по значениям элементовНапример, следующий скрипт проходит по списку режисеров в индексированном массиве и выводит имя каждого: Выше приведенный код выведет: Цикл по ключам и значениямА что насчет ассоциированных массивов? При использовании такого типа массивов часто нужно иметь доступ к ключу каждого элемента, так же как и к его значению. Конструкция foreach имеет способ решить поставленную задачу: Пример организации цикла по ассоциированному массиву с информацией о кинофильмах, выводит ключ каждого элемента и его значение в HTML списке определений: Данный скрипт при выполнении выведет: Изменение значения элементаА как обстоит дело с изменением значения элемента при проходе цикла? Вы можете попробовать такой код: Однако, если запустить его на выполнение, то вы обнаружите, что значения в массиве не изменяются. Причина заключается в том, что foreach работает с копией значений массива, а не с оригиналом. Таким образом оригинальный массив остается нетронутым. Для изменения значений массива вам нужна ссылка на значение. Для этого нужно поставить знак & перед переменной значения в конструкции foreach : — это указатель на оригинальное значение. Она похожа на ярлык в Windows, или на псевдоним в Mac OS. В данном уроке мы рассмотрели, как использовать конструкцию PHP foreach для организации цикла по элементам массива. Были рассмотрены вопросы: Данный урок подготовлен для вас командой сайта ruseller.com 5 последних уроков рубрики «PHP»Фильтрация данных с помощью zend-filterКогда речь идёт о безопасности веб-сайта, то фраза «фильтруйте всё, экранируйте всё» всегда будет актуальна. Сегодня поговорим о фильтрации данных. Контекстное экранирование с помощью zend-escaperОбеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода. Подключение Zend модулей к ExpressiveExpressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей. Совет: отправка информации в Google Analytics через APIПредположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке. Подборка PHP песочницПодборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.
|