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

Из документов isset() :

 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 для неустановленных переменных! Также представляется, что:

 @($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 .

Во- вторых, ответ array_key_exists() , array_key_exists() работает правильно, для моего 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 .)

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

 array_key_exists('v', $GLOBALS) 

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

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

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

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

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

Ответ в этом случае заключается в использовании array_key_exists() вместо isset() .

Так как это берет массив для проверки как аргумент функции, PHP все равно будет поднимать «уведомления», если сам массив не существует. В некоторых случаях можно утверждать, что каждое измерение должно быть сначала инициализировано, поэтому уведомление выполняет свою работу. В других случаях функция «рекурсивная» array_key_exists , которая 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 массив $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; с 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's 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. Поэтому, а не isset вы можете назвать его has_been_assigned_to , а не unset , reset_assignment_state .

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

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

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

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

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

 | | ===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

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

 $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 ().

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

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

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

Обработать NULL правильно

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

Аргумент

Давайте теперь спорим. «Но NULL – это как сказать 0 или FALSE или».

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

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

Резюме

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

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

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

Пример из единичного теста:

 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"); } } 

В качестве дополнения к обсуждению большого бигмассива о том, что означает NULL , рассмотрим, что на самом деле означает «существование переменной».

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

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

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

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

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

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

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

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

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

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() вывода 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 

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

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

Что делать, если вы проверяете значения, возвращаемые из базы данных, а один из столбцов имеет значение NULL, вы все равно хотите знать, существует ли он, даже если значение NULL … nope не доверяет 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 – его просто глупый дизайн

I'm going to add a quick two cents to this. One reason this issue is confusing is because this scenario seems to return the same result with error reporting not on full:

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

You could assume from this result that the difference between $a = null and not defining $b at all is nothing.

Crank error reporting up:

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

Note: it threw an undefined variable error, but the output value of var_dump is still NULL .

PHP obviously does have an internal ability to distinguish between a null variable and an undefined variable. It seems to me that there should be a built in function to check for this.

I think the accepted answer is good for the most part, but if I was going to implement it I would write a wrapper for it. As previously mentioned in this answer , I have to agree that I haven't actually encountered a situation where this has been a problem. I seem to almost always end up in a scenario where my variables are either set and defined, or they aren't (undefined, unset, null, blank, etc). Not to say that a situation like this won't occur in future, but as it seems to be quite a unique issue I'm not surprised that the PHP devs haven't bothered to put this in.

If I run the following:

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

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

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

If I run the following:

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

I do not get the error.

If I have a variable that should be set, I usually do something like the following.

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

или

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

That way, later in the script, I can safely use $foo and know that it "is set", and that it defaults to null. Later I can if ( is_null($foo) ) { /* ... */ } if I need to and know for certain that the variable exists, even if it is null.

The full isset documentation reads a little more than just what was initially pasted. Yes, it returns false for a variable that was previously set but is now null, but it also returns false if a variable has not yet been set (ever) and for any variable that has been marked as unset. It also notes that the NULL byte ("\0") is not considered null and will return true.

Determine whether a variable is set.

If a variable has been unset with unset(), it will no longer be set. isset() will return FALSE if testing a variable that has been set to NULL. Also note that a NULL byte ("\0") is not equivalent to the PHP NULL constant.

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

 unset($v) 

It seems the only time a variable is not set is when it is specifically unset($v). It sounds like your meaning of 'existence' is different than PHP's definition. NULL is certainly existing, it is NULL.

I have to say in all my years of PHP programming, I have never encountered a problem with isset() returning false on a null variable. OTOH, I have encountered problems with isset() failing on a null array entry – but array_key_exists() works correctly in that case.

For some comparison, Icon explicitly defines an unused variable as returning &null so you use the is-null test in Icon to also check for an unset variable. This does make things easier. On the other hand, Visual BASIC has multiple states for a variable that doesn't have a value (Null, Empty, Nothing, …), and you often have to check for more than one of them. This is known to be a source of bugs.

According to the PHP Manual for the empty() function, "Determine whether a variable is considered to be empty. A variable is considered empty IF IT DOES NOT EXIST or if its value equals FALSE. empty() does not generate a warning if the variable does not exist." (My emphasis.) That means the empty() function should qualify as the "best way to test a variable's existence in PHP", per the title Question.

However, this is not good enough, because the empty() function can be fooled by a variable that does exist and is set to NULL.

I'm interrupting my earlier answer to present something better, because it is less cumbersome than my original answer (which follows this interruption, for comparing).

  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 } 

Two simple lines of code can use the above function to reveal if a variable is 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 

You can follow those two lines with anything appropriate, such as this example:

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

I wanted to put the call to ob_start() and the ($testvar===null) inside the function, and simply pass the variable to the function, but it doesn't work. Even if you try to use "pass by reference" of the variable to the function, the variable BECOMES defined, and then the function can never detect that it previously had been undefined. What is presented here is a compromise between what I wanted to do, and what actually works.

The preceding implies that there is another way to always avoid running into the "Undefined variable" error message. (The assumption here is, preventing such a message is why you want to test to see if a variable is undefined.)

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

Just call that function before doing something to your $testvar:

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

The newly-instantiated variable's value is set to null, of course!

(Interruption ends)

So, after some studying and experimenting, here is something guaranteed to work:

  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 } 

The explanation: A variable $er is initialized to a default value of "no error". A "handler function" is defined. If the $testvar (the variable we want to know whether or not is undefined) passes the preliminary empty() function test, then we do the more thorough test. We call the set_error_handler() function to use the previously-defined handler function. Then we do a simple identity-comparison involving $testvar, WHICH IF UNDEFINED WILL TRIGGER AN ERROR. The handler function captures the error and specifically tests to see if the reason for the error is the fact that the variable is undefined. The result is placed in the error-information variable $er, which we can later test to do whatever we want as a result of knowing for sure whether or not $testvar was defined. Because we only need the handler function for this limited purpose, we restore the original error-handling function. The "myHndlr" function only needs to be declared once; the other code can be copied to whatever places are appropriate, for $testvar or any other variable we want to test this way.

I think the only full solution is to report notices with

 error_reporting(E_ALL); // Enables E_NOTICE 

But you will have to fix all the notices generated by undefined variables, constants, array keys, class properties amongst others. Once you have done that you won't have to worry about the difference between null and not declared variables, and the ambiguity dissappears.

Enabling notice reporting might not be a good alternative in all situations, but there are good reasons to enable it:

Why should I fix E_NOTICE errors?

In my case was more than a year working in a proyect without it, but was used to be careful about declaring variables, so it was fast to transition.

THE only way to know if a variable is defined in current scope ( $GLOBALS is not trustworthy) is array_key_exists( 'var_name', get_defined_vars() ) .

I prefer using not empty as the best method to check for the existence of a variable that a) exists, and b) is not null.

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