121. ALEN - 26 Сентября, 2013 - 10:06:39 - перейти к сообщению
LIME, тогда поправлюсь: "Очередь выполнения".
122. LIME - 26 Сентября, 2013 - 10:08:33 - перейти к сообщению
ALEN а в чем состоит поправка?
123. EuGen - 26 Сентября, 2013 - 10:10:20 - перейти к сообщению
Итак, чтобы более не затягивать ответ - опишу ситуацию как можно подробнее - насколько смогу. Прежде всего, прошу прощения за некий вызов, сделанный мною сообществу в hint1 - это было сделано не с целью выразить мысль "всё равно не ответите", а с целью подогреть интерес. Надеюсь, ни один пользователь не в обиде.
Итоги и направление мыслей
На самом деле, полный ответ на поставленный вопрос уходит далеко за рамки обычного использования PHP. Скажу, что наиболее правильные мысли были выражены Ильёй (DeepVarvar) и caballero (к сожалению, не знаю имени). Илья предположил, что дело в том, как интерпретатор работает с такими выражениями на уровне байткода (п. 2 в комментарии), тогда как caballero разумно предложил посмотреть, что происходит на ассемблерном уровне. На самом деле, ассемблерный уровень показал бы, что это действительно так происходит, но не показал бы, почему это так - поскольку это уже скомпилированные из опкодов готовые процессорные инструкции. Правда кроется как раз в опкодах, которые получаются после интерпретации программы и которые затем исполняются в Zend VM.
Ассоциативность и приоритет
Многие предполагали, что суть дела состоит в приоритете операторов или в некоторой ассоциативности. Отчасти это правда, однако без понимания, как именно это работает, ответ всё равно не очевиден. Правда состоит в том, что на самом деле выражения в PHP не выполняются с учётом ассоциативности и приоритета. Ассоциативность и приоритет лишь показывают, как выражения будут сгруппированы - но не порядок их дальнейшего исполнения. Почувствуйте разницу. Для обсуждаемых примеров:
Итоги и направление мыслей
На самом деле, полный ответ на поставленный вопрос уходит далеко за рамки обычного использования PHP. Скажу, что наиболее правильные мысли были выражены Ильёй (DeepVarvar) и caballero (к сожалению, не знаю имени). Илья предположил, что дело в том, как интерпретатор работает с такими выражениями на уровне байткода (п. 2 в комментарии), тогда как caballero разумно предложил посмотреть, что происходит на ассемблерном уровне. На самом деле, ассемблерный уровень показал бы, что это действительно так происходит, но не показал бы, почему это так - поскольку это уже скомпилированные из опкодов готовые процессорные инструкции. Правда кроется как раз в опкодах, которые получаются после интерпретации программы и которые затем исполняются в Zend VM.
Ассоциативность и приоритет
Многие предполагали, что суть дела состоит в приоритете операторов или в некоторой ассоциативности. Отчасти это правда, однако без понимания, как именно это работает, ответ всё равно не очевиден. Правда состоит в том, что на самом деле выражения в PHP не выполняются с учётом ассоциативности и приоритета. Ассоциативность и приоритет лишь показывают, как выражения будут сгруппированы - но не порядок их дальнейшего исполнения. Почувствуйте разницу. Для обсуждаемых примеров:
CODE (php):
скопировать код в буфер обмена
скопировать код в буфер обмена
- //в первом выражении:
- $x + $x++;
- // "++" имеет более высокий приоритет, чем "+", поэтому "$x++" будет сгруппирован, что равносильно
- $x + ($x++);
- //во втором выражении:
- $x + $x + $x++;
- // и снова "++" имеет более высокий приоритет, чем "+", так что группировка будет такой:
- $x + $x + ($x++);
- // и, наконец, "+" - это лево-ассоциативный оператор, поэтому левый "+" будет сгруппирован (но не правый!):
- ($x + $x) + ($x++);
Понятнее? Наверно. Однако - что это значит в плане порядка вычисления выражения? А ничего. В последнем выражении левая часть ($x + $x) или же правая ($x++) - могут вычисляться первыми. И PHP не регламентирует, что же случится в таком случае на самом деле. Поэтому вывод таков - не стоит полагаться на это (то есть ошибочно считать, что ассоциативность и приоритет определяют порядок выполнения выражения) в своих программах. Однако же это не даёт полного ответа, верно? И тем более про случай с "@".
Оптимизация "компилированных переменных"
Если предыдущая часть ещё могла быть предположена (хотя ни из одного руководства это явно не следует), то эта - уже намного более специфична - и именно здесь скрыт ответ, почему так происходит в обсуждаемых примерах.
Немного пояснений. PHP исполняется в два этапа. На первом этапе интерпретатор проходит программу и генерирует опкоды - некоторые инструкции, которые имеют два операнда и инструкцию, к которой эти операнды применяются. Затем опкоды исполняются в Zend VM (Zend Virtual Machine) для получения уже готовых машинных инструкций.
Начиная с версии 5.1 в PHP введена функциональность оптимизации "компилированных переменных" (compiled variables optimization). Эта оптимизация позволяет простым переменным быть непосредственно операндами самих опкодов. Простые переменные - это, например, $foo или $bar - но не $foo->bar или $foo['bar']. Итак, можно проверить, что же получится в результате обработки первого примера в терминах опкодов:
CODE (text):
скопировать код в буфер обмена
скопировать код в буфер обмена
- //код второго примера:
- $x = 1;
- $y = ($x + $x) + ($x++);
- //опкоды:
- ASSIGN $x, 1
- $tmp_1 = ADD $x, $x
- $tmp_2 = POST_INC $x
- $tmp_3 = ADD $tmp_1, $tmp_2
- ASSIGN $y, $tmp_3
- в принципе, должно быть интуитивно понятно соответствие опкодов операциям программы. Обратите внимание, что $x может участвовать в опкодах непосредственно - так как она является простой переменной. На всякий случай, всё же поясню: сначала присвоить $x значение 1 (ASSIGN $x, 1), затем добавить к $x её же и сохранить результат в $tmp_1 ($tmp_1 = ADD $x, $x), затем выполнить постинкремент для $x и сохранить результат в $tmp_2 ($tmp_2 = POST_INC $x), затем сложить $tmp1 и $tmp2 с сохранением результата в $tmp_3 ($tmp_3 = ADD $tmp_1, $tmp_2) - и, наконец, присвоить $x конечное значение выражения (ASSIGN $y, $tmp_3). Как видно, в данном примере вычисление произошло слева направо (вероятно, ожидаемый порядок)
Теперь первый пример.
Опкоды, которые получатся в результате, будут такими:
CODE (text):
скопировать код в буфер обмена
скопировать код в буфер обмена
- //код первого примера:
- $x = 1;
- $y = $x + ($x++);
- //опкоды
- ASSIGN $x, 1
- $tmp_1 = POST_INC $x
- $tmp_2 = ADD $x, $tmp_1
- ASSIGN $y, $tmp_2