Логические операторы присваивания в PHP

Я ловлю себя на том, что делаю такие вещи довольно часто:

$foo = true;
$foo = $foo && false; // bool(false)

С побитовыми операторами вы можете использовать сокращения &= и |=:

$foo = 1;
$foo &= 0; // int(0)

Учитывая, что побитовые операции над 1 и 0 функционально эквивалентны логическим операциям над true и false, мы можем положиться на приведение типов и сделать что-то вроде этого:

$foo = true;
$foo &= false; // int(0)
$foo = (bool)$foo; // bool(false)

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

То, что я действительно хотел бы сделать, это что-то вроде этого:

$foo = true;
$foo &&= false; // bool(false)

... но &&= и ||=, очевидно, недопустимые операторы. Итак, мой вопрос: есть ли какой-то другой слащавый синтаксис или, может быть, неясная основная функция, которая могла бы служить заменой? С такими короткими переменными, как $foo, несложно просто использовать синтаксис $foo = $foo && false, но элементы массива с несколькими измерениями и/или вызовы объектных методов могут сделать синтаксис довольно длинным.


person FtDRbwLXw6    schedule 09.07.2013    source источник
comment
Что не так с $foo = $foo && false;? Или это просто любопытство?   -  person Dany Caissy    schedule 09.07.2013
comment
@DanyCaissy: я объяснил в последнем абзаце. В этом нет ничего плохого; просто этот синтаксис избыточен и может стать очень длинным (например, $some['big']['long']['variable'] = $some['big']['long']['variable'] && $some['other']['boolean'];).   -  person FtDRbwLXw6    schedule 09.07.2013
comment
Вы можете изменить свои примеры. Логически, они всегда будут давать один и тот же результат = $foo &= false; и $foo &&= false; для $foo = true. Поэтому я не вижу проблемы/цели.   -  person Jason McCreary    schedule 09.07.2013
comment
@JasonMcCreary: это зависит от вашего определения того же самого. Как показывают мои примеры, первое дает int(0), а второе дает bool(false). Итак, хотя 0 == false из-за неявного приведения типов, 0 !== false.   -  person FtDRbwLXw6    schedule 09.07.2013
comment
Это все еще актуально спустя 7 лет с проверками соблюдения типов PHP7/8. Очень легко случайно преобразовать логическое значение в целое, если вы используете короткий синтаксис.   -  person evo_rob    schedule 06.04.2021


Ответы (2)


В каком-то смысле вы сами ответили на свой вопрос:

побитовые операции с 1 и 0 функционально эквивалентны логическим операциям с истинными и ложными

Принимая во внимание, что PHP является слабо типизированным языком, поэтому нет необходимости приведения типов к строгим логическим значениям и обратно, поскольку 1 и 0 эквивалентны true и false (за исключением строгого равенства, см. ниже).

Рассмотрим следующий код, используя ваши примеры:

$foo = true;
$foo &= false;

if (!$foo) {
  echo 'Bitwise works!';
}

$bar = true;
$bar = $bar && false;

if (!$bar) {
  echo 'Boolean works!';
}

// Output: Bitwise works!Boolean works!

Учитывая неявное жонглирование типами, ложные значения, и за исключением строгое равенство, мне трудно понять, где такие сокращенные операции &&= и ||= не дали бы того же результата, что и &= и |=. Особенно при оценке логических значений. Вероятно, поэтому таких сокращений не существует в PHP.

Обновлять

Некоторые быстрые тесты доказывают, что они действительно эквивалентны, за исключением настоящих массивов/объектов:

<?php
$values = array(false, 0, 0.0, "", "0", array(), 12, "string", array(1));

foreach ($values as $value) {
    $bit_test = true;
    $bit_test &= $value;

    $bool_test = true;
    $bool_test = $bool_test && false;
    if ($bit_test != $bool_test) {
        echo 'Difference for: ';
        var_dump($value);
    }
}

// Output:
// Difference for: array(1) {
//  [0]=>
//  int(1)
// }
person Jason McCreary    schedule 09.07.2013
comment
Вы правы, но на самом деле я искал решение, которое не меняет тип (поэтому мой третий пример явно возвращал тип обратно в bool). У меня есть контроль над обоими операндами, и они гарантированно будут bool, поэтому я бы предпочел просто использовать более длинный синтаксис, чем потенциально вносить ошибки в код, возвращая ложные значения вместо строгого bool, как задокументировано в интерфейсе. - person FtDRbwLXw6; 09.07.2013
comment
Справедливый. Несмотря на то, что PHP является динамическим языком со слабой типизацией, я поддерживаю написание такого явного кода. Мой ответ основан на том факте, что для логических значений (и большинства других простых типов) побитовые операторы эквивалентны логическим операторам. Но если вы хотите использовать такое сокращение, вам придется отказаться от строгих проверок на равенство (или сравнить с 0/1). В итоге не вижу смысла. Тем не менее, я хотел дать ответ будущим читателям. - person Jason McCreary; 09.07.2013
comment
Имейте в виду, что вы всегда можете привести его до возврата значения. В любом случае, наверное, хорошая практика. - person Jason McCreary; 09.07.2013
comment
@JasonMcCreary Нет, логические и побитовые операторы не эквивалентны, хотя их вывод эквивалентен. Большая разница в том, что PHP даже не выполняет выражение для логических операций, если результат не имеет значения. Так что $res = sql_query() OR die(); не эквивалентно $res = sql_query(); $res |= die();. - person Matmarbon; 25.03.2015
comment
@Matmarbon, правильно, в этих комментариях я забыл ключевой квалификатор - функционально эквивалентен. - person Jason McCreary; 25.03.2015

Как упомянул Джейсон, побитовые операторы будут работать, и нет необходимости преобразовывать результат обратно в логическое значение, поскольку PHP уже будет правильно обрабатывать их значение как логическое.

Если вам нужна альтернатива, которая не использует побитовые операторы, ради удобочитаемости или если вы хотите строгого равенства, вы можете использовать этот метод:

function OrOp(&$booleanVar, $conditions)
{
    $booleanVar = $booleanVar && $conditions;
    return $booleanVar;
}

Это означает, что вы можете изменить это:

$foo = $foo && false;

К этому :

OrOp($foo, false);

Это также будет работать с несколькими условиями:

OrOp($foo, $condition1 && $condition2 && $condition3);
person Dany Caissy    schedule 09.07.2013
comment
Хотя это полезно, кажется, что ОП ищет что-то родное и знает, что может написать собственный код. - person Jason McCreary; 09.07.2013
comment
Я не думаю, что есть что-то родное, что сделало бы это за них, поэтому я разместил это здесь. Я удалю свой ответ, если докажу, что ошибаюсь. Хотя я уверен, что OP мог бы сам придумать этот метод, будущие менее опытные читатели могут найти его полезным. - person Dany Caissy; 09.07.2013