Булев тип данных в PHP и возможные проблемы

archive view archive save

articles Булев или булевый (логический, бинарный) это простейший тип данных. Его значение выражается истинностью или ложью  - либо TRUE, либо FALSE, был введен начиная с PHP4. При определении булева типа используется ключевое слово TRUE или FALSE, оба регистро-независимы.

В примерах ниже мы будем использовать заранее заготовленные функции:

<?php function EmptyReturn(){}
function SimpleReturn(){ return; }
function ReturnNull(){ return null; }
function ReturnFalse(){ return false; }
function ReturnTrue(){ return true; }
function ReturnZero(){ return 0; }
function ReturnZeroString(){ return '0'; }
function ReturnOneString(){ return '1'; }

Отображение булева типа с помощью функции var_dump();

  • Код: var_dump(EmptyReturn()); - вернёт: null
  • Код: var_dump(SimpleReturn()); - вернёт: null
  • Код: var_dump(ReturnNull()); - вернёт: null
  • Код: var_dump(ReturnFalse()); - вернёт: boolean false
  • Код: var_dump(ReturnTrue()); - вернёт: boolean true
  • Код: var_dump(ReturnZero()); - вернёт: int 0
  • Код: var_dump(ReturnZeroString()); - вернёт: string '0' (length=1)
  • Код: var_dump(ReturnOneString()); - вернёт: string '1' (length=1)

Отображение булева типа с помощью функции echo ...;

  • Код: echo EmptyReturn(); - выведет: ""
  • Код: echo SimpleReturn(); - выведет: ""
  • Код: echo ReturnNull(); - выведет: ""
  • Код: echo ReturnFalse(); - выведет: ""
  • Код: echo ReturnTrue(); - выведет: "1"
  • Код: echo ReturnZero(); - выведет: "0"
  • Код: echo ReturnZeroString(); - выведет: "0"
  • Код: echo ReturnOneString(); - выведет: "1"
  • Код: echo true; - выведет: "1"

Вопрос: Почему "echo ReturnTrue();" выводит "1"? Ответ: Потому, что в любых выражениях, в которых должна быть строка, происходит автоматическое преобразование в требуемый тип. Например когда мы сравниваем строку со значением переменной либо используем print() или echo().

  • Булево (boolean) значение FALSE всегда представляется как пустая строка (""), а значение TRUE всегда преобразуется в строку "1".
  • Число с плавающей точкой (float) или целое число (integer) преобразуется в строку, представленную числом, которое состоит из цифр (для чисел с плавающей точкой включая показатель степени).
  • Массивы (array) всегда преобразуются в строку "Array", потому мы не сможем узнать содержимое массива при помощи print() или echo().
  • Объекты преобразуются в строку "Object".
  • Ресурсы (resource) в строки со структурой "Resource id #1", где 1 - является уникальным номером ресурса, который PHP присвоил ему во время выполнения скрипта. Для получения типа ресурса нужно использовать get_resource_type().
  • NULL всегда преобразуется в "" (пустую строку).

Для вывода содержимого массивов, объектов или ресурсов можно использовать функции var_dump() и print_r().

Операторы сравнения булева типа

В официальной документации PHP: Операторы сравнения - Manual сказано, что "Преобразование типов не происходит при использовании === или !== так как в этом случае кроме самих значений сравниваются еще и типы."

Код с оператором сравнения:
if(ReturnTrue() !== true){echo 'FALSE'}else{echo 'TRUE'}
выдаст:
TRUE

Код с оператором сравнения:
if(ReturnOneString() !== true){echo 'FALSE'}else{echo 'TRUE'}
выдаст:
FALSE

Иными словами, если мы используем оепратор сравнения !== при сравнении булевого значения "1" в виде строки с булевым "TRUE", то мы всегда будем получать "FALSE". Для успешного сравнения как булевого типа так и его строкового значения, например "TRUE" и "1", мы должны использовать операторы сравнени при которых происходит автоматическое преобразование типов "if("1" != true)" или "if(!$a)", либо явно выполнять преобразование типа "if((bool)"1" !== true)".

Также следует иметь ввиду, что преобразование в булев тип любой строки (кроме (bool)"" и (bool)"0") будет равно = TRUE!

Когда может потребоваться преобразование в булев тип

В некоторых ситуациях, некоторые функции по каким-то причинам могут возвращать булев тип TRUE в виде строкового значения "1" в результате чего может быть получен заведомо ложный и непредсказуемый результат.

С одним из таких примеров пришлось познакомиться в движке PrestaShop, когда выполнялось обновление адреса доставки в "личном кабинете". При этом в некоторых случаях обновление адреса выполнялось без ошибок, а в некоторых завершалось ошибкой сервера (код 500 Internal Server Error), которая на удивление генерировалась самим движком с помощью header('HTTP/1.1 500 Internal Server Error');, а не сервером. Исследование проблемы, после включения в движке режима разработчика и отображения всех ошибок PHP, вывело на кусок кода где выполнялось сравнение логического типа возвращаемых данных (или ака булев тип) в функции validateFields() из файла /classes/ObjectModel.php:

function validateFields() {
    $message = $this->validateField($field, $this->$field);
    if ($message !== true) {
        ...
            throw new PrestaShopException($message);
    }
}

echo gettype($message); exit; в результате выдало "string", а echo $message; показало "1", что в итоге использования оператора "!==" закономерно привело к FALSE результату.

Однако, мы видим, что переменная $message, в результате работы функции validateField() кроме булева типа TRUE/FALSE может ещё содержать строку с сообщением об ошибках кидаемое в PrestaShopException($message) класс /classes/exception/PrestaShopException.php, - а значит, конкретно в данном случае, преобразование строкового значения "1" в булев тип TRUE может быть лишь временным решением проблемы и в идеале стоило бы отловить тот кусок кода, который возвращает логический тип TRUE (ещё ака двоичные данные) в виде строкового значения "1".

Чтобы преобразование строки в булев тип было более безопасным, ведь мы знаем, что любая строка (только если это не "" или "0") после преобразования будет = TRUE, что может создавать определённый риск для безопасности, можно поступить следующим, более безопасным, способом преобразования:

function validateFields() {
    $message = $this->validateField($field, $this->$field);
    if (gettype($message) == "string" || gettype($message) == "integer"
                    && $message == "1") {
        $message = (bool)$message;
    }
    if ($message !== true) {
        ...
            throw new PrestaShopException($message);
    }
}

Комментарии в блоге
Новое на форуме