php порядок выполнения операторов
Приоритет оператора
Приоритет оператора определяет, насколько «тесно» он связывает между собой два выражения. Например, выражение 1 + 5 * 3 вычисляется как 16, а не 18, поскольку оператор умножения («*») имеет более высокий приоритет, чем оператор сложения («+»). Круглые скобки могут использоваться для принудительного указания порядка выполнения операторов. Например, выражение (1 + 5) * 3 вычисляется как 18.
Операторы с равным приоритетом, но не имеющие ассоциативность, не могут использоваться вслед друг за другом, например 1 1 недопустимо в PHP. С другой стороны, выражение 1 Порядок выполнения операторов
(int) (float) (string) (array) (object) (bool) @
Пример #1 Ассоциативность
приоритет и ассоциативность оператора определяет только группировку выражений, и не определяет порядок выполнения. PHP (в целом) не описывает в каком порядке выражение выполняется, и следует избегать код, опирающийся на порядок выполнения, так как поведение может меняться между версиями PHP в зависимости от окружающего кода.
Пример #2 Неизвестный порядок выполнения
Операторы в PHP
Но прежде чем перейти к изучению операторов, я предлагаю изучить функцию var_dump(). Она позволяет вывести тип и значение чего-либо.
2 меньше 4. Результат – истина. Всё верно.
Поздравляю, теперь мы в любой момент можем узнать, что находится в переменной, или же сразу узнать результат какого-либо выражения. Давайте теперь вернёмся к нашей основной теме урока.
Тема операторов очень плотно переплетается с темой типов данных. Как мы с вами уже говорили на прошлом уроке, тип данных, который получится в результате какого-либо выражения, будет зависеть от оператора. И, например, результатом сложения при использовании оператора плюса всегда будет число.
Приоритет операторов
Начнём с того, что операторы имеют разный приоритет. Как, например, в математике: оператор умножения будет более приоритетным по сравнению с оператором сложения:
Приоритетом можно управлять с помощью круглых скобок, например:
Ну и тут думаю всё понятно:
В целом приоритеты операторов довольно предсказуемы.
Приоритет всех операторов в PHP можно посмотреть в официальной документации.
Типы операторов в PHP
А теперь давайте рассмотрим разные типы операторов
Арифметические операторы
Тут нас ждут знакомые со школьной скамьи операторы:
Многие впадают в ступор от оператора остатка от деления. Тут всё просто. В нашем случае мы вычисляем остаток от деления 6 на 4. Нацело не делится, целая часть – 1, и 2 в остатке. Ещё несколько аналогичных примеров для понимания:
Думаю, арифметические операторы больше не требуют дополнительных разъяснений.
Оператор присваивания
С ним мы уже работали. Используется для присваивания какого-либо значения в переменную.
Классический пример:
Ок, тут всё просто. А как на счёт такого:
Помимо этого есть так называемые сокращенные, комбинированные операторы присваивания.
Например, в комбинации с оператором сложения:
можно было бы записать как
Таким образом, если что-то совершается с левым операндом в исходном виде, то можно использовать эти сокращённые варианты. И да, это применяется и выглядит весьма элегантно, стоит к ним привыкать уже сейчас. Давайте рассмотрим ещё несколько примеров:
И с конкатенацией строк:
Операторы сравнения
Ну тут из названия понятно, что это за операторы и для чего они предназначены. Результатом их работы всегда будет булево значение (true или false).
Начнём с операторов равенства/неравенства:
boolean true
boolean false
boolean false
boolean true
Давайте поясню. Оператор == приводит операнды к одному типу и после сравнивает их значения. Так строка ‘2’ была преобразована к числу и значения оказались равными.
Оператор тождественного равенства === не выполняет приведения типов и сравнивает сначала то, что типы значений идентичны, например, целые числа, а затем сравнивает их значения. И если они одинаковы, то только в таком случае возвращает true.
Оператор неравенства != приводит типы к одному и сравнивает значения. Если они не равны, вернёт true, иначе – false.
Оператор тождественного неравенства !== сначала сравнивает типы, если они не идентичны, например, строка и число, то вернёт true, иначе сравнит их значения. Если они не равны, вернёт true, иначе – false.
Также к операторам сравнения относятся:
boolean false
boolean true
boolean false
boolean true
Тут всё очевидно, не будем задерживаться.
Spaceship
Порядок выполнения тернарного оператора
В php есть интересная особенность тернарного оператора — специфический и уникальный порядок выполнения.
Java и C++ тоже вернут 1
А какая вообще разница?
Я знаю об этом интересном нюансе довольно давно, но буквально вчера обнаружил ошибку в одном из открытых исходников: автор явно не знал об этом нюансе и попался. Потому, данная статья служит всего-лишь предупреждением. Ведь, если программист ожидает от php такого же поведения, как от других языков — может попасть в халепу.
Такой приём очень удобный для задания значений, зависимо от условий. Изящная замена if-else. Например:
Как избежать ошибки?
Первый способ — не использовать тернарный оператор в php.
Второй — прямо указывать порядок выполнения с помощью скобок:
Чем-то напоминает Лисп, не так ли?
Почему оно вообще так происходит?
Давайте разберемся в порядке выполнения тернарного оператора на примере JavaScript vs PHP
Напишем два тестовых скрипта, чтобы понять, как работает каждый из языков.
$ node ternary.js
cond.first
value.first
result: first
$ php ternary.php
cond.first
value.first
value.second
value.third
result: third
Какой из этого результата можно сделать вывод. Javascript разбирает тернарный оператор вполне логично. Сначала проверяет самое левое условие. Если оно верно, то выполняет и возвращает левую часть после первого двоеточия, если не верно, то правую.
Порядок вычисления в PHP
Примечание переводчика. Никита Попов внёс и продолжает вносить огромный вклад в развитие языка PHP. Он очень хорошо понимает внутренности движка PHP и в данной статье он объясняет некоторые особенности работы PHP в плане порядка вычисления выражений, которые, пожалуй, особо нигде и не найти. Этой статье около 7 лет и она практически не потеряла актуальность, однако найти её довольно сложно, потому что её нет в блоге Никиты Попова, а она опубликована в его gist-ах на гитхабе. Думаю полезно будет представить её сообществу на русском языке.
В своём любимом сообществе lolphp на реддит я наткнулся на пост, где люди удивляются результату следующего кода:
Приоритет и ассоциативность операторов
И «+» является лево-ассоциативным оператором, поэтому левый «+» формирует отдельную группу:
Примечание переводчика: код из оригинальной статьи представлял примеры выражений и не являлся валидным, поэтому он был несколько скорректирован.
В PHP на самом деле не определено что же произойдет. Одна версия PHP может выдать вам один результат, а другая — другой. Не пишите код, который зависит от какого-то определенного порядка вычисления выражения.
CV оптимизации
Все-таки, даже не смотря на то, что PHP не определяет порядок вычисления, будет интересно выяснить, почему же мы получаем довольно неожиданный результат в первом выражении (он будет одинаковым во всех последних версиях PHP).
Сгенерированные опкоды получаются довольно понятными:
Когда не происходит CV оптимизация
Теперь, когда мы применили оператор подавления ошибок, результат неожиданно поменялся с 3 на 2. И чтобы выяснить почему, давайте взглянем на опкоды:
CV оптимизация не выполняется, например, в случае обращения к элементам массивов или свойствам объектов.
Теперь, когда мы начали использовать массив, результат неожиданно поменялся с 3 на 2. И чтобы выяснить почему, давайте взглянем на опкоды:
Как мы видим, обращение к элементу массива здесь происходит при помощи FETCH_DIM_R (для считывания) и FETCH_DIM_RW (для чтения/записи) вместо прямого использования его в качестве операндов.
Именно потому, что получение операндов для сложения сейчас имеет отдельный опкод, оно будет происходить до инкремента, и результат будет другим.
На самостоятельное изучение оставлю аналогичный пример, но с использованием объектов. Найти пример кода можно по этой ссылке на 3v4l.org.
Выводы
Если делать какие-то выводы из всего этого, то я думаю они должны быть такими:
Примечание переводчика: как было сказано выше, @ отключает CV оптимизации только в 5.x, в PHP 7 CV оптимизации имеют место даже в случае использования оператора подавления ошибок (но возможно, это происходит не во всех случаях). У Никиты Попова в блоге есть интересная статья Static Optimization in PHP 7, на случай, если кто-то хочет глубже копнуть тему оптимизации.
Операторы PHP
Операторы в PHP работают с операндами, которые определяют переменные и значения, которые должны использоваться в конкретной операции. Число и расположение этих операндов по отношению к операторам (т.е. до и / или после оператора) зависит от типа рассматриваемого оператора.
Арифметические операторы PHP
Арифметические операторы используются для выполнения простых математических операций, таких как сложение, вычитание, умножение и т.д. Ниже приводится список арифметических операторов вместе с синтаксисом и операциями, которые предоставляет нам PHP:
Арифметические операторы работают с двумя операндами, один слева, а другой справа от оператора. Например:
Пример
Логические или реляционные операторы
Здесь логический оператор — это часть предложения «И». Если бы мы выразили это на языке PHP, мы бы использовали описанные ранее операторы сравнения вместе с логическим оператором && :
Точно так же, составим следующее предложение:
Затем мы заменим «ИЛИ» на эквивалент PHP || :
Мы представляем исключающее ‘или’ ключевым словом xor :
Операторы сравнения PHP
Операторы сравнения предоставляют возможность сравнивать одно значение с другим и возвращать истинный (true) или ложный (false) результат в зависимости от статуса совпадения. Например, вы можете использовать оператор сравнения, чтобы проверить, соответствует ли значение переменной определенному числу или идентична ли одна строка другой. PHP предоставляет широкий выбор операторов сравнения практически для любых задач сравнения.
Операторы сравнения используются с двумя операндами, один слева и один справа от оператора. В следующей таблице представлены операторы сравнения PHP и приведены краткие описания и примеры:
При сравнивании целого числа со строкой, строка будет преобразована к числу. В случае, если вы сравниваете две числовые строки, они сравниваются как целые числа:
Пример
Оператор spaceship (космический корабль)
Пример
Условные или тернарные операторы
Синтаксис:
Пример
Результат выполнения кода:
Пример
Результат выполнения кода:
Операторы присваивания
В следующей таблице перечислены семь операторов присваивания, доступных в PHP, вместе с описаниями и примерами их использования:
Пример
Операторы массивов PHP
Операторы массивов PHP используются для сравнения массивов.
В следующей таблице приведён список операторов, работающих с массивами PHP:
Пример
Операторы увеличения и уменьшения PHP
Эти операторы можно использовать двумя способами: до и после операнда. Предварительный режим выполняет приращение (Pre-Increment) или декремент (Pre-Decrement) перед выполнением остальной части выражения. Например, вам нужно увеличить значение переменной до того, как оно будет присвоено другой переменной или использовано в вычислениях. В пост- режиме увеличение (Post-Increment) или уменьшение (Post-Decrement) выполняется после того, как выражение было выполнено. В этом случае вы можете уменьшить значение после того, как оно было назначено или использовано в вычислении.
В следующей таблице представлены различные формы операторов инкремента и декремента (до и после), а также примеры, которые показывают, как эквивалентная задача должна быть выполнена без операторов инкремента и декремента:
Пример
Строковые операторы PHP
Оператор конкатенации строк PHP используется для объединения значений при создании строки. Оператор конкатенации представлен точкой (.) и может использоваться для построения строки из других строк, переменных, не содержащих строки (например, чисел), и даже констант.
В PHP есть два оператора, специально разработанные для строк: