Модераторы: skyboy, MoLeX, Aliance, ksnk

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> foreach+reference: проблема при нескольких циклах, с одноименным "контейнером"-переменной 
V
    Опции темы
skyboy
Дата 25.1.2010, 17:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 75
Всего: 260



чем меньше знаешь - тем радостнее жить: что ни день - Великое открытие smile
создавая дерево на основе линейного массива столкнулся с ситуацией, когда из исходного массива "пропадал" элемент. Так как у меня все-то кода было - несколько foreach-циклов с использованием ссылок и без, а все присвоение шло внутри элементов массива, заподозрил неладное. в мане среди коментариев к foreach наткнулся на(малость добавил вывода для полноты картины):
Код

$months = array("Jan", "Feb", "March");
foreach ($months as &$month) {
    $month .= "Beep";
}
print_r($months);
foreach ($months as $month) {
    print_r($months);
}

код выводит(жирным выделен исходный массив):
Цитата

Array
(
    [0] => JanBeep
    [1] => FebBeep
    [2] => MarchBeep
)

Array
(
    [0] => JanBeep
    [1] => FebBeep
    [2] => JanBeep
)
Array
(
    [0] => JanBeep
    [1] => FebBeep
    [2] => FebBeep
)
Array
(
    [0] => JanBeep
    [1] => FebBeep
    [2] => FebBeep
)

видно, что в массиве без присвоений и явного использования ссылок происходит модификация массива.
происходит это вследствие того, что после последней итерации первого цикла, в переменной $month осталось значением ссылка на последний элемент массива $months: $month = &$months[2];
следовательно, при каждой итерации второго цикла, очередной элемент массива $months будет связан не просто с именем "$month", а с тем, на что это имя ссылается - последний элемент исходного массива. И с каждой итерацией второго цикла, последний элемент исходного массива будет изменяться и изменяться.
В основном теле статьи есть предупреждение:
Цитата

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset(). 

Так что зря я сразу в коменты полез, надо было до конца дочитать.
Зачем пишу, раз в мануале есть?
Во-первых, мне поначалу такое поведение показалось неожиданным настолько, что стал искать у себя алгоритмическую ошибку.
Во-вторых, сам в чужом коде встречал массив с использованием ссылок, но нигде после цикла не попадалось на глаза unset.
Надеюсь, кого-то просветит.

PM MAIL   Вверх
solenko
Дата 25.1.2010, 18:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1473
Регистрация: 15.1.2006
Где: Украина

Репутация: 34
Всего: 67



Кстати, этот пример рассматривается в Zend Certification Guide. Тоже долго смотрел на результат выполнения. ))


--------------------
Ла-ла-ла-ла
Заметьте, нет официального подтверждения, что это не просто четыре слога.
PM MAIL WWW ICQ Skype   Вверх
SamDark
Дата 25.1.2010, 18:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Добрый кот
***


Профиль
Группа: Участник
Сообщений: 1424
Регистрация: 25.7.2006
Где: Voronezh

Репутация: 10
Всего: 38





--------------------
rmcreative.ru — Это жжж неспроста...
yiiframework.ru — О фреймворке Yii на русском.
reggi — здесь я регистрирую домены
PM MAIL WWW GTalk Jabber MSN   Вверх
sTa1kEr
Дата 25.1.2010, 20:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


Профиль
Группа: Завсегдатай
Сообщений: 1553
Регистрация: 21.2.2007

Репутация: 56
Всего: 146



Цитата(skyboy @  25.1.2010,  18:46 Найти цитируемый пост)
Зачем пишу, раз в мануале есть?

А это бесполезно smile Сколько ни пиши в мануалах, сколько не кричи на форумах - все бес толку. Вот только когда программист сам наступит на эти грабли, сам набьет себе шишку, только тогда он полезет читать мануал,  задавать вопросы на форумах, гуглить наконец smile 

Тут я писал в свое время чем опасны эмперсанды и зачем надо делать unset в конце цикла.
А почему так происходит можно узнать прочитав статью Дерика Ретанса о ссылках в PHP.

Цитата(skyboy @  25.1.2010,  18:46 Найти цитируемый пост)
print_r($months);

Это еще одна причина почему стоит для дампа переменных использовать функцию var_dump(), а не всякие print_r'ы и var_export'ы.


Это сообщение отредактировал(а) sTa1kEr - 25.1.2010, 20:42
PM MAIL   Вверх
skyboy
Дата 25.1.2010, 22:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 75
Всего: 260



Цитата(sTa1kEr @  25.1.2010,  19:38 Найти цитируемый пост)
Это еще одна причина почему стоит для дампа переменных использовать функцию var_dump(), а не всякие print_r'ы и var_export'ы.

прости, но не мог бы ты объяснить, что именно ты имеешь в виду? то, что в выдаче print_r никак не указываются ссылки, а выводятся значения?
за ссылки спасибо, да.
статью касательно ссылок встречал уже, но тут меня удивило другое: я находил логичным обнулять счетчик цикла, а не работать как с ссылкой. не могу представить, где и как может быть задействован этот механизм. вот после цикла оставлять значение счетчика или последнего элемента полезно - ведь внутри цикла может быть вызван break и в вспомогательной переменной должен быть совсем не последний элемент, да.

Добавлено @ 22:34
Цитата(SamDark @  25.1.2010,  17:48 Найти цитируемый пост)
Я тоже сталкивался

что-то я не то читаю, раз раньше не натыкался. smile
PM MAIL   Вверх
Simpliest
Дата 25.1.2010, 22:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 625
Регистрация: 1.9.2009

Репутация: 1
Всего: 3



skyboy, рекомендую покопать еще деструкторы у классов и последовательность их вызова при завершении скрипта smile

О сколько нам открытий чудных, готовит просвещенья дух smile

Я, кстати, так и не нашел способа однозначно убить все ссылки на объект smile


--------------------
user posted image
PM   Вверх
sTa1kEr
Дата 25.1.2010, 23:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


Профиль
Группа: Завсегдатай
Сообщений: 1553
Регистрация: 21.2.2007

Репутация: 56
Всего: 146



Цитата(skyboy @  25.1.2010,  23:34 Найти цитируемый пост)
прости, но не мог бы ты объяснить, что именно ты имеешь в виду? то, что в выдаче print_r никак не указываются ссылки, а выводятся значения?

Именно. А точнее то, что var_dump выдает исчерпывающую(почти) информацию о переменной, даже если она равна NULL.
PM MAIL   Вверх
skyboy
Дата 25.1.2010, 23:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 75
Всего: 260



Цитата(Simpliest @  25.1.2010,  21:58 Найти цитируемый пост)
О сколько нам открытий чудных, готовит просвещенья дух

ну, открытий много, но не все они кажутся мне нелогичными/неожиданными smile
Цитата(Simpliest @  25.1.2010,  21:58 Найти цитируемый пост)
и последовательность их вызова при завершении скрипта

наверное, плохо я копал, раз нашел только тот факт, что унаследованные деструкторы(так же, как и конструкторы) не вызываются автоматически и должны быть вызваны явно. и что деструктор нормально вызывается уже после отправки данных пользователю. я не в ту сторону начал рыть?
PM MAIL   Вверх
Simpliest
Дата 26.1.2010, 01:08 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 625
Регистрация: 1.9.2009

Репутация: 1
Всего: 3



Цитата(skyboy @  25.1.2010,  22:41 Найти цитируемый пост)
раз нашел только тот факт, что унаследованные деструкторы(так же, как и конструкторы) не вызываются автоматически и должны быть вызваны явно. и что деструктор нормально вызывается уже после отправки данных пользователю. я не в ту сторону начал рыть? 

В ту smile но есть нюансы http://www.php.ru/forum/viewtopic.php?p=195444



--------------------
user posted image
PM   Вверх
sTa1kEr
Дата 26.1.2010, 11:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


Профиль
Группа: Завсегдатай
Сообщений: 1553
Регистрация: 21.2.2007

Репутация: 56
Всего: 146



Цитата(skyboy @  26.1.2010,  00:41 Найти цитируемый пост)
и что деструктор нормально вызывается уже после отправки данных пользователю.

Кстати, это тоже не совсем так smile Данные/заголовки могут быть отправлены и после/вовремя вызова деструкторов и/или shutdown function. Это возможно при использовании fcgi sapi и nginx'а в качестве основного web сервера.

Причина такого поведения, я думаю, заключается в самой архитектуре cgi интерфейса. А именно в том, что пока воркер веб сервера не дождется завершения php-cgi процесса, он будет ждать от него данные и при этом ему не известно, да и не важно, что происходит в php-cgi и в какой стадии находится скрипт. Однако это только мои домыслы, пруфлинка у меня нет.

Это сообщение отредактировал(а) sTa1kEr - 26.1.2010, 11:10
PM MAIL   Вверх
skyboy
Дата 26.1.2010, 12:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 75
Всего: 260



Цитата(Simpliest @  26.1.2010,  00:08 Найти цитируемый пост)
но есть нюансы http://www.php.ru/forum/viewtopic.php?p=195444

ясно. спасибо. интересно.
скопирую обсуждаемый код сюда:
Код

<?php
class a1 {
    var $b;
    function __construct($b) {
        $this->b = $b;
    }
    function __destruct() {
        echo ' a1(b1:' . $this->b->b .')';
    }
}
class a2 {
    var $b;
    function __construct($b) {
        $this->b = $b;
    }
    function __destruct() {
        echo ' a2 ';
    }
}
class b1 {
    var $b;
    function __construct($b) {
        $this->b = $b;
    }
    function __destruct() {
        echo " b1:{$this->b} ";
        $this->b++;
    }
}
$b1 = new b1(1);
$a2 = new a2($b1);
test($b1);
function test($b) {
    $a1 = new a1($b);
    exit;
}


также интересно:
0. деструктор $b1 вызывается, но данные объекта(поле $b->b) доступно и после вызова деструктора. для меня это тоже было удивительно.
1. если вместо exit/die использовать throw new Exception(), вызов деструкторов "встанет на свои места".
PM MAIL   Вверх
solenko
Дата 26.1.2010, 14:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1473
Регистрация: 15.1.2006
Где: Украина

Репутация: 34
Всего: 67



0. 
Collecting Cycles
Цитата

Secondly, in a garbage cycle, it is possible to discover which parts are garbage by checking whether it is possible to decrease their refcount by one, and then checking which of the zvals have a refcount of zero.

Насколько я понимаю, именно это и происходит. Хотя, возможно, в режиме "завершения" сборщик мусора просто игнорирует refcount и вызывает деструкторы для ненулевых. 
Поведение, естественно, некорректное, но и exit/die некорректный способ завершения скрипта. Вобщем, "будь осторожен, следи за собой" и грабель станет меньше )

1. Тут то как раз все понятно. Если вы используете throw (или просто return), то функция завершается корректно и удаляется локальная переменная $a1 а не все скопом.


--------------------
Ла-ла-ла-ла
Заметьте, нет официального подтверждения, что это не просто четыре слога.
PM MAIL WWW ICQ Skype   Вверх
sTa1kEr
Дата 26.1.2010, 16:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


Профиль
Группа: Завсегдатай
Сообщений: 1553
Регистрация: 21.2.2007

Репутация: 56
Всего: 146



А в чем собственно проблема с деструкторами? Порядок их вызова после завершения работы скрипта? Да и черт с ним, в PHP он не важен.
PM MAIL   Вверх
skyboy
Дата 26.1.2010, 16:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 75
Всего: 260



sTa1kEr, ну, не сказал бы. По ссылке, приведенной Simpliest, обсуждается ситуация, когда в деструкторе идет работа с БД через mysqli. Если деструктор mysqli объекта будет вызван раньше, чем деструктор остальных, то подключение к базе окажется закрытым. Если я правильно понял.
PM MAIL   Вверх
sTa1kEr
Дата 26.1.2010, 17:02 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


Профиль
Группа: Завсегдатай
Сообщений: 1553
Регистрация: 21.2.2007

Репутация: 56
Всего: 146



Shutdown function полностью решает эту проблему. Да и зачем вообще работать с базой в деструкторе, можно реальный пример, а не высосанный из пальца?

Добавлено через 6 минут и 41 секунду
Если использовать деструкторы по назначению, а именно для очистки/уничтожения за собой мусора и не закрытых хендлеров, то никаких проблем не будет.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "PHP"
Aliance
IZ@TOP
skyboy
SamDark
MoLeX

Новичкам:

  • PHP редакторы собираются и обсуждаются здесь
  • Электронные книги по PHP, документацию можно найти здесь
  • Интерпретатор PHP, полную документацию можно скачать на PHP.NET

Важно:

  • Не брезгуйте пользоваться тегами [code=php]КОД[/code] для повышения читабельности текста/кода.
  • Перед созданием новой темы воспользуйтесь поиском и загляните в FAQ
  • Действия модераторов можно обсудить здесь

Внимание:

  • Темы "ищу скрипт", "подскажите скрипт" и т.п. будут переноситься в форум "Web-технологии"
  • Темы с именами: "Срочно", "помогите", "не знаю как делать" будут УДАЛЯТЬСЯ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, IZ@TOP, skyboy, SamDark, MoLeX, awers.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0602 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.