Как стать автором
Обновить

Комментарии 116

«Кто-либо знает причину этого явления?»

Ну кто-то в интерпретаторе где-то накосячил=)

Ваш К.О.
Хабрахабр — такой смешной=)

Коммент плюсуют, а карму минисуют=))
НЛО прилетело и опубликовало эту надпись здесь
Неее=) Писал было +10 стало +17=))

Кстати а можно попросить всех заминусовать этот коммент?) (В карму тоже)

Интересно скока получится?)

Я даже могу сказать что PHP это устарелое гамно с дофига фреймворков и библиотек пытающихся это скрыть=))

НЛО прилетело и опубликовало эту надпись здесь
ПХП — не гомно…. Просто руки у некоторых, не там выросли…
Не мотая вниз, прозреваю толпу тупых руби-долбоёбов в комментах, которые там самокомпенсируются.
Да не, зачем. Там просто история разбора бага и запатчивания.
Ну слава Богу, а то бы опять всё настроение испортили.
Бог здесь не при чём. Я пять часов подряд читал исходники, а не молитвы.
Я атеист, это просто присказка такая.
Ну, что вы! Там толпа супер-мега-пехапешек, которые самокомпенсируются криками — топикстартер дурак, тк если такой код не писать, то и бага не будет. :)
И один реальный энтузиаст — давинчи.
От обсирания пыха, у тебя либидо растёт? Или ты лучше кодишь? Или тебе больше платят?
Какие вы предсказуемые — это что-то…
Уймись, хамло.
Если функцию описать заранее, баг не проявляется — смею предположить, это как-то связано с механизмом объявления еще не описанных функий?
Это не совсем так.
Пожалуйста смотрите мой коммент ниже: habrahabr.ru/blogs/php/95595/#comment_2919653

Да, в этом случае если функцию описать заранее, баг не проявляется. Но если эту функцию включить в класс, и описать его заранее, то данный баг появится снова во всей своей красе.
Боюсь без исходных кодов тут можно только гадать.
Я бы предположил что там какая-нибудь очередь инициализации переменных сдвигается из-за того что он пытается определить неизвестную переменную, для которой нет значения.
Неприятно… Только вот разве причины бага могут быть интересны? :)

Провел небольшие эксперименты. Судя по всему все объявленные после такого вызова имена становятся ссылками на одну и ту же переменную.

А еще обнаружил, что интерпретатор «чинится», если взять ссылку, типа $z1 = &$z2; (можно абсолютно посторонние переменные).
То есть вот это работает уже корректно:

<?php
f(0, $$var);
$zz = &$z;
$x = 1;
$y = 2;
echo $x;
function f($a, $b) {}
Мало ли, может быть это какая-то фича, о которой мы не знали, и за которую отвечает какая-либо новая опция. Потому я на всякий случай и спросил, знает ли кто-либо об этом что-либо…

Но вообще да, скорее всего это действительно баг.
:))) Это 100% баг: незатронутые переменные ни при каких обстоятельсвах не должны изменять свое значение
теперь ясно, что это не баг, а «пасхальное яйцо»)
Лишняя иллюстрация того, что код без предупреждений надёжнее, чем с предупреждениями. ;-)
Либо неописанная фича либо баг, т.к. еще в 5.2 «фича» не работает еще.
Кстати, Автор небось забыл написать, чтобы за баг голосовали :)
-доктор, у меня тут болит, когда я делаю так
-не делайте так

этот код логически неверный, он не имеет смысла. просто не делайте так

зы: надеюсь, всем заметно, что в вызове функции используется указание на неопредённую область памяти через неинициализированную переменную
Да К.О., но в 5.2 это не проходит :)
Это понятно.
Но факт есть факт, что интерпретатор не должен сходить с ума после подобного вызова функции.

В этом случае $$var должен возвращать NULL (что и происходит), и PHP должен кидать нотис «Notice: Undefined variable» (что и делается), но не более того.
я бы даже не notice кидал, а fatal error (лучше даже exception), если пытаются таким образом вызвать фукнцию.

Не должны такие куски кода дальше продолжать работать, т.к. чаще всего они и вызывают недоумение у начинающих, которые просто «забыли» передать какой-либо параметр и получили на выходе совсем не то, что хотели.
Начинающим нужно ставить полный error_reporting. С рождения! И править все нотификейшены.
Самый лучший способ их научить — это отключить нафиг вообще нотификейшены и сыпать сразу ексепшены)

А мне больше интересно, как вы обнаружили такой баг? )
и долго ли «ржали» и что курили
да-да! +1 реквест фичкейса
Вот тут об том, как это началось: https://www.weblancer.net/forum/themes/17865.html
Так это целый «заповедник» извращенцев :)
НЛО прилетело и опубликовало эту надпись здесь
Вероятно автор имел ввиду сборщик мусора, который отдельным потоком чистит память.
создание переменных в си и паскале случаются при запуске программы, а уничтожение перед выходом из нее.

А сборщик мусора просто ищет области памяти(переменные), на которую больше нету ссылок и очищает ее. Причем это можно делать сразу после удаления последней ссылки или отдельным потоком. Но в паскале и си, в отличие от Java, такого нету (при работе с памятью), имхо=)
Особенно порадовал сборщик мусора в паскале
Да сейчас везде есть сборщики мусора для обьектов — при корректном закрытии приложения всё ООП очищается. Конечно, это не похоже на классический вариант и не всё чистится, но тем не менее.
Не надо путать финализацию и сборку мусора. Это разные понятия.
Возможно автор этого утверждения просто .NET-разработчик и имел в виду C# и VisualBasic. Впрочем это не меняет фатка, что выражение мутное.
НЛО прилетело и опубликовало эту надпись здесь
На лицо явно новые баги в интерпретатор добавили.
конечно же баг, уже в тот момент, когда разрешили вызывать не декларированые до вызова функции.

Кстати вот такой пример нагляднее:

f(0, $$var);
function f($a, $b) {}
$x = 1;
$y = 2;
echo $x;
1. PHP допускает вызывать не декларированые до вызова функции, и, хоть это и плохой стиль, но не должно вызывать подобные проблемы интерпретатора.

2. Вот Вам другой пример, где функция заранее задекларирована (как метод класса), но имеем тот же самый дефект, выводится 2, хотя по логике должно было бы быть 1.

<?php
class A {
function f($a, $b) {}
}

$a = new A();
$a->f(0, $$var);

$x = 1;
$y = 2;
echo $x;
?>
Да я и не спорю, что это баг интерпретатора, это я так, немножко поненавидел PHP.

$$var — вот в любом случае странная конструкция, то есть $var то до этого не декларирован — по хорошему надо просто сразу посылать да и всё
вот наглядно — если пытаемся передать переменную $ с пустым названием — то получаем баг

<?php
class A {
function f($a, $b) {}
}

$var = '';
$a = new A();
$a->f(0, $$var);

$x = 1;
$y = 2;
$z = 3;
echo $x;
?>
А вот если закрыть написать ${x} = 1; то получите на выходе 1.

Вызывается код referenc'инга, который не равен коду присваивания в байткоде (я и не знал до часа назад, что PHP уже байткодный).
Извините, но чем этот баг «интереснее» тысячи других?
Как по мне, так баги в компиляторах всегда стоит освещать. Потому что это не просто нарушение целостности программы, а очень коварная штука, которая может стоить кому то недели (а то и месяца) головной боли и адского дебага.
Давайте для каждого бага в хабре писать по посту? Это же полная ерунда, то о чем вы говорите.
Потому что стоило поменять местами аргументы в вызове f() и баг пропадал. Стоило объявить функцию до вызова и баг пропадал. Стоило вместо $x = 1 написать ${x} = 1 и баг пропадал. То есть он был очень нестабильный, и оказался достаточно серьёзным.
НЛО прилетело и опубликовало эту надпись здесь
переменная переменная.

$b=2;
$a='b';

echo $$a; //2
Спасибо, интересная конструкция, странно что не видел раньше. Наверно считается плохим стилем программирования, отлавливать ошибки с использованием "$$" операция не из легких.
Нет, это не считается плохим стилем программирования. Это просто переменная перменная. Иногда это бывает нужно. А не видели это видимо потому, что вы начинающий программист и просто не было потребности. Но скоро будет.
По-моему абсолютно бесполезная и глупая конструкция. В крайнем случае можно использовать $GLOBALS[$a] вместо $$a, правда опять же, не могу предположить, где это может реально понадобиться так, что без этого или не обойтись, или будет сложный код.
Вы, наверное, и тернарный оператор не используете? ;-)
НЛО прилетело и опубликовало эту надпись здесь
Я с глобалс привел просто как пример, естественно есть случаи, где его использовать нельзя будет.

Я имел в виду что в общем случае $$var по сути — ассоциативный массив, только без ограничения «области действия». Во всех этих случаях можно воспользоваться обычным ассоциативным массивом.
Да $$var это это ассоциативный массив. Область действия такая же, как и у обычного массива.
Бывает очень удобно использовать именно переменную. Частенько я сталприменять его после того, как под ооп начал писать.
Да нет, это плохой стиль, как и много идей заложеных в PHP когда из шаблонизатора решили сделать ООЯП.

В большинстве случаев проще и понятнее $a[$b] или, в крайнем случае, $a->$b.

$b = 'b';
$a = 'b';
$$$a = 'a';
print $$$a; // Happy debugging, bitches :)
У меня переполнение мозга! Спасибо, улыбнулся! :)
" Иногда это бывает нужно."

Ну прям хоть бы вот один примерчик, где это необходимо.
Не нашел подсветки html синтаксиса. Ну хорошо. Обьясню на словах.
Есть класс Config. Служебную информацию для классов он берет из файлов, а информацию для более частого изменени из админки. Так надо. У каждого модуля (класса) свои настройки. Дабы не забирать из базы или не создавать один большой файл с настройками мы делим все на подклассы. То есть на модули. Ниже по тексту покажу пример.
Запишу массив пременных из базы в $this->dbConfig, а массив переменных из файлов в $this->fileConfig.
Теперь немного кода.
public static function get($module, $fromFile = false) {
$ff = $fromFile? 'fileConfigs': 'dbConfigs';
return (isset(self::$$ff[$module][$name]))? self::$$ff[$module][$name]: false;
}

Можно было бы решить ифом и повторяющимся кодом. Или сделать $array[][][]. Можно найти еще много решений. Но данное самое удобно.
Ну и еще пример — конструктор запросов. $db->$delete->$user->from($array)->where($array). Но такие штуки я не люблю. И не изучал.
*php ситаксиса
*$this->dbConfigself::dbConfig
*$this->fileConfigself::fileConfig
*Но данное самое удобное. На мой взгляд.

Опечатки. Случайно нажал написать, вместо предпросмотр.
Самое удобное? Там где заранее известны возможные значения переменной и их 2 решение очень простое и читается лучше вашего:

public static function get($module, $name, $fromFile = false) {
    $m = $fromFile ? self::fileConfigs[$module] : self::dbConfigs[$module];
    return (isset($m[$name])) ? $m[$name] : false;
}


$name — вы забыли, кажется.

только self::$fileConfigs (вот за это я тоже не люблю PHP)
Да. $name забыл. C браузера плохо писать.
Ваш вариант действительно интересней, читаемей и проще.
Ну рас попросили показать, когда это нужно, то я и показал. Немного не досмотрел. Ну если переменных три или четыре, то использовать ее удобней будет.
Ну и если нужно создать новую переменную, то приходится использовать.
НЛО прилетело и опубликовало эту надпись здесь
-Папа, как мне доехать до Люберец из Москвы.
-Сядь на электричку на Выихино, проехай две остановки и выйди в Люберцах.
-А на вертолете не катит?
НЛО прилетело и опубликовало эту надпись здесь
Да. И денег такой перелет много кушает. Но вообще — покатит.
Так это целый «заповедник» извращенцев :)
На всякий случай… зарепортил его в баг-трекер


Отличная логика. :)

Зарепортили это хорошо, но забыли нажать там ссылку Add a patch и патч, собственно, приложить. :)
причина бага отсутствие строгой типизации и декларации переменных в php и отсутствии культуры программирования у авторов «бага»
> отсутствии культуры программирования у авторов «бага»

А вот такого писать не нужно. Вышеприведенный код не является примером рабочего кода авторов бага, а является специальным демонстрационным примером, предназначенным для выявления проблемы интерпретатора PHP.

Если хотите посмотреть примеры моего кода, могу дать ссылки на какие-либо из моих опенсорсных либ. Ну или погуглите например по слову multicurl. Вот если там найдете ламерство, вот тогда такое про «отсутствии культуры программирования» у автора будете иметь полное право писать. И я буду Вам за это благодарен, так как очень люблю критику в свой адрес.
Сам в исходники php не смотрел, но встречал отзывы от одного из лучших специалистов в криптографии ( vitus-wagner, патчи которого есть и в mysql, и в pgsql, и во многих других серьёзных продуктах) — невысокого мнения о php он.
Можно полюбопытствовать, занимательное чтиво.
рабинович напел?
Ну, в музыкальном слухе этого Рабиновича я уверен.
Отличный слух еще не означает того, что он так же поёт ;-)
Забыл дописать:

Ваш К.О.
я совершенно не согласен с этим человеком, будь он хоть господь бог
одна его цитата чего стоит: «Ситуация такова, что разработчик, который понимает что такое БД и зачем она нужна, ни за что не возьмется делать проект на mySQL»
что за бред? тысячи серьезных крупных сайтов с mysql
Вадим очень мощный разработчик
Авторы бага — это php team, надо полагать?
Авторы «бага» — звучит то как. Можно подумать специально его внесли в код.
НЛО прилетело и опубликовало эту надпись здесь
Не очень сейчас соображаю, но то, что «var» — зарезервированное слово, не играет роли? Или в этом и суть конструкции?
Переменная может называтся $var.
в пхп переменная может называть вообще как угодно. хоть $global, хоть $class
Скорее всего какое-то вмешательство в стековый фрейм (при передачи параметров функции).
НЛО прилетело и опубликовало эту надпись здесь
Не передавайте по ссылке неинициализированные переменные, и всё будет хорошо!
Таки да, это не PHP а руки автора.

begemot@ubuntu:/var/www$ php test.php
PHP Notice: Undefined variable: var in /var/www/test.php on line 2
PHP Notice: Undefined variable: in /var/www/test.php on line 2
2

Если у Вас приложения с нотисами, как вы собираетесь баг править?

Странно, а у меня результат, таки 1. Помните про буратино и яблоки?
А оно опять код скушало

$test = 0;
$var = 'test';
f(0, $$var);
$x = 1;
$y = 2;
echo $x;
function f($a, $b) {}

А ещё можно поменять местами аргументы у f и всё будет работать (кстати, проверьте, правда ли это).
Или объявить f до вызова.
Да, если объявление функции поместить ранше её вызова, результат 1.
А если поменять местами?
Проверить хочу, связано ли это с архитектурой/компилятором.
Это проблема не PHP а криворукости программиста.
1. использование контрукции $$var
2. не обявление всех переменных
3. вызов функции до её объявления
Похоже, что нет. Вернее, да, программиста. Но не того.

Свободный язык не должен глючить от свободы. Если он глючит — то он не свободный. К тому же, баг скорее всего не единственный в этом месте.
А если язык говорит — вот вам нотисы, то это как понимать?
ну, нотис — это лишь политкорректный намёк, который не должен менять логику работы
Если функцию объявить заранее, то дефект не наблюдается. Но если ее объявить как метод класса (неважно, статический или нет), то дефект все-таки будет.

<?php
class A {
public static function f($a, $b) {}
}

A::f(0, $$wrongvar);

$x = 1;
$y = 2;
echo $x;
?>

Имеем то же дефект.
Да, спасибо, я заметил уже это. Очень странное поведение. Я вот только проснулся, сейчас продолжим искать.
Это связано с тем, что вызов таких функций (у которых имя не известно до исполнения) происходит динамически, через zend_do_dynamic_function_call.

В этом случае, похоже, часть аргументов каким-то образом передаётся иначе. Самое странное это конечно зависимость от порядка аргументов.
Это понятно.
Но факт есть факт, что интерпретатор не должен сходить с ума после вызова функции с $$var.

В этом случае $$var должен возвращать NULL (что и происходит), и PHP должен кидать нотис «Notice: Undefined variable» (что и делается), но не более того.

И, надеюсь, под автором Вы имели в виду не меня, а кого-то другого? у меня нет «приложений с нотисами», вышеприведенный пример носит исключительно демонстрационный характер, чтобы показать баг интерпретатора PHP.
Вы сказали, что баг проявляется для Win и Linux версий, однако в багрепорте присутствует только Linux: bugs.php.net/bug.php?id=52001
Похоже, что нашёл причину бага: неправильный dereferencing EG(unitialized_zval_ptr).

bugs.php.net/bug.php?id=52001

Будем искать ещё.
Очень ценно, спасибо!
Нашёл, смотрите по ссылке в баг трекере.
Зопатчил.
Теперь я могу рассказать, почему это происходило. (Версия PHP 5.3.2)

Во-первых, если вызвать функцию до объявления (или если вызвать её как статическую функцию), будет инициирован вызов по имени (иначе на этапе компиляции по известном имени будет вызвана функция). При вызове по имени аргументы передаются через байткод, помещающий их в стэк аргументов. При вызове по указателю (объявление до вызова) аргументы передаются непосредственно. (Поправьте, если я не прав).

Вызов по имени приводит к тому, что при вызове zend_do_end_variable_parse передаётся arg_offset, нужный для помещения аргументов в стэк (Zend/zend_compile.c стр. 2169). В Zend/zend_compile.c стр. 1066 устанавливалось в этом случае ZEND_FETCH_MAKE_REF, но только если arg_offset был > 0 (для аргументов начиная со второго, что возможно тоже баг).

На этом интересная нам часть компиляции заканчивается.

Теперь, при выполнении, из небытия восстаёт новая переменная: $$var. В этом случае компилятор помещает её в локальный scope EG(active_symbol_table), беря в качестве структуры, хранящей данные, предназначенный для этого &EG(uninitialized_zval). Вернее используется указатель на указатель &EG(uninitialized_zval_ptr).

Далее для получения аргументов функцией вызывается вспомогательная функция zend_fetch_var_address_helper (zend_vm_def.h:4431), которой очень не нравится, что это не reference, и пытается сделать reference «вызовом» SEPARATE_ZVAL_TO_MAKE_IS_REF.

Однако она работает неправильно и перезатирает *variable_ptr_ptr, указывающий на EG(uninitialized_zval). После этого в качестве uninitialized_zval используется уже неправильная структура. У этой структуры refcount=1 (поскольку на неё ссылка лишь одна), в то время как у uninitialized_zval он равен 3.

И теперь фините ля комедия. Из небытия восстают $x, $y, $z, которые так же используют EG(unitialized_zval_ptr), который уже был переписан вызовом SEPARATE_ZVAL_TO_MAKE_IS_REF в другое значение. У этого значения refcount, напомню, равен 1, поэтому оно считается свободным. Из-за этого не создаётся новая структура в Zend/zend_execute.c стр 703 и ниже, а используется существующая все три раза. И последний раз затирает структуру последним значением (стр 708 и ниже в том же файле).

Если написать ${x} = 1, то это вызовет создание переменной не в CV и потому бага не будет.
> При вызове по указателю (объявление до вызова) аргументы передаются непосредственно. (Поправьте, если я не прав).

Вот пример. Функция объявлена до вызова как метод класса, вызывается как не статическая. Но имеем тот же баг.

<?php
class A {
public function f($a, $b) {}
}

$a = new A();
$a->f(0, $$var);

$x = 1;
$y = 2;
echo $x;
?>

В любом случае, спасибо за детальный разбор того, что там происходит внутри. Мы дальше того, чтобы локализовать и зарепортить этот баг, не пошли.
Не понятно, запатчил ли я его правильно. Это должны узнавать сами разработчики PHP.

В вышеприведённом случае функция так же вызывается динамически. Похоже статически вызываются только функции с явно глобальным пространством времён.

Я сейчас at least напишу хабратопик на тему поиска.
Кстати:
<?php
$var = 't'; #$t = 10;
f(0, $$var);
$x = 1;
$y = 2;
$z = 3;
echo "$x, $z, $y, $t\n";
function f($a, $b) {}
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории