Проверить, установлено ли значение и равно null

88

Мне нужно проверить, определено ли значение как что-либо, включая null. issetобрабатывает нулевые значения как неопределенные и возвращает false. В качестве примера возьмем следующее:

$foo = null;

if(isset($foo)) // returns false
if(isset($bar)) // returns false
if(isset($foo) || is_null($foo)) // returns true
if(isset($bar) || is_null($bar)) // returns true, raises a notice

Обратите внимание, что $bar это не определено.

Мне нужно найти условие, удовлетворяющее следующему:

if(something($bar)) // returns false;
if(something($foo)) // returns true;

Любые идеи?

Тату Улманен
источник
19
if (isset ($ foo)) // возвращает false, я упал со стула, все эти годы ...
max4ever
in_array ($ key, array_keys ($ _ SESSION)) && is_null ($ _ SESSION [$ key]) Я так долго думал об этом ..
Джек
1
Для меня это не нормальное поведение, isset= установлено?, Ваша переменная установлена ​​на ноль. Я потратил кучу времени из-за этого ...
Винсент Деко

Ответы:

84

IIRC, вы можете использовать get_defined_vars()для этого:

$foo = NULL;
$vars = get_defined_vars();
if (array_key_exists('bar', $vars)) {}; // Should evaluate to FALSE
if (array_key_exists('foo', $vars)) {}; // Should evaluate to TRUE
Хенрик Опель
источник
+1 Собирался предложить такую ​​же функцию, get_defined_varsс размахом прекрасно справляется.
Салате
1
Вроде работает, но я надеялся на что-нибудь попроще. Ну что ж. Посмотрим, сможет ли кто-нибудь придумать однострочник.
Тату Улманен
4
ну, вам не нужны вары, поэтому теоретически это одна строка "if (array_key_exists ('foo', get_defined_vars ())) {}"
Ханнес
новый ответ FVN в может быть более быстрым способом , чтобы получить переменную , которая существует в текущем контексте, чтобы избежать затрат на get_defined_vars(): array_key_exists('foo', compact('foo')). Или быстрее, если тестирование глобальной: array_key_exists('foo', $GLOBALS).
ToolmakerSteve
25

Если вы имеете дело со свойствами объекта, которые могут иметь значение NULL, вы можете использовать: property_exists()вместоisset()

<?php

class myClass {
    public $mine;
    private $xpto;
    static protected $test;

    function test() {
        var_dump(property_exists($this, 'xpto')); //true
    }
}

var_dump(property_exists('myClass', 'mine'));   //true
var_dump(property_exists(new myClass, 'mine')); //true
var_dump(property_exists('myClass', 'xpto'));   //true, as of PHP 5.3.0
var_dump(property_exists('myClass', 'bar'));    //false
var_dump(property_exists('myClass', 'test'));   //true, as of PHP 5.3.0
myClass::test();

?>

В отличие от isset (), property_exists () возвращает TRUE, даже если свойство имеет значение NULL.

Джон Магнолия
источник
11
Вы можете сделать то же самое для массивов с помощью array_key_exists ();
teaqu
14

См. « Лучший способ проверить наличие переменной в PHP»; isset () явно не работает

 if( array_key_exists('foo', $GLOBALS) && is_null($foo)) // true & true => true
 if( array_key_exists('bar', $GLOBALS) && is_null($bar)) // false &  => false
Лоик Феврие
источник
3
Цитируемый вами код работает только в том случае, если переменная находится в глобальной области видимости.
Равелин
Действительно, но разве это не самый частый случай? В функции у вас будут переменные в глобальной области видимости и аргументы (которые всегда определены). У вас также могут быть свойства объекта, но тогда вы можете использовать property_exists.
Лоик Феврие,
Использование $ GLOBALS кажется немного нестабильным, мне нужно провести некоторое тестирование, прежде чем я смогу заявить, что это работает.
Тату Улманен
4

Я обнаружил, что compactэто функция, которая игнорирует неустановленные переменные, но действует на те, которые установлены null, поэтому, когда у вас есть большая локальная таблица символов, я думаю, вы можете получить более эффективное решение по проверке array_key_exists('foo', get_defined_vars()), используя array_key_exists('foo', compact('foo')):

$foo = null;
echo isset($foo) ? 'true' : 'false'; // false
echo array_key_exists('foo', compact('foo')) ? 'true' : 'false'; // true
echo isset($bar) ? 'true' : 'false'; // false
echo array_key_exists('bar', compact('bar')) ? 'true' : 'false'; // false

Обновить

Начиная с PHP 7.3 compact () выдает уведомление о неустановленных значениях, поэтому, к сожалению, эта альтернатива больше не действует.

compact () теперь выдает ошибку уровня E_NOTICE, если данная строка ссылается на неустановленную переменную. Раньше такие строки молча пропускали.

nzn
источник
Интересная альтернатива. Но обратите внимание, что это, вероятно, медленнее, чем вызов array_key_exists в существующем массиве, таком как $ GLOBALS, потому что поиск в хеш-таблице не замедляется, когда таблица становится больше, и вы добавили дополнительную работу compact. Тем не менее, я поддержал его, потому что он полезен в одной ситуации: если вы хотите знать, fooсуществует ли он в текущем контексте , независимо от того, откуда он появился - если вам все равно, является ли он локальным или глобальным, просто хотите знать, является ли он существует.
ToolmakerSteve
@ToolmakerSteve - на самом деле я имел в виду потенциально значительные накладные расходы на вызов get_defined_vars. Смотрите здесь .
nzn
1

Следующий код, написанный как расширение PHP, эквивалентен array_key_exists ($ name, get_defined_vars ()) (спасибо Хенрику и Ханнесу).

// get_defined_vars()
// https://github.com/php/php-src/blob/master/Zend/zend_builtin_functions.c#L1777
// array_key_exists
// https://github.com/php/php-src/blob/master/ext/standard/array.c#L4393

PHP_FUNCTION(is_defined_var)
{

    char *name;
    int name_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
        return;
    }

    if (!EG(active_symbol_table)) {
        zend_rebuild_symbol_table(TSRMLS_C);
    }

    if (zend_symtable_exists(EG(active_symbol_table), name, name_len + 1)) {
        RETURN_TRUE;
    }

}
масакэластичный
источник
0

Вы можете использовать is_null и empty вместо isset (). Empty не выводит сообщение об ошибке, если переменная не существует.

Равелин
источник
Я использую is_null. Результат один и тот же независимо от формата isset.
Тату Улманен
Я допустил ошибку при публикации своего первого ответа: вы пробовали использовать empty ()?
Равелин
1
Это не сработает для значений, которые не являются пустыми и не NULL, такими как FALSE, 0, array () или "".
teaqu
1
Это неверный ответ. is_nullимеет ту же проблему, что и is_set: он не может различить «не установлено» и «установлено в ноль», что и является проблемой OP. emptyеще хуже, как отмечает Калум.
ToolmakerSteve
0

Вот какой глупый обходной путь с помощью xdebug. ;-)

function is_declared($name) {
    ob_start();
    xdebug_debug_zval($name);
    $content = ob_get_clean();

    return !empty($content);
}

$foo = null;
var_dump(is_declared('foo')); // -> true

$bla = 'bla';
var_dump(is_declared('bla')); // -> true

var_dump(is_declared('bar')); // -> false
Филипп Гербер
источник
1
Не выглядит очень портативным .. :)
Тату Улманен
-3

is_null($bar)возвращает истину, поскольку у него вообще нет значений. В качестве альтернативы вы можете использовать:

if(isset($bar) && is_null($bar)) // returns false

чтобы проверить, $barопределено ли оно, и вернет истину, только если:

$bar = null;
if(isset($bar) && is_null($bar)) // returns true
Руэль
источник
Нет, он сказал, что это if(isset($bar))неверно, когда $bar = null.
Лоик Феврие,
2
Это не будет передавать никакие другие переменные, кроме null (например, if $bar = "test").
Тату Улманен
3
Когда $ bar = null, isset () вернет false, а is_null () вернет true. Ложь и истина всегда дает ложь.
Bartek Kosa
Это совершенно неверный ответ. Как сказал OP, isset($bar)возвращает false даже после $bar = null;.
ToolmakerSteve