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() работает правильно для моего вышеупомянутого варианта использования и для любых глобальных переменных:
<?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.)
Да, был бы полезен хороший пример того, почему вам это действительно нужно.
Я согласен. Насколько вам нужно знать, ни один из них не установлен, потому что оба они равны нулю. Вы не можете использовать их ни для чего.
NULL имеет очень специфическое значение в PHP, и это совершенно отдельная концепция от того, установлена ли переменная или нет.
Интересное наблюдение, я этого не знал. Ответ был найден, так что это избыточно и неверно, но я бы, вероятно, использовал значение-заполнитель или объект для каждой строки, чтобы сделать поиск неустановленного свойства более понятным.
Та же проблема здесь для того же использования (значения db null). Необходимость использовать array_key_exists() не интуитивно понятна. Еще одна капля в мою чашку PHP ...
Я не вижу вариантов использования в отредактированном вопросе или каких-либо ответах и их комментариях, которые фактически оправдывают наличие Переменная, который может существовать или не существовать во время выполнения. Ключи массива и свойства объекта можно рассматривать как переменные и тестировать с помощью isset, но их существование также можно проверить с помощью array_key_exists() и property_exists(), поэтому варианты использования с их участием следует рассматривать отдельно.
Register_globals приводит к глобальным переменным, которые могут существовать или не существовать во время выполнения, особенно в устаревших установках.
@chazomaticus Во-первых, если они NULL, это все равно не имеет значения. Во-вторых, если это так, мы можем списать это на одну из многих ошибок, которые были неправильными в этой функции, которая была полностью удалена из PHP. Обоснование одной плохой идеи другой не убеждает меня в разумном использовании несуществующих переменных.
Да, он был удален из новых версий PHP. К сожалению, это происходит не при каждом развертывании PHP. Кроме того, кажется бессмысленной семантической деталью спорить о том, говорим ли мы об элементах массива или переменных. Независимо от того, каких стандартов, по вашему мнению, должен придерживаться код, полезно знать, как обойти несогласованность в языке PHP.
@chazomaticus Но переменные и элементы массива принципиально разные вещи; просто потому, что вы можете делать с ними то же самое, не означает, что они на 100% взаимозаменяемы. Здесь нет «несоответствия в языке PHP», просто то, что вам случайно не нравится / не понятно. Что касается register_globals, я все еще не могу придумать ситуацию, когда даже это потребовало бы такого различия, поскольку все, что зарегистрировано из HTTP-запроса, всегда будет строкой, а не null.
Настоящая проблема здесь в том, что API баз данных PHP обрабатывают database-NULL эквивалентно PHP-NULL, несмотря на то, что это разные вещи. (PHP-NULL "не инициализирован"; database-NULL "инициализирован как пустой")






Если я запустил следующее:
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;
Таким образом, позже в сценарии я могу безопасно использовать $ foo и знать, что он «установлен» и что по умолчанию он имеет значение null. Позже я могу if ( is_null($foo) ) { /* ... */ }, если мне нужно, и я точно знаю, что переменная существует, даже если она равна нулю.
Полный документация isset читает немного больше, чем то, что было изначально вставлено. Да, он возвращает false для переменной, которая ранее была установлена, но теперь имеет значение null, но также возвращает false, если переменная еще не была установлена (когда-либо) и для любой переменной, которая была помечена как не установленная. Он также отмечает, что нулевой байт ("\ 0") не считается нулевым и вернет истину.
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.
Он получил документы по этой ссылке. Прочтите первое предложение, второй абзац раздела описания по предоставленной вами ссылке. Это именно то, что он процитировал выше.
Это неплохая практика для простых скриптов, но в сложных (например, больших OO) проектах это становится невозможным. Кроме того, как я сказал выше, is_null () возвращает TRUE для переменных, которые не установлены, поэтому на самом деле нет причин делать то, что вы говорите, кроме как избежать предупреждения PHP.
Почему это может быть проблемой для хорошо спроектированных «больших объектно-ориентированных» проектов? Зачем вам вообще $ foo в теле метода, который, возможно, не был установлен до его первого использования?
Если проверяемая вами переменная будет в глобальной области видимости, вы можете:
array_key_exists('v', $GLOBALS)
Ага! СЕЙЧАС ты говоришь! Как бы вы это сделали, скажем, со свойствами класса?
В качестве варианта, если проверка должна работать и для переменных локальной области видимости, on может выполнить $defined_vars = get_defined_vars();, а затем протестировать через array_key_exists('v', $defined_vars);.
Мне это кажется немного уродливым, но в случае, когда вы фактически проверяете элемент массива, это имеет гораздо больший смысл: isset($foo[$bar]) становится array_key_exists($bar, $foo)
property_exists кажется многообещающим, за исключением следующего:> Функция property_exists () не может обнаруживать свойства, которые волшебным образом доступны с помощью магического метода __get.
@alexw Переменные, "созданные" через __get, действительно не существуют. __get - это произвольный код, используемый как резерв для несуществующих переменных, который может возвращать все, что захочет, независимо от того, были ли когда-либо сохранены какие-либо соответствующие данные.
Если у вас есть волшебный метод __get, вы, вероятно, захотите также иметь волшебный метод __isset, который вы могли бы использовать.
Это, вероятно, не имеет отношения к реальности, но ... get_defined_vars() + array_key_exists БУДЕТ НЕТ проверять, действительно ли переменная была объявлен или нет, это просто способ определить, что переменная была "инициализирована" с помощью null ... Просто простое объявление - $a; - допустимый код, хотя этот метод приведет к false :) Но, как я уже сказал, это скорее "забавный факт", который может коснуться нескольких случайных людей C :-)
Я должен сказать, что за все годы программирования на PHP я никогда не сталкивался с проблемой, когда isset() возвращал false для нулевой переменной. OTOH, у меня возникли проблемы с отказом isset() при записи нулевого массива, но array_key_exists() в этом случае работает правильно.
Для некоторого сравнения Icon явно определяет неиспользуемую переменную как возвращающую &null, поэтому вы используете тест is-null в Icon, чтобы также проверить наличие неустановленной переменной. Это действительно облегчает жизнь. С другой стороны, Visual BASIC имеет несколько состояний для переменной, не имеющей значения (Null, Empty, Nothing, ...), и вам часто приходится проверять более одного из них. Известно, что это источник ошибок.
Попробуйте использовать
unset($v)
Кажется, единственный раз, когда переменная не установлена, - это когда она специально не установлена ($ v). Похоже, ваше значение слова «существование» отличается от определения PHP. NULL определенно существует, это NULL.
Я не уверен, что вы имеете в виду. Если у вас есть массив с одним элементом 'a', вам не нужно unset () element 'b', чтобы элемент 'b' не существовал в PHP, он просто не существует. То же самое, например, с глобальные переменные, которые можно рассматривать как элементы массива $ GLOBALS.
Но я согласен с тем, что переменная со значением NULL действительно существует.
Свойства объекта можно проверить на наличие property_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 будут отображаться.
$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 и большинство других языков просто не думают, что есть.
PHP этого не делает, но я бы не сказал, что наиболее на других языках нет. Практически любой язык, в котором объявляются переменные, выдаст ошибку, если переменная не была объявлена, но вы можете установить для них значение NULL. Семантически NULL должно означать «нет ресурса», но отсутствие определения переменной является ошибкой программиста.
@MMiller Конечно, но писать код, который следует по одному пути в случае «отсутствия ресурса» и по другому пути в случае «ошибки программиста», довольно бессмысленно. Если вы хотите обнаружить необъявленные переменные во время отладки, используйте инструмент статического анализа, как если бы вы находили потенциальные ошибки на любом языке.
@MMiller, Круто, как ты вообще об этом подумал.
Теперь, когда я использую JavaScript, я смотрю на это немного по-другому, потому что состояний больше. Если вы скажете alert(x);, когда x никогда не объявлялся, будет выдана справочная ошибка. Если вы сделаете var x; alert(x);, вы получите undefined. undefined - это состояние, отличное от null. Например, многие программы используют хэш параметров, например варианты сценария лайтбокса могут быть такими: var options = { onClose: function () { ... }, centerHorizontally: true }. Если для onClose задано значение null, это может означать, что ничего не делать, тогда как undefined (или отсутствие) подразумевает использование настройки по умолчанию.
@MMiller Как и многие примеры здесь, ваш пример параметров JS является ключом в каком-то конкретном объекте / хэше, а не простой переменной. Таким образом, эквивалентный код в PHP будет использовать array_key_exists или property_exists, а не isset.
@IMSoP Да, я знаю. Этот комментарий был просто ответом на ответ, чтобы указать, что есть цель иметь случай, когда что-то не существует, а не когда оно равно нулю. Я не намеревался сделать свой комментарий ответом на вопрос ОП, а скорее опровержением этого конкретного ответа.
@MMiller Но это не работает как опровержение, потому что утверждение в ответе явно говорит о том, что "переменная не существует", а ваш контрпример касается свойство объекта / хэш-ключ не существует. Различие между этими случаями не случайно.
Ага, ты права, моя ошибка. Хотя то же самое было бы, если бы я сделал var onClose = options.onClose; if (onClose === undefined) { ... } else if (onClose === null) { ... }
@MMiller - это действительно лучший пример. Тем не менее, после более чем 20 лет программирования на строгих языках, ситуации, когда мне нужно было различать undefined и null, настолько редки, что я не упускаю их. ИМХО, основное применение undefined - «ошибка программиста на нестрогом языке». Строго говоря, если мне нужно отдельное состояние для client did not state a value, я объявляю значение, соответствующее ситуации, и проверяю его. В худшем случае придется добавить отдельную флаговую переменную. Но выполнение этого редко лучше, чем необходимость ВСЕГДА справляться с ДВУМЯ разными состояниями, не имеющими значения !!
Думаю, очевидный ответ на все это ... Не инициализируйте свои переменные как NULL, инициализируйте их как нечто относящееся к тому, чем они должны стать.
NULL следует рассматривать как «несуществующее значение», что является значением NULL. Переменная не может быть классифицирована как существующая для PHP, потому что не было сказано, какой тип объекта она пытается быть. Он также может не существовать, поэтому PHP просто говорит: «Хорошо, это не так, потому что в этом нет никакого смысла, а NULL - мой способ сказать это».
Давайте теперь поспорим. «Но NULL - это то же самое, что сказать 0, FALSE или».
Wrong, 0-FALSE- '' все еще классифицируются как пустые значения, но они УКАЗАНЫ как некоторый тип значения или заранее определенный ответ на вопрос. ЛОЖНЫЙ - это ответ на да или нет, '' - это ответ на заголовок, который кто-то прислал, а 0 - это ответ на количество или время и т. д. Они УСТАНАВЛИВАЮТСЯ как некоторый тип ответа / результата, что делает их действительными как установленные.
NULL - это просто не ответ, он не говорит нам «да» или «нет», не сообщает нам время и не сообщает нам, что была отправлена пустая строка. Это основная логика понимания NULL.
Речь идет не о создании дурацких функций, чтобы обойти проблему, а просто о том, чтобы изменить то, как ваш мозг смотрит на NULL. Если он ПУСТО (NULL), предположим, что он ничего не установлен. Если вы предварительно определяете переменные, то предварительно определите их как 0, FALSE или "" в зависимости от типа их использования.
Не стесняйтесь цитировать это. Это не в моей логической голове :)
Отличный ответ. Я столько раз вижу, как люди разглагольствуют о том, как они ненавидят ту или иную особенность языка. Но они, кажется, предполагают, что «если это не МОЙ путь, значит, он сломан». Да, есть плохие дизайнерские решения. Но есть и очень близкие по духу разработчики!
Есть ОГРОМНАЯ разница между неустановленной переменной и переменной === null. Один не существует, другой имеет значение null. Аргументы, что null означает отсутствие значения, просто неверны. Null - это ЗНАЧЕНИЕ типа null. Это совершенно допустимое значение, и у php нет причин рассматривать его как несуществующее значение, что, к сожалению, и делает. Было бы нормально, если бы несуществующие переменные были нулевыми, а каждая существующая переменная не была бы нулевой, и присвоение null переменной отключило бы ее. Но есть МНОГО ситуаций, когда функции возвращают null как фактическое значение. Тогда мы облажались, потому что нет кровавого способа проверить это.
@enrey Если функция возвращает NULL, и вы тестируете это возвращаемое значение, значит, вы его куда-то поместили, поэтому знать указывает, что контейнер существует. За исключением отладки, никогда не должно быть случая, когда переменная - в отличие от ключа массива или свойства объекта - «могла или не могла быть еще установлена».
@IMSoP, что привело вас к этой идее? Разве вы никогда не сталкивались с ситуацией, когда функция находится в середине сложных циклов и условий, и она могла или не могла быть запущена, а возвращаемое значение было / не было присвоено переменной, и тестирование переменной Существование - единственное, что может вам помочь ... Ну, не совсем так, поскольку вы не можете проверить установленную / неустановленную переменную в php, вам пришлось добавить этот $my_function_has_run = true; прямо рядом с вызовом вашей функции. Что ж, это не делает язык менее полным по Тьюрингу. Но это, безусловно, делает его более раздражающим.
@enrey Для меня это очень похоже на отладку. Если эта проверка необходима в окончательном коде, у вас есть довольно дурацкая логика. Помните, что во многих языках переменная либо существует, либо не основана на объявлении в определенной области, поэтому такой тест будет бессмысленным.
@IMSoP true, но php не следует логике других языков. У него есть свой мир, и в этом собственном мире переменная создается при назначении, для нее нет объявления. Кроме того, даже на других языках вы можете проверить наличие переменной. Я не говорю, что равенство null переменной unset - это большая проблема, это всего лишь небольшая проблема в языке, который был построен исключительно из небольших неприятностей. Итак, что угодно. Вернемся к делу.
@enrey См. мой ответ, почему неинициализированная переменная все еще может считаться "существующей". Вы даже можете назначить из него другую переменную, и вы получите null. Было бы гораздо неприятнее иметь вторую константу, представляющую не-нулевую-но-читаемую-из-унитализованной-переменной, что было бы последовательным результатом того, что isset() проводит такое различие.
Я знаю, что мы «не должны» проверять существование переменных в php, черт возьми, нет даже реального способа это проверить. Я не собираюсь писать код, зависящий от него, потому что это невозможно в php. Это ограничение php. Очевидно, что существует разница между ненулевой и ненулевой переменными, но php не позволяет их различать. Тем не менее, многие мета-функции зависят от него внутри: чтение несуществующей переменной var вызывает уведомление, isset($a['x']) сообщит вам false, если x равен нулю, но он будет отображаться в count($a) .. compact будет работать со всеми установленными переменными, включая nulls, и так на.
@enrey Осторожно, еще раз в некоторых ваших примерах используются массивы, для которых у нас есть array_key_exists. Подавляющее большинство функций обрабатывает неинициализированную переменную как равную нулю, поэтому совершенно разумно не различать эти два состояния. Уведомление - это намек на то, что вам следует изменить свой код, чтобы явно инициализировать его для лучшего кода, а не потому, что он имеет какое-либо значение само по себе. Что касается compact, это больше похоже на отражение кода, чем на то, что вы видите в повседневном коде.
Этот ответ ошибочен в одном из основных моментов: в объектно-ориентированном программировании значение null является логическим выбором для обозначения «без объекта». Например, в исключительных обстоятельствах, когда функция может возвращать объект или не возвращать объект, очевидным выбором будет null. Технически в PHP можно использовать false или любое другое значение, которое считается ложным в логическом контексте, но тогда вы теряете некоторую семантическую чистоту. Таким образом, null - это вполне разумное значение для инициализации переменной, которая в конечном итоге должна содержать объект, потому что является имеет отношение к тому, чем она должна стать.
IMSoP, Руководство по PHP фактически дает понять, что неинициализированные переменные могут иметь любое количество типов, в зависимости от контекста, поэтому неправильно говорить «подавляющее большинство функциональных возможностей ...», не зная, что делает код. Кроме того, никто не утверждает, что вы инициализируете переменные не должен перед их использованием. Вопрос просто в том, как это решить, учитывая ситуацию, когда существует некоторая двусмысленность в отношении того, была ли объявлена переменная.
@IMSoP key состоит из 3 символов, isset - из 5 символов, array_key_exists - из 16. Подумайте об этом. Плюс array_key_exists жалуется, если самого массива не существует. Поэтому правильная проверка индекса, который находится внизу огромной древовидной структуры, приведет к появлению большого количества шаблонов, и все это только для проверки существования одного ключа. Опять же, я не говорю, что это непригодно, это просто причиняет ненужную боль. Но мне все равно, я все равно убегаю от php.
@chazomaticus 1) Я никогда не говорил, что null недопустимы; Я сказал, что не существует допустимого использования для «неинициализированный» в качестве значения / состояния, отличного от null. 2) Я раньше не видел этого описания неинициализированных переменных, но обратите внимание, что на практике они постоянно ведут себя так же, как и значение null, принудительное в той же ситуации. Я поддерживаю утверждение, что, за исключением очень эзотерических ситуаций, нет и не должно быть значимого различия между двумя состояниями.
@enrey Я не совсем уверен, какое отношение имеет длина имен функций ко всему, что здесь обсуждается. Аргумент для рекурсивного array_key_exists (то есть того, который выполняет несколько проверок существования, а не проверяет нормальную переменную по конкретному тесту) является интересным; Я думаю, вы можете использовать @ «оператор заткнись», но это всегда похоже на взлом. Однако это все еще не вариант использования функции, которая обнаруживает неинициализированные голые переменные, а не ключи массива.
IMSoP, я думаю, вы запутались. Мой комментарий о null был ответом на этот ответ, а не вы. И повторюсь по поводу вашего утверждения: этого никто не отрицает! Этот разговор вышел далеко за рамки полезного.
@chazomaticus Извините, я неправильно понял этот момент. Это правильное пояснение к ответу, что null может быть допустимым состоянием инициализации. Конечно, тогда это будет константа в коде, поэтому нет необходимости проверять, произошло это или нет во время выполнения.
NULL идеально подходит для установки произвольных переменных, если значение еще не определено. Таким образом, аргумент против установки $ var как NULL нарушен. Так ISSET ()
Этот ответ в корне ошибочен, как показали Энри и другие.
Пока PHP выдает ошибки для неопределенных переменных, но не для null, тогда есть разница. Если null и undefined на самом деле являются одной и той же концепцией, тогда PHP должен принимать значения по умолчанию undefined / undeclared как null и никогда не выдавать ошибку, но никто этого не хочет, потому что это кошмар разработки. Null и undefined могут не отличаться в контексте семантики значений, но они очень разные, когда дело доходит до написания понятного и отлаживаемого кода.
@ChrisMiddleton Совершенно верно! В большинстве языков существует большая разница между null / nil и undefined. Первый - это тип данных, то есть вы можете назначать его переменным (в основном это означает «пустая, но не пустая строка, ноль или ложь»). Последнее является ключевым словом только для чтения, которое принимает значение true в случае, если идентификатор не найден. Разница в том, почему в JavaScript есть два разных ключевых слова, и почему более строгие языки, такие как Basic, не позволяют вам устанавливать для Integer / Booleans / Strings значение Null (только для вариантов или объектов).
Есть множество причин, по которым переменные должны иметь значение NULL. Что, если я получаю данные из базы данных и хочу проверить, существует ли столбец, но имеет ли он значение NULL (что происходит постоянно)?
@HugoZink - ИМХО, вы неправильно поняли обсуждение. Никто не предлагал, чтобы переменные не были равны нулю. Вопрос только в том, нужны ли и когда должны быть ДВЕ разные концепции: null и has never been set to anything, not even to null. Этот пространный ответ призван продемонстрировать, что, когда вам нужно сделать это различие, существует способ некоторый сделать это в php [за исключением ограниченных случаев, когда есть разногласия о том, насколько они важны]. Хотя необходимый код различается в зависимости от того, какой объект вы исследуете.
Я не согласен с вашими рассуждениями о NULL, и говорить, что вам нужно изменить свое отношение к NULL, просто странно.
Я думаю, что isset () был спроектирован неправильно, isset () должен сообщить вам, установлена ли переменная, и не должен беспокоиться о фактическом значении переменной.
Что делать, если вы проверяете значения, возвращаемые из базы данных, и один из столбцов имеет значение NULL, вы все равно хотите знать, существует ли он, даже если значение NULL ... нет, не доверяйте 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 - это просто глупый дизайн
В вашем примере проверяется не наличие переменной, а ключ массива. Решение этой проблемы существует в виде array_key_exists. Вы никогда не должны оказаться в ситуации, когда во время выполнения вы не знаете, существует ли реальная переменная.
@chazomaticus Что ж, вы никогда не должны попадать в ситуацию, когда register_globals включен, поэтому я поддерживаю это утверждение.
О, я согласен. Тем не менее, не все могут контролировать, где развертывается их код. Полезно иметь информацию для каждой ситуации, независимо от того, как все должно быть или нет.
@chazomaticus Если ваша проблема - register_globals, то ваш ответ - не изменение isset(). Руководство по PHP упоминает, что «в целом хорошая практика программирования - сначала инициализировать переменные», что решает проблему register_globals во время разработки, а не во время выполнения. Также существует запись в FAQ, дающий функцию unregister_globals() для работы с ней во время выполнения.
Я предпочитаю использовать not empty как лучший метод для проверки существования переменной, которая a) существует, а b) не равна нулю.
if (!empty($variable)) do_something();
empty() не проверяет, является ли переменная нулевой, он проверяет, является ли она ложной-y, например ни один из "" (пустая строка), 0 (0 как целое число), 0.0 (0 как плавающее), "0" (0 как строка), NULL, FALSE, array() (пустой массив) и $var; (переменная заявлен, но без стоимости). Допустим, у вас есть обязательное поле радио в форме с двумя входами со значениями 0 и 1. Если вы используете empty() для проверки, а пользователь выбирает 0, вы случайно получите ошибку «обязательное поле не может быть пустым». См. Руководство php.net/manual/en/function.empty.phpisset проверяет, установлена ли переменная, и если да, то не равно ли ее значение 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
В качестве дополнения к Greatbigmassive обсуждение того, что означает 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, любой код, который считает это различие равным be значимый использует переменные не так, как они предназначены.
Очень хорошие моменты, хотя и не совсем ответ на вопрос.
На явный вопрос «Как мы должны надежно проверять наличие переменной в PHP?» Я отвечаю: «Нет, и вот почему». И этот ответ, и greatbigmassive также отвечают на вопрос скрытый «Почему isset() так себя ведет?».
«Если вы читаете переменную, в которой в данный момент нет записи, PHP считает, что эта переменная имеет значение NULL». Это неправда. Неопределенная переменная просто не определена. Он может вернуть null, когда вы попытаетесь получить к нему доступ, но это не имеет значения.
@HugoZink К чему это не имеет отношения? Любой тест, который вы сделаете для значение неопределенной переменной, скажет вам, что это значение null. Существует ли это значение до того, как вы посмотрите на него, - это вопрос философов, но с точки зрения любого наблюдаемого поведения значение неизменно является null.
Иногда я немного теряюсь, пытаясь понять, какую операцию сравнения использовать в той или иной ситуации. 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 === nullisset($a)empty($a)$a ? true : falseВсе результаты являются логическими, выводится true, а false опускается.
Можете сами запустить тесты, проверьте суть:
https://gist.github.com/mfdj/8165967
Возможно, это выходит за рамки этого вопроса, но вы можете добавить "0" в таблицу для полноты и ясности работы empty.
Согласно руководству 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
}
Две простые строки кода могут использовать указанную выше функцию, чтобы определить, не определена ли переменная:
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) внутри функции и просто передать переменную функции, но это не работает. Даже если вы попытаетесь использовать «передать по ссылке» переменную в функцию, переменная СТАНОВИТСЯ определенной, и тогда функция никогда не сможет обнаружить, что она ранее не была определена. Здесь представлен компромисс между тем, что я хотел сделать, и тем, что действительно работает.
Изложенное выше подразумевает, что есть еще один способ избежать появления сообщения об ошибке «Неопределенная переменная». (Предполагается, что предотвращение появления такого сообщения - вот почему вы хотите проверить, не определена ли переменная.)
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 инициализируется значением по умолчанию «без ошибок». Определена «функция-обработчик». Если $ testvar (переменная, которую мы хотим знать, является ли она неопределенной) проходит предварительную проверку функции empty (), мы проводим более тщательную проверку. Мы вызываем функцию set_error_handler (), чтобы использовать ранее определенную функцию-обработчик. Затем мы выполняем простое сравнение идентичности с использованием $ testvar, КОТОРОЕ, ЕСЛИ НЕОПРЕДЕЛЕННО, ВЫЗОВИТ ОШИБКУ. Функция обработчика фиксирует ошибку и, в частности, проверяет, является ли причина ошибки тем, что переменная не определена. Результат помещается в переменную информации об ошибке $ er, которую мы можем позже протестировать, чтобы делать все, что захотим, в результате того, что мы точно знаем, определена ли $ testvar. Поскольку нам нужна только функция-обработчик для этой ограниченной цели, мы восстанавливаем исходную функцию обработки ошибок. Функцию myHndlr нужно объявить только один раз; другой код можно скопировать в любое подходящее место для $ testvar или любой другой переменной, которую мы хотим протестировать таким образом.
Если намерение состоит в том, чтобы избежать предупреждения о том, что ваши переменные не были объявлены, решение состоит в том, чтобы исправить ваш код, чтобы правильно объявить их. Ваша функция inst в основном похожа на оператор подавления ошибок @: «Я знаю, что делаю здесь что-то не так, но я просто хочу, чтобы это сообщение исчезло, фактически не изменяя работу моего кода каким-либо образом».
С другой стороны, методы обнаружения гениальны, но я по-прежнему твердо убежден в том, что вы никогда не должны использовать их, кроме как для отражения тех самых предупреждающих сообщений, которые они улавливают. (Вероятно, вам следует уточнить, что ваша версия буферизации вывода требует, чтобы error_reporting был установлен на высокий уровень, а display_errors был включен.)
Я собираюсь добавить к этому пару центов. Одна из причин, по которой эта проблема сбивает с толку, заключается в том, что этот сценарий, кажется, возвращает тот же результат с сообщением об ошибке нет при полном:
$a = null;
var_dump($a); // NULL
var_dump($b); // NULL
Из этого результата можно было предположить, что разница между $a = null и отсутствием определения $b - ничто.
Сообщение об ошибке запуска:
NULL
Notice: Undefined variable: b in xxx on line n
NULL
Примечание: он выдал ошибку неопределенной переменной, но выходное значение var_dump по-прежнему равно NULL.
Очевидно, что у PHP есть внутренняя способность различать нулевую переменную и неопределенную переменную. Мне кажется, что для этого должна быть встроенная функция.
Я думаю, что принятый ответ по большей части хорош, но если бы я собирался его реализовать, я бы написал для него оболочку. Как упоминалось ранее в этом ответе, я должен согласиться с тем, что на самом деле я не сталкивался с ситуацией, когда это было проблемой. Кажется, я почти всегда попадаю в сценарий, в котором мои переменные либо установлены и определены, либо нет (undefined, unset, null, blank и т. д.). Нельзя сказать, что подобная ситуация не возникнет в будущем, но, поскольку это кажется довольно уникальной проблемой, я не удивлен, что разработчики PHP не удосужились вставить это.
Предупреждение о неопределенных переменных - это подсказка программисту, что они сделали что-то не так в коде. Вне отладки (для которой существуют инструменты за пределами языка) никогда не должно быть необходимости в программе для обнаружения такого состояния, потому что программист всегда должен знать, какие переменные они объявляют.
Я думаю, что единственное полное решение - это сообщать уведомления с
error_reporting(E_ALL); // Enables E_NOTICE
Но вам нужно будет исправить все уведомления, генерируемые неопределенными переменными, константами, ключами массива, свойствами класса среди прочего. Как только вы это сделаете, вам не придется беспокоиться о разнице между нулевыми и необъявленными переменными, и двусмысленность исчезнет.
Включение уведомление об уведомлении может быть не хорошей альтернативой во всех ситуациях, но есть веские причины для его включения:
Зачем мне исправлять ошибки E_NOTICE?
В моем случае более года работал в проекте без него, но раньше я был осторожен с объявлением переменных, поэтому переход был быстрым.
Единственный способ узнать, определена ли переменная в текущей области ($GLOBALS не заслуживает доверия) - это array_key_exists( 'var_name', get_defined_vars() ).
Я думаю, что это то, что многие говорили раньше, или я ошибаюсь?
Вы не можете проверить - но зачем вам это нужно?