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

Из isset() docs:

isset() will return FALSE if testing a variable that has been set to NULL.

В принципе, isset() не проверяет, установлена ​​ли переменная вообще, но установлено ли что-либо, кроме NULL.

Учитывая, что лучший способ проверить наличие переменной? Я попробовал что-то вроде:

if(isset($v) || @is_null($v))

(@ необходимо избегать предупреждения, если $v не задано), но is_null() имеет схожую проблему с isset(): он возвращает TRUE в unset variables! Также представляется, что:

@($v === NULL)

работает точно так же, как @is_null($v), так что тоже.

Как мы должны надежно проверять наличие переменной в PHP?


Изменить: есть явно разница в PHP между переменными, которые не установлены, и переменными, которые установлены на NULL:

<?php
$a = array('b' => NULL);
var_dump($a);

PHP показывает, что $a['b'] существует и имеет значение NULL. Если вы добавите:

var_dump(isset($a['b']));
var_dump(isset($a['c']));

вы можете видеть двусмысленность, о которой я говорю, с помощью функции isset(). Здесь вывод всех трех этих var_dump()s:

array(1) {
  ["b"]=>
  NULL
}
bool(false)
bool(false)

Дальнейшее редактирование: две вещи.

Один, прецедент. Массив, который преобразуется в данные оператора SQL UPDATE, где ключи массива являются столбцами таблицы, а значения массива - значения, которые должны применяться к каждому столбцу. Любой столбец таблицы может содержать значение NULL, означаемое передачей значения NULL в массиве. Вам нужно различать не существующий ключ массива, а значение массива - NULL; что разница между не обновлением значения столбца и обновлением значения столбца до NULL.

Во-вторых, ответ Zoredache, array_key_exists() работает корректно, для моего примера использования и для любых глобальных переменных:

<?php
$a = NULL;
var_dump(array_key_exists('a', $GLOBALS));
var_dump(array_key_exists('b', $GLOBALS));

выходы:

bool(true)
bool(false)

Так как это правильно обрабатывает почти всюду, я вижу, что существует какая-то двусмысленность между переменными, которые не существуют, и переменными, которые установлены на NULL, Я называю array_key_exists() самым простым в PHP способом чтобы действительно проверить наличие переменной.

(Только другой случай, о котором я могу думать, относится к свойствам класса, для которых существует property_exists(), который, согласно своим документам, работает аналогично array_key_exists() тем, что он правильно различает, не устанавливая и устанавливая на NULL.)

Ответ 1

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

array_key_exists('v', $GLOBALS) 

Ответ 2

Попытка дать обзор различных обсуждений и ответов:

Нет единого ответа на вопрос, который может заменить все способы использования isset. Некоторые примеры использования рассматриваются другими функциями, в то время как другие не выдерживают проверки, или имеют сомнительную ценность за пределами гольфа. В других вариантах использования, которые не являются "разбитыми" или "несогласованными", объясняется, почему реакция isset на null является логическим поведением.

Реальные варианты использования (с решениями)

1. Клавиши массива

Массивы можно рассматривать как совокупности переменных, причем unset и isset обрабатывают их так, как если бы они были. Однако, поскольку они могут быть итерированы, подсчитаны и т.д., Недостающее значение не совпадает с значением, значение которого null.

В этом случае ответ будет использовать array_key_exists() вместо isset().

Так как это берет массив для проверки как аргумент функции, PHP все равно будет поднимать "уведомления", если сам массив не существует. В некоторых случаях можно утверждать, что каждое измерение должно быть сначала инициализировано, поэтому уведомление выполняет свою работу. В других случаях функция "рекурсивная" array_key_exists, которая поочередно проверяла каждое измерение массива, избегала бы этого, но в основном была бы такой же, как @array_key_exists. Это также несколько касательно обработки значений null.

2. Свойства объекта

В традиционной теории "объектно-ориентированного программирования" инкапсуляция и полиморфизм являются ключевыми свойствами объектов; в реализации OOP на основе класса, такой как PHP, инкапсулированные свойства объявляются как часть определения класса и заданные уровни доступа (public, protected или private).

Тем не менее, PHP также позволяет динамически добавлять свойства к объекту, например, вы должны использовать ключи для массива, а некоторые люди используют объекты без классов (технически, экземпляры встроенного stdClass, который не имеет методов или частная функциональность) аналогично ассоциативным массивам. Это приводит к ситуациям, когда функция может захотеть узнать, добавлено ли какое-либо конкретное свойство к предоставленному ему объекту.

Как и в случае с ключами массива, решение для проверки свойств объекта включено в язык, называемый достаточно разумным property_exists.

Неоправданные варианты использования, с обсуждением

3. register_globals и другое загрязнение глобального пространства имен

Функция register_globals добавила переменные в глобальную область, имена которой были определены аспектами HTTP-запроса (параметры GET и POST и файлы cookie). Это может привести к ошибочному и небезопасному коду, поэтому по умолчанию он отключен, поскольку PHP 4.2, выпущенный в августе 2000 г. и полностью удален в PHP 5.4, выпущенный в марте 2012 г.. Однако возможно, что некоторые системы все еще работают с включенной или эмулируемой функцией. Также возможно "загрязнять" глобальное пространство имен другими способами, используя ключевое слово global или массив $GLOBALS.

Во-первых, сам register_globals неожиданно создает переменную null, так как значения GET, POST и cookie всегда будут строками (с '', все еще возвращающим true из isset), и переменные в сеансе должны полностью находиться под контролем программиста.

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

4. get_defined_vars и compact

Несколько редко используемых функций в PHP, таких как get_defined_vars и compact, вы можете обрабатывать имена переменных, как если бы они были ключами в массиве. Для глобальных переменных суперглобальный массив $GLOBALS обеспечивает аналогичный доступ и чаще встречается. Эти методы доступа будут вести себя по-разному, если переменная не определена в соответствующей области.

Как только вы решили обработать набор переменных в виде массива с использованием одного из этих механизмов, вы можете делать с ним все те же операции, что и на любом нормальном массиве. Следовательно, см. 1.

Функциональность, которая существовала только для предсказания того, как эти функции будут вести себя (например, "будет ли ключ" foo "в массиве, возвращаемом get_defined_vars?" ) является излишним, поскольку вы можете просто запустить функцию и найти без побочных эффектов.

4а. Переменные переменные ($$foo)

Хотя они не совсем такие же, как функции, которые превращают набор переменных в ассоциативный массив, в большинстве случаев используется "переменные переменные" ( "присваивать к переменной, названной на основе этой другой переменной" ), может и должно быть изменено, чтобы вместо этого использовать ассоциативный массив.

Имя переменной, в основном, является меткой, присвоенной программисту; если вы определяете его во время выполнения, это не действительно метка, а ключ в каком-то хранилище ключей. Более практично, не используя массив, вы теряете способность подсчитывать, итерировать и т.д.; также может оказаться невозможным иметь переменную "снаружи" хранилища значений ключа, поскольку она может быть переписана с помощью $$foo.

После изменения использования ассоциативного массива код будет поддаваться решению 1. Доступ к косвенным объектным свойствам (например, $foo->$property_name) можно решить с помощью решения 2.

5. isset гораздо легче набрать, чем array_key_exists

Я не уверен, что это действительно актуально, но да, имена функций PHP могут быть довольно длинными и непоследовательными иногда. По-видимому, доисторические версии PHP использовали длину имени функции как хэш-ключ, поэтому Rasmus намеренно составлял имена функций, такие как htmlspecialchars, поэтому у них было бы необычное количество символов...

Тем не менее, по крайней мере, мы не пишем Java, а?;)

6. Неинициализированные переменные имеют тип

На странице справочной страницы на основе основных переменных содержится следующее утверждение:

Неинициализированные переменные имеют значение по умолчанию для своего типа в зависимости от контекста, в котором они используются

Я не уверен, есть ли в Zend Engine понятие "неинициализированный, но известный тип", или это слишком много читает в заявлении.

Ясно, что это не имеет никакого практического значения для их поведения, поскольку поведение, описанное на этой странице для неинициализированных переменных, идентично поведению переменной, значение которой null. Чтобы выбрать один пример, оба $a и $b в этом коде заканчиваются как целое число 42:

unset($a);
$a += 42;

$b = null;
$b += 42;

(Первое приведет к уведомлению о необъявленной переменной, чтобы заставить вас писать лучший код, но не имеет никакого значения, как работает код.)

99. Обнаружение, если функция выполнялась

(Сохраняя это последнее, так как он намного длиннее других. Возможно, я отредактирую его позже...)

Рассмотрим следующий код:

$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
    if ( some_test($thing, $test_value) ) {
        $result = some_function($thing);
    }
}
if ( isset($result) ) {
    echo 'The test passed at least once!';
}

Если some_function может возвращать null, существует вероятность того, что echo не будет достигнута, даже если some_test возвращен true. Целью программиста было определить, когда $result никогда не устанавливался, но PHP не позволяет им это делать.

Однако есть и другие проблемы с этим подходом, которые становятся ясными, если вы добавляете внешний цикл:

foreach ( $list_of_tests as $test_value ) {
    // something missing here...
    foreach ( $list_of_things as $thing ) {
        if ( some_test($thing, $test_value) ) {
            $result = some_function($thing);
        }
    }
    if ( isset($result) ) {
        echo 'The test passed at least once!';
    }
}

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

Чтобы исправить это, нам нужно что-то сделать в строке, где я прокомментировал, что что-то не хватает. Наиболее очевидным решением является установка $result на "конечное значение", которое some_function никогда не может вернуться; если это null, тогда остальная часть кода будет работать нормально. Если нет никакого естественного кандидата для конечного значения, потому что some_function имеет чрезвычайно непредсказуемый тип возврата (который, вероятно, является плохим знаком сам по себе), то дополнительное логическое значение, например. $found, можно использовать вместо этого.

Мысленный эксперимент: константа very_null

PHP теоретически может предоставить специальную константу, а также null - для использования здесь в качестве конечного значения; предположительно, было бы незаконным возвращать это из функции или было бы принуждено к null, и то же самое, вероятно, применимо для передачи его в качестве аргумента функции. Это сделало бы этот конкретный случай немного более простым, но как только вы решили перефакторировать код, например, чтобы включить внутренний цикл в отдельную функцию - это станет бесполезным. Если константа может быть передана между функциями, вы не можете гарантировать, что some_function не вернет ее, поэтому она больше не будет полезной в качестве универсального значения терминала.

Аргумент для определения неинициализированных переменных в этом случае сводится к аргументу для этой специальной константы: если вы замените комментарий на unset($result) и относитесь к нему иначе, чем к $result = null, вы вводите "значение" для $result, которые не могут быть переданы и могут быть обнаружены только определенными встроенными функциями.

Мысленный эксперимент два: счетчик присваивания

Еще один способ подумать о том, что спрашивает последний if, - "что-нибудь сделало присвоение $result?"? Вместо того, чтобы рассматривать это как особое значение $result, вы могли бы подумать об этом как о "метаданных" об этой переменной, немного о Perl-переменной "tainting". Поэтому вместо isset вы можете назвать его has_been_assigned_to, а не unset, reset_assignment_state.

Но если так, зачем останавливаться на логическом? Что делать, если вы хотите узнать, сколько раз прошло испытание? вы можете просто расширить свои метаданные до целого числа и иметь get_assignment_count и reset_assignment_count...

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

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

Ответ 3

Иногда я немного теряюсь, пытаясь выяснить, какую операцию сравнения использовать в данной ситуации. isset() применяется только к неинициализированным или явно нулевым значениям. Передача/присвоение значения null - отличный способ гарантировать, что логическое сравнение работает как ожидалось.

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

|           | ===null | is_null | isset | empty | if/else | ternary | count>0 |
| -----     | -----   | -----   | ----- | ----- | -----   | -----   | -----   |
| $a;       | true    | true    |       | true  |         |         |         |
| null      | true    | true    |       | true  |         |         |         |
| []        |         |         | true  | true  |         |         |         |
| 0         |         |         | true  | true  |         |         | true    |
| ""        |         |         | true  | true  |         |         | true    |
| 1         |         |         | true  |       | true    | true    | true    |
| -1        |         |         | true  |       | true    | true    | true    |
| " "       |         |         | true  |       | true    | true    | true    |
| "str"     |         |         | true  |       | true    | true    | true    |
| [0,1]     |         |         | true  |       | true    | true    | true    |
| new Class |         |         | true  |       | true    | true    | true    |

Чтобы подогнать таблицу, я немного сжал эти метки:

  • $a; относится к объявленной, но неназначенной переменной
  • все остальное в первом столбце относится к назначенному значению, например:
    • $a = null;
    • $a = [];
    • $a = 0;
    • ...
  • столбцы относятся к операциям сравнения, например:
    • $a === null
    • isset($a)
    • empty($a)
    • $a ? true : false
    • ...

Все результаты булевы, true печатается и false опущен.

Вы можете запускать тесты самостоятельно, проверьте этот смысл:
https://gist.github.com/mfdj/8165967

Ответ 4

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

$x = null;
$y = 'y';

$r = compact('x', 'y', 'z');
print_r($r);

// Output:
// Array ( 
//  [x] => 
//  [y] => y 
// ) 

В случае вашего примера:

if (compact('v')) {
   // True if $v exists, even when null. 
   // False on var $v; without assignment and when $v does not exist.
}

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

B.t.w. лично я бы избегал ситуаций, подобных чуме, где существует семантическая разница между переменной, не существующей, и переменной, имеющей нулевое значение. PHP и большинство других языков просто не думаю, что есть.

Ответ 5

Объяснение NULL, логическое мышление

Я думаю, очевидный ответ на все это... Не инициализируйте свои переменные как NULL, и сделайте их как что-то относящееся к тому, к чему они предназначены.

Правильно относиться к NULL

NULL следует рассматривать как "несуществующее значение", что означает значение NULL. Переменная не может быть классифицирована как существующая для PHP, потому что ей не было рассказано, какой тип сущности она пытается быть. Возможно, этого не существует, поэтому PHP просто говорит "Хорошо, это не потому, что в этом нет никакого смысла, и NULL - мой способ сказать это".

Аргумент

Теперь спорьте. "Но NULL - это как сказать 0 или FALSE или".

Неверно, 0-FALSE- '' все еще классифицируются как пустые значения, но они ARE указаны как некоторый тип значения или предопределенный ответ на вопрос. ЛОЖЬ - ответ на да или нет, '' - это ответ на заголовок, который был отправлен, а 0 - это ответ на количество или время и т.д. Они заданы как некоторый тип ответа/результата, который делает их действительными как заданные.

NULL просто не отвечает, что так всегда, он не говорит нам "да" или "нет", и это не говорит нам о времени, и это не говорит нам, что пустая строка была отправлена. Это основная логика в понимании NULL.

Резюме

Это не о создании сумасшедших функций, чтобы обойти проблему, она просто меняет то, как ваш мозг смотрит на NULL. Если он NULL, предположите, что он не установлен ни во что. Если вы предварительно определяете переменные, тогда предопределяйте их как 0, FALSE или "" в зависимости от типа использования, которое вы им намереваетесь.

Не стесняйтесь процитировать это. Это от вершины моей логической головы:)

Ответ 6

Свойства объекта можно проверить для существования property_exists

Пример из unit test:

function testPropertiesExist()
{
    $sl =& $this->system_log;
    $props = array('log_id',
                   'type',
                   'message',
                   'username',
                   'ip_address',
                   'date_added');

    foreach($props as $prop) {
        $this->assertTrue(property_exists($sl, $prop),
                           "Property <{$prop}> exists");
    }
}

Ответ 7

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

На многих языках вы должны явно объявлять каждую переменную, прежде чем использовать ее; это может определить его тип, но, что более важно, он заявляет о своем объеме. Переменная "существует" повсюду в своей области действия и нигде вне нее - будь то целая функция или один "блок".

В пределах своей области переменная присваивает некоторый смысл метке, которую вы, программист, выбрали. Вне его сферы действия эта метка бессмысленна (независимо от того, используете ли вы одну и ту же метку в другой области, в основном неактуальна).

В PHP переменные не должны быть объявлены - они оживают, как только они вам понадобятся. Когда вы пишете в переменную в первый раз, PHP выделяет запись в памяти для этой переменной. Если вы читаете переменную, которая в настоящее время не имеет записи, PHP считает, что переменная имеет значение NULL.

Однако автоматические детекторы качества кода обычно предупреждают вас, если вы используете переменную без "инициализации" ее сначала. Во-первых, это помогает обнаружить опечатки, такие как назначение $thingId, но чтение из $thing_id; но, во-вторых, это заставляет вас рассмотреть область, в которой эта переменная имеет значение, точно так же, как декларация.

Любой код, который заботится о том, является ли переменная "существует" частью области действия этой переменной - независимо от того, была ли она инициализирована, вы, как программист, указали, что значение метки в этой точке код. Поскольку вы используете его, он должен в некотором смысле "существовать", и если он существует, он должен иметь неявное значение; в PHP это неявное значение NULL.

Из-за того, как работает PHP, можно написать код, который обрабатывает пространство имен существующих переменных не как область ярлыков, которые вы указали, а как своего рода хранилище значений ключа. Например, вы можете запустить код следующим образом: $var = $_GET['var_name']; $$var = $_GET['var_value'];. Просто потому, что вы можете, это не значит, что это хорошая идея.

Оказывается, PHP имеет гораздо лучший способ представления хранилищ для ключей, называемых ассоциативными массивами. И хотя значения массива можно рассматривать как переменные, вы также можете выполнять операции над массивом в целом. Если у вас есть ассоциативный массив, вы можете проверить, содержит ли он ключ, используя array_key_exists().

Вы также можете использовать объекты аналогичным образом, динамически настраивая свойства, и в этом случае вы можете использовать property_exists() точно так же путь. Конечно, , если вы определяете класс, вы можете объявить, какие свойства у него есть, вы можете выбрать только public, private и protected.

Хотя существует техническая разница между переменной (в отличие от ключа массива или свойства объекта), которая не была инициализирована (или явно явно unset()), и значением, значением которого является NULL, любой код, который считает, что разница имеет смысл, использует переменные так, как они не предназначены для использования.

Ответ 8

isset проверяет, установлена ​​ли переменная, и если да, то является ли ее значение не NULL. Последняя часть (на мой взгляд) не входит в сферу этой функции. Нет подходящего метода обхода, чтобы определить, является ли переменная NULL , потому что она не установлена ​​ или потому, что явно установлено значение NULL.

Вот одно из возможных решений:

$e1 = error_get_last();
$isNULL = is_null(@$x);
$e2 = error_get_last();
$isNOTSET = $e1 != $e2;
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);

// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL:     isNOTSET: 0, isNULL: 1
// when $x = false:    isNOTSET: 0, isNULL: 0

Другим обходным способом является определение вывода get_defined_vars():

$vars = get_defined_vars();
$isNOTSET = !array_key_exists("x", $vars);
$isNULL = $isNOTSET ? true : is_null($x);
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);

// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL:     isNOTSET: 0, isNULL: 1
// when $x = false:    isNOTSET: 0, isNULL: 0

Ответ 9

Я не согласен с вашими рассуждениями о NULL и говорю, что вам нужно изменить свое мышление о NULL, это просто странно.

Я думаю, что isset() не был спроектирован правильно, isset() должен сказать вам, была ли установлена ​​переменная, и это не должно касаться фактического значения переменной.

Что делать, если вы проверяете значения, возвращаемые из базы данных, а один из столбцов имеет значение NULL, вы все равно хотите знать, существует ли он, даже если значение NULL... nope dont trust isset() здесь.

аналогично

$a = array ('test' => 1, 'hello' => NULL);

var_dump(isset($a['test']));   // TRUE
var_dump(isset($a['foo']));    // FALSE
var_dump(isset($a['hello']));  // FALSE

isset() должен был быть разработан таким образом:

if(isset($var) && $var===NULL){....

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

Ответ 10

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

$a = null;
var_dump($a); // NULL
var_dump($b); // NULL

Вы можете предположить из этого результата, что разница между $a = null и не определяющая $b вообще ничего.

Отчет об ошибках:

NULL

Notice: Undefined variable: b in xxx on line n
NULL

Примечание: он отклонил переменную переменной undefined, но выходное значение var_dump все еще NULL.

PHP, очевидно, имеет внутреннюю способность различать нулевую переменную и переменную undefined. Мне кажется, что для проверки должна быть встроенная функция.

Я думаю, что принятый ответ хорош по большей части, но если бы я собирался его реализовать, я бы написал для него обертку. Как уже упоминалось в этом ответе, я должен согласиться с тем, что на самом деле я не столкнулся с ситуацией, когда это было проблемой. Кажется, я почти всегда оказываюсь в сценарии, где мои переменные заданы и определены, или они не являются (undefined, unset, null, blank и т.д.). Не сказать, что подобная ситуация не будет происходить в будущем, но поскольку это кажется совершенно уникальной проблемой, я не удивлен, что разработчики PHP не потрудились включить это.

Ответ 11

Если я запустил следующее:

echo '<?php echo $foo; ?>' | php

Я получаю сообщение об ошибке:

PHP Notice:  Undefined variable: foo in /home/altern8/- on line 1

Если я запустил следующее:

echo '<?php if ( isset($foo) ) { echo $foo; } ?>' | php

Я не получаю ошибку.

Если у меня есть переменная, которая должна быть установлена, я обычно делаю что-то вроде следующего.

$foo = isset($foo) ? $foo : null;

или

if ( ! isset($foo) ) $foo = null;

Таким образом, позже в script, я могу безопасно использовать $foo и знать, что он "установлен" и что по умолчанию он имеет значение null. Позже я могу if ( is_null($foo) ) { /* ... */ }, если мне нужно и точно знать, что переменная существует, даже если она равна нулю.

Полная isset documentation читает чуть больше, чем то, что первоначально было вставлено. Да, он возвращает false для переменной, которая была ранее установлена, но теперь является нулевой, но также возвращает false, если переменная еще не установлена ​​(никогда) и для любой переменной, которая была отмечена как неустановленная. Он также отмечает, что NULL-байт ( "\ 0" ) не считается нулевым и вернет true.

Определите, установлена ​​ли переменная.

Если переменная была отключена unset(), он больше не будет установлен. isset() вернет FALSE, если тестирование переменная, которая была установлена ​​в NULL. Также обратите внимание, что NULL-байт ( "\ 0" ) не эквивалентно PHP NULL константа.

Ответ 12

Попробуйте использовать

unset($v)

Кажется, что единственный раз, когда переменная не установлена, - это когда она специально не установлена ​​($ v). Похоже, что ваш смысл "существования" отличается от определения PHP. NULL, безусловно, существует, он NULL.

Ответ 13

Я должен сказать, что за все годы программирования PHP я никогда не сталкивался с проблемой с isset() возвратом false для нулевой переменной. OTOH, я столкнулся с проблемами с isset() сбой при записи нулевого массива, но array_key_exists() работает корректно в этом случае.

Для некоторого сравнения Icon явно определяет неиспользуемую переменную как возвращающую &null, поэтому вы используете тест is-null в значке, чтобы также проверить нефиксированную переменную. Это облегчает ситуацию. С другой стороны, Visual BASIC имеет несколько состояний для переменной, которая не имеет значения (Null, Empty, Nothing,...), и вам часто приходится проверять более одного из них. Это, как известно, является источником ошибок.

Ответ 14

В соответствии с руководством PHP для функции empty() "Определите, считается ли переменная пустой. Переменная считается пустой, ЕСЛИ НЕ СУЩЕСТВУЕТ, или если ее значение равно FALSE. empty() не генерирует если переменная не существует." (Мой акцент.) Это означает, что функция empty() должна квалифицироваться как "лучший способ проверить существование переменной в PHP" в заголовке "Вопрос".

Однако это недостаточно, потому что функция empty() может быть обманута переменной, которая существует и установлена ​​в NULL.

Я прерываю свой предыдущий ответ, чтобы представить что-то лучше, потому что он менее громоздкий, чем мой первоначальный ответ (который следует за этим прерыванием, для сравнения).

  function undef($dnc) //do not care what we receive
  { $inf=ob_get_contents();             //get the content of the buffer
    ob_end_clean();                     //stop buffering outputs, and empty the buffer
    if($inf>"")                         //if test associated with the call to this function had an output
    { if(false!==strpos($inf, "Undef"); //if the word "Undefined" was part of the output
        return true;                    //tested variable is undefined
    }
    return false;                       //tested variable is not undefined
  }

Две простые строки кода могут использовать указанную выше функцию, чтобы определить, есть ли переменная undefined:

  ob_start();                           //pass all output messages (including errors) to a buffer
  if(undef($testvar===null))            //in this case the variable being tested is $testvar

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

    echo("variable is undefined");
  else
    echo("variable exists, holding some value");

Я хотел поставить вызов ob_start() и ($ testvar === null) внутри функции и просто передать переменную функции, но это не сработает. Даже если вы попытаетесь использовать "передать по ссылке" переменной в функцию, определена переменная BECOMES, а затем функция никогда не сможет обнаружить, что она ранее была undefined. Здесь представлен компромисс между тем, что я хотел сделать, и тем, что на самом деле работает.

Из предыдущего следует, что существует еще один способ избежать ошибки в сообщении об ошибке "Undefined". (Предполагается, что это предупреждение, предотвращающее такое сообщение, поэтому вы хотите проверить, есть ли переменная undefined.)

   function inst(&$v) { return; }  //receive any variable passed by reference; instantiates the undefined

Просто вызовите эту функцию, прежде чем что-то делать с вашим $testvar:

   inst($testvar);                //The function doesn't affect any value of any already-existing variable

Вновь созданное значение переменной установлено равным нулю, конечно!

(Прерывание заканчивается)

Итак, после некоторых исследований и экспериментов, здесь есть что-то гарантированное:

 function myHndlr($en, $es, $ef, $el)
 { global $er;
   $er = (substr($es, 0, 18) == "Undefined variable");
   return;
 }

 $er = false;
 if(empty($testvar))
 { set_error_handler("myHndlr");
   ($testvar === null);
   restore_error_handler();
 }
 if($er)  // will be 1 (true) if the tested variable was not defined.
 { ; //do whatever you think is appropriate to the undefined variable
 }

Объяснение: переменная $er инициализируется значением по умолчанию "no error". Определена "функция обработчика". Если $testvar (переменная, которую мы хотим знать, есть или нет undefined), проходит предварительный тестовый метод empty(), то мы проводим более тщательный тест. Мы вызываем функцию set_error_handler() для использования ранее определенной функции обработчика. Затем мы делаем простое сопоставление идентичности с использованием $testvar, WHICH IF UNDEFINED БУДЕТ ТРИГГЕР ОШИБКА. Функция обработчика захватывает ошибку и, в частности, проверяет, является ли причиной ошибки тот факт, что переменная undefined. Результат помещается в переменную информации об ошибке $er, которую мы можем позже проверить, чтобы делать все, что мы хотим, в результате уверенного в том, был ли определен параметр $testvar. Поскольку нам нужна только функция обработчика для этой ограниченной цели, мы восстанавливаем исходную функцию обработки ошибок. Функция "myHndlr" должна быть объявлена ​​только один раз; другой код можно скопировать в любые места, подходящие для $testvar или любой другой переменной, которую мы хотим протестировать таким образом.

Ответ 15

Я думаю, что единственным полным решением является сообщить уведомления с помощью

error_reporting(E_ALL); // Enables E_NOTICE

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

Включение уведомления о уведомлениях не всегда является хорошей альтернативой во всех ситуациях, но есть веские причины для ее включения:

Почему я должен исправлять ошибки E_NOTICE?

В моем случае прошло более года, работая в proyect без него, но было использовано для осторожности при объявлении переменных, поэтому переход был быстрым.

Ответ 16

Единственный способ узнать, определена ли переменная в текущей области ($GLOBALS не заслуживает доверия) - это array_key_exists( 'var_name', get_defined_vars() ).

Ответ 17

Я предпочитаю использовать не пустой как лучший метод для проверки существования переменной, которая существует a), а b) не является нулевым.

if (!empty($variable)) do_something();