194

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()。これら3つすべての出力は次のvar_dump()sとおりです。

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

さらに編集:2つのこと。

1 つ、使用例。配列は SQLUPDATEステートメントのデータに変換されます。配列のキーはテーブルの列であり、配列の値は各列に適用される値です。テーブルのどの列にも値を保持できます。これは、配列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ため、変数 の存在を真に確認するために PHP で公式の最も簡単な方法を呼び出しarray_key_exists()ています。

(私が考えることができる唯一の他のケースは、 が存在するクラスプロパティの場合であり、その docsproperty_exists()によれば、設定されていないことと に設定されていることを適切に区別するという点で と同様に機能します。)array_key_exists()NULL

4

17 に答える 17

97

チェックしている変数がグローバルスコープにある場合は、次のように実行できます。

array_key_exists('v', $GLOBALS) 
于 2009-01-06T21:07:07.590 に答える
48

さまざまな議論と回答の概要を説明しようとしています:

使用できるすべての方法を置き換えることができる質問に対する単一の答えはありませんisset一部のユースケースは他の機能によって対処されますが、他のユースケースは精査に耐えられないか、コードゴルフを超えた疑わしい価値があります. 「壊れている」または「矛盾している」ということではなく、他の使用例は、issetに対する の反応nullが論理的な動作である理由を示しています。

実際のユースケース (ソリューション付き)

1.配列キー

配列は、変数のコレクションのように扱うことができunsetますisset。ただし、それらは反復、カウントなどできるため、欠損値は、値が である欠損値と同じではありませんnull

この場合の答えは、の代わりに使用するarray_key_exists()isset()ことです。

これは関数の引数としてチェックする配列を取るため、配列自体が存在しない場合でも、PHP は「通知」を発生させます。場合によっては、各次元が最初に初期化されるべきであると正当に主張できるため、通知はその役割を果たしています。他の場合では、配列の各次元を順番にチェックする「再帰」array_key_exists関数を使用すると、これを回避できますが、基本的には と同じ@array_key_existsです。また、値の処理にもやや接していnullます。

2. オブジェクトのプロパティ

「オブジェクト指向プログラミング」の伝統的な理論では、カプセル化とポリモーフィズムがオブジェクトの重要な特性です。PHP のようなクラスベースの OOP 実装では、カプセル化されたプロパティがクラス定義の一部として宣言され、アクセス レベル ( publicprotected、またはprivate) が与えられます。

ただし、PHP では、配列にキーを追加する場合と同様に、オブジェクトにプロパティを動的に追加することもできます。一部の人々は、クラスのないオブジェクト (技術的には、組み込みの のインスタンスで、stdClassメソッドやプライベート機能を持たない) を同様の方法で使用します。連想配列への道。これにより、特定のプロパティが指定されたオブジェクトに追加されているかどうかを関数が知りたい場合があります。

配列キーと同様に、オブジェクトのプロパティをチェックするためのソリューションが言語に含まれていますproperty_exists

議論を伴う、正当化できないユースケース

3. register_globals、およびその他のグローバル名前空間の汚染

このregister_globals機能は、HTTP 要求 (GET および POST パラメーター、および Cookie) の側面によって名前が決定される変数をグローバル スコープに追加しました。これは、バグのある安全でないコードにつながる可能性があるため、2000 年 8 月にリリースされた PHP 4.2以降、デフォルトで無効化され、2012 年 3 月にリリースされた PHP 5.4 で完全に削除されました。ただし、一部のシステムは、この機能が有効またはエミュレートされた状態で実行されている可能性があります。globalキーワードまたは$GLOBALS配列を使用して、他の方法でグローバル名前空間を「汚染」することもできます。

まず、GET、POST、および Cookie の値は常に文字列であり (引き続きから返されます)、セッション内の変数は完全にプログラマーの制御下にある必要があるため、register_globalsそれ自体が予期せず変数を生成する可能性は低いです。null''trueisset

第二に、値による変数の汚染は、nullこれが以前の初期化を上書きする場合にのみ問題になります。初期化されていない変数を で「上書き」するnullことは、他の場所のコードが 2 つの状態を区別している場合にのみ問題になるため、この可能性自体は、そのような区別を行うことに対する議論です。

4.get_defined_varsそしてcompact

や など、PHP でめったに使用されないいくつかの関数を使用するget_defined_varscompact、変数名を配列内のキーのように扱うことができます。グローバル変数の場合、スーパーグローバル配列$GLOBALSは同様のアクセスを可能にし、より一般的です。関連するスコープで変数が定義されていない場合、これらのアクセス方法は異なる動作をします。

これらのメカニズムのいずれかを使用して一連の変数を配列として扱うことを決定したら、通常の配列と同じ操作をすべて実行できます。したがって、1 を参照してください。

これらの関数がどのように動作しようとしているのかを予測するためだけに存在していた機能 (たとえば、「? によって返される配列にキー 'foo' があるget_defined_varsでしょうか?」) は不要です。なぜなら、単に関数を実行して悪影響を与えることなく見つけることができるからです。

4a. 可変変数 ( $$foo)

一連の変数を連想配列に変換する関数とまったく同じではありませんが、「変数変数」 (「この別の変数に基づいて名前が付けられた変数に割り当てる」) を使用するほとんどの場合は、代わりに連想配列を使用するように変更できますし、変更する必要があります。 .

変数名は、基本的に、プログラマーが値に付けるラベルです。実行時に決定する場合、実際にはラベルではなく、キーと値のストアのキーです。より実際には、配列を使用しないと、カウント、反復などの機能が失われます。によって上書きされる可能性があるため、変数をキー値ストアの「外部」に置くことができなくなる可能性もあります$$foo

連想配列を使用するように変更すると、コードは解決策 1 に従います。間接的なオブジェクト プロパティ アクセス (例: $foo->$property_name) は、解決策 2 で対処できます。

5.issetよりもはるかに簡単に入力できますarray_key_exists

これが本当に関係があるかどうかはわかりませんが、はい、PHP の関数名はかなり長く、一貫性がない場合があります。どうやら、以前のバージョンの PHP では関数名の長さをハッシュ キーとして使用していたため、Rasmus は意図的に関数名を次のように作成しhtmlspecialchars、通常とは異なる文字数になるようにしました...

それでも、少なくとも私たちは Java を書いているわけではありませんよね? ;)

6. 初期化されていない変数には型があります

変数の基本に関するマニュアルページには、次のステートメントが含まれています。

初期化されていない変数には、使用されるコンテキストに応じた型のデフォルト値があります

Zend Engine に「初期化されていないが既知の型」という概念があるのか​​、それともステートメントを読みすぎているのかはわかりません。

初期化されていない変数のそのページで説明されている動作は、値が である変数の動作と同じであるため、動作に実質的な違いがないことは明らかですnull。一例を挙げる$a$b、このコードでは と の両方が integer になり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可能性があります。プログラマーの意図は、いつ設定されていないかを検出することでしたが、PHP ではそれができません。echosome_testtrue$result

ただし、このアプローチには他にも問題があり、外側のループを追加すると明らかになります。

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を使用できます。

思考実験1:very_null定数

nullPHP は、理論的には、ここで端末値として使用するための特別な定数を提供することができます。おそらく、関数から this を返すことは違法であるか、 に強制されnullます。同じことが関数の引数として渡す場合にも当てはまります。これにより、この非常に特殊なケースが少し簡単になりますが、コードをリファクタリングすることを決定するとすぐに (たとえば、内側のループを別の関数に入れるなど)、役に立たなくなります。関数間で定数を渡すことができた場合、 が定数をsome_function返さないことを保証できないため、ユニバーサル ターミナル値としてはもはや役に立ちません。

この場合、初期化されていない変数を検出するための引数は、その特別な定数の引数に要約されます。コメントを に置き換え、unset($result)それを とは異なる方法で処理すると、渡すことができない$result = null「値」が導入され、$result特定の組み込み関数によって検出されます。

思考実験2:課題カウンター

最後に何を尋ねているかについての別の考え方ifは、「何かが割り当てられました$resultか?」です。これを の特別な値であると考えるのではなく、Perl の「変数汚染」に少し似た、変数に関する$result「メタデータ」と考えることができます。だから、あなたがそれを呼ぶのではなく、そしてむしろ、.issethas_been_assigned_tounsetreset_assignment_state

しかし、もしそうなら、なぜブール値で止まるのですか? テストに合格した回数を知りたい場合はどうしますか。メタデータを整数に単純に拡張してget_assignment_countreset_assignment_count...

明らかに、そのような機能を追加すると、言語の複雑さとパフォーマンスのトレードオフが発生するため、期待される有用性と慎重に比較検討する必要があります。定数と同様very_nullに、非常に狭い状況でのみ有用であり、同様にリファクタリングに耐性があります。

うまくいけば明白な疑問は、通常のコードを使用して明示的に追跡するのではなく、PHP ランタイム エンジンが、そのようなことを追跡することを事前に想定しなければならない理由です。

于 2013-09-05T21:52:07.150 に答える
17

コンパクトな言語構造を使用して、null 変数の存在をテストできます。存在しない変数は結果に表示されませんが、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() も使用できます。

ところで、個人的には、存在しない変数と null 値を持つ変数との間に意味的な違いがあるペストのような状況は避けたいと思います。PHP や他のほとんどの言語は、存在するとは考えていません。

于 2009-12-28T11:57:53.477 に答える
15

NULLを解説、論理的に考える

これらすべてに対する明白な答えは次のとおりだと思います...変数をNULLとして初期化しないでください。変数が意図するものに関連するものとして初期化してください。

NULL を適切に扱う

NULL は、NULL の意味である「存在しない値」として扱われるべきです。変数は、それがしようとしているエンティティのタイプが通知されていないため、PHP に存在するものとして分類できません。存在しない可能性もあるので、PHP は単に「問題ありません。意味がなく、NULL が私の言い方です」と言うだけです。

引数

今議論しましょう。「しかし、NULL は、0 または FALSE または '' と言うようなものです。

間違い、0-FALSE-'' はすべて空の値として分類されますが、何らかのタイプの値または質問に対する事前に決定された回答として指定されます。FALSEは yes または no に対する答え、''は誰かが提出したタイトルに対する答え、0は数量または時間などに対する答えです。それらは、設定されているものとして有効にするある種の答え/結果として設定されます。

NULL は何の答えにもなりません。イエスかノーかはわかりませんし、時刻もわかりませんし、空白の文字列が送信されたこともわかりません。これが NULL を理解する上での基本的なロジックです。

概要

問題を回避するために奇抜な関数を作成することではなく、脳が NULL を見る方法を変更するだけです。NULL の場合は、何も設定されていないと見なされます。変数を事前に定義している場合は、使用目的のタイプに応じて、変数を 0、FALSE、または "" として事前定義します。

これを自由に引用してください。それは私の論理的な頭のてっぺんから外れています:)

于 2010-03-31T10:13:18.697 に答える
9

オブジェクトのプロパティは、 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");
    }
}
于 2009-01-24T12:35:01.213 に答える
5

NULL が何を意味するかについての greatbigmassive の議論への追加として、「変数の存在」が実際に何を意味するかを検討してください。

多くの言語では、すべての変数を使用する前に明示的に宣言する必要があります。これはその型を決定するかもしれませんが、より重要なことは、そのスコープを宣言することです。変数は、そのスコープ内のどこにでも「存在」し、その外側には存在しません-関数全体であろうと、単一の「ブロック」であろうと。

変数は、そのスコープ内で、プログラマーが選択したラベルに何らかの意味を割り当てます。そのスコープ外では、そのラベルは無意味です (別のスコープで同じラベルを使用するかどうかは基本的に無関係です)。

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

于 2013-09-03T21:11:59.243 に答える
4

isset変数が設定されているかどうかを確認し、設定されている場合はそのが NULL でないかどうかを確認します。後者の部分は (私の意見では) この関数の範囲内ではありません。変数が設定されていないか、明示的に NULL に設定されているため、変数が NULL であるかどうかを判断する適切な回避策はありません。

考えられる解決策の 1 つを次に示します。

$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
于 2013-01-31T15:32:55.250 に答える
2

私はあなたのNULLについての推論に同意しません、そしてあなたがNULLについての考え方を変える必要があると言うのはただ奇妙です。

isset()は正しく設計されていないと思います。isset()は、変数が設定されているかどうかを通知する必要があり、変数の実際の値とは関係ありません。

データベースから返された値をチェックしていて、列の1つに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){....

このように、型をチェックするのはプログラマーに任せ、値がNULLであるため、isset()に任せて、そこにないと想定するのはやめましょう。

于 2011-01-28T15:48:40.510 に答える
2

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.

于 2014-05-22T21:52:04.660 に答える
1

使ってみて

unset($v)

変数が設定されていないのは、変数が明確に設定解除されているとき ($v) だけのようです。「存在」の意味は、PHP の定義とは異なるようです。NULL は確かに存在します。NULL です。

于 2009-01-06T22:17:20.630 に答える
1

次を実行すると:

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) ) { /* ... */ }変数がnullであっても、変数が存在することを確実に知る必要がある場合は、後でできます。

完全なisset ドキュメントは、最初に貼り付けられたものよりも少し多くのことを読みます。はい、以前に設定されたが現在は null である変数に対して false を返しますが、変数がまだ設定されていない (これまでに) 場合、および未設定としてマークされている変数に対しても false を返します。また、NULL バイト ("\0") は null と見なされず、true を返すことにも注意してください。

変数が設定されているかどうかを判断します。

変数が unset() で設定解除された場合、それは設定されなくなります。isset() は、NULL に設定された変数をテストすると FALSE を返します。また、NULL バイト ("\0") は PHP の NULL 定数と同等ではないことに注意してください。

于 2009-01-06T21:00:42.627 に答える
0

唯一の完全な解決策は、通知を報告することだと思います

error_reporting(E_ALL); // Enables E_NOTICE

ただし、未定義の変数、定数、配列キー、クラス プロパティなどによって生成されるすべての通知を修正する必要があります。これが完了すると、null 変数と宣言されていない変数の違いについて心配する必要がなくなり、あいまいさがなくなります。

通知レポートを有効にすることは、すべての状況で適切な代替策ではない可能性がありますが、有効にする十分な理由があります。

なぜ E_NOTICE エラーを修正する必要があるのですか?

私の場合、それなしのプロジェクトで1年以上働いていましたが、変数の宣言に注意することに慣れていたので、移行は速かったです。

于 2015-05-29T17:50:22.480 に答える
0

何年にもわたって PHP プログラミングを行ってきisset()ましたが、null 変数に対して false を返すという問題に遭遇したことは一度もありません。isset()OTOH、 null 配列エントリで失敗するという問題が発生しましたがarray_key_exists()、その場合は正しく動作します。

比較のために、Icon は未使用の変数を返すものとして明示的に定義しているため、Icon&nullの is-null テストを使用して未設定の変数もチェックします。これにより、物事が簡単になります。一方、Visual BASIC では、値を持たない変数に対して複数の状態 (Null、Empty、Nothing など) があり、複数の状態をチェックする必要があることがよくあります。これはバグの原因であることが知られています。

于 2009-01-06T21:50:57.393 に答える
0

変数が現在のスコープで定義されている ($GLOBALS信頼できない) かどうかを知る唯一の方法は、 array_key_exists( 'var_name', get_defined_vars() ).

于 2017-02-01T14:34:35.390 に答える
-1

私は、a)が存在し、b)がnullでない変数の存在をチェックするための最良の方法として、空でないことを使用することを好みます。

if (!empty($variable)) do_something();
于 2011-03-07T22:28:42.133 に答える