В php есть интересная особенность тернарного оператора — специфический и уникальный порядок выполнения.
Я знаю об этом интересном нюансе довольно давно, но буквально вчера обнаружил ошибку в одном из открытых исходников: автор явно не знал об этом нюансе и попался. Потому, данная статья служит всего-лишь предупреждением. Ведь, если программист ожидает от php такого же поведения, как от других языков — может попасть в халепу.
Такой приём очень удобный для задания значений, зависимо от условий. Изящная замена if-else. Например:
Первый способ — не использовать тернарный оператор в php.
Второй — прямо указывать порядок выполнения с помощью скобок:
Чем-то напоминает Лисп, не так ли?
Давайте разберемся в порядке выполнения тернарного оператора на примере JavaScript vs PHP
Напишем два тестовых скрипта, чтобы понять, как работает каждый из языков.
На всякий случай поясню, что при
Какой из этого результата можно сделать вывод. Javascript разбирает тернарный оператор вполне логично. Сначала проверяет самое левое условие. Если оно верно, то выполняет и возвращает левую часть после первого двоеточия, если не верно, то правую.
PHP мыслит оригинально.
Что подтверждается в официальном мануале:
В общем PHP себя снова показал «с лучшей стороны», но проблема далеко не критическая. Главное — знать про неё и быть осторожным с тернарным оператором в PHP.
$ python -c "print 1 if true else 2 if true else 3 if true else 4 if true else 5"
1
$ node -e " true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5"
1
$ perl -e "print true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5"
1
$ ruby -e "print true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5"
1
$ php -r "print true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5;"
4
Java и C++ тоже вернут 1А какая вообще разница?
Я знаю об этом интересном нюансе довольно давно, но буквально вчера обнаружил ошибку в одном из открытых исходников: автор явно не знал об этом нюансе и попался. Потому, данная статья служит всего-лишь предупреждением. Ведь, если программист ожидает от php такого же поведения, как от других языков — может попасть в халепу.
Такой приём очень удобный для задания значений, зависимо от условий. Изящная замена if-else. Например:
value = isCondFirst() ? valueFirst() :
isCondSecond() ? valueSecond() :
isCondThird() ? valueThird() :
valueDefault();
/********** Вместо **********/
if (isCondFirst()) {
value = valueFirst();
} else if (isCondSecond()) {
value = valueSecond();
} else if (isCondThird()) {
value = valueThird();
} else {
value = valueDefault();
}
Как избежать ошибки?
Первый способ — не использовать тернарный оператор в php.
Второй — прямо указывать порядок выполнения с помощью скобок:
$ php -r "print true ? 1 : (true ? 2 : (true ? 3 : (true ? 4 : 5)));"
1
Чем-то напоминает Лисп, не так ли?
Почему оно вообще так происходит?
Давайте разберемся в порядке выполнения тернарного оператора на примере JavaScript vs PHP
Напишем два тестовых скрипта, чтобы понять, как работает каждый из языков.
На всякий случай поясню, что при
$foo = $lambda('fooMsg', 'fooReturn')
, $foo
содержит в себе функцию, которая при вызове выведет в консоль сообщение 'fooMsg'
и вернет значение 'fooReturn'
$ cat ternary.js
var lambda = function (logMsg, returnValue) { return function () { console.log(logMsg); return returnValue; }; }; var cond = { first : lambda('cond.first' , true), second: lambda('cond.second', true), third : lambda('cond.third' , true) }; var value = { first : lambda('value.first' , 'first'), second : lambda('value.second' , 'second'), third : lambda('value.third' , 'third'), default: lambda('value.default', 'default') }; console.log( 'result: ', cond.first() ? value.first() : cond.second() ? value.second() : cond.third() ? value.third() : value.default() );
$ node ternary.js
cond.first
value.first
result: first
$ cat ternary.php
<?php $lambda = function ($logMsg, $returnValue) { return function () use ($logMsg, $returnValue) { echo $logMsg . PHP_EOL; return $returnValue; }; }; $cond = array( 'first' => $lambda('cond.first' , true), 'second'=> $lambda('cond.second', true), 'third' => $lambda('cond.third' , true), ); $value = array( 'first' => $lambda('value.first' , 'first'), 'second' => $lambda('value.second' , 'second'), 'third' => $lambda('value.third' , 'third'), 'default'=> $lambda('value.default', 'default'), ); echo 'result: ' . ( $cond['first']() ? $value['first']() : $cond['second']() ? $value['second']() : $cond['third']() ? $value['third']() : $value['default']() ) . PHP_EOL; ?>
$ php ternary.php
cond.first
value.first
value.second
value.third
result: third
Какой из этого результата можно сделать вывод. Javascript разбирает тернарный оператор вполне логично. Сначала проверяет самое левое условие. Если оно верно, то выполняет и возвращает левую часть после первого двоеточия, если не верно, то правую.
(cond.first() ? value.first() :
(cond.second() ? value.second() :
(cond.third() ? value.third() :
(value.default()))));
/********
* ===>
*/
true ? 'value.first' : /* ignored */;
PHP мыслит оригинально.
(
(
cond.first() ?
value.first() :
cond.second()
) ?
value.second() :
cond.third()
) ?
value.third() :
value.default();
/********
* ===>
*/
(
(
'value.first'
) ?
value.second() :
cond.third()
) ?
value.third() :
value.default();
/********
* ===>
*/
(
'value.second'
) ?
value.third() :
value.default();
/********
* ===>
*/
'value.third'
Что подтверждается в официальном мануале:
$a = true ? 0 : true ? 1 : 2; // (true ? 0 : true) ? 1 : 2 = 2
Заключение
В общем PHP себя снова показал «с лучшей стороны», но проблема далеко не критическая. Главное — знать про неё и быть осторожным с тернарным оператором в PHP.