PHPでの変数の存在をテストする最良の方法。isset()は明らかに壊れています


187

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つは使用例です。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)

これは、存在しない変数とに設定された変数のあいまいさが存在するすべての場所で適切に処理されるためNULLPHPで公式の最も簡単な方法を呼び出しarray_key_exists()て、変数の存在を本当に確認しています

(私が考えることができる他のケースのみがクラスのプロパティでproperty_exists()あり、そのドキュメントによるとarray_key_exists()設定されていないことと設定されていることを適切に区別するという点で同様に機能しNULLます)


確認できませんが、なぜ確認する必要があるのですか?
あまりにも多くのphp

12
NULLはPHPで非常に特殊な意味を持ち、変数が設定されているかどうかとはまったく別の概念です。
chazomaticus 2009年

33
nullと存在しないものを区別する理由はいくつかあります。たとえば、データベーステーブルの行を表すオブジェクトを作成しているとします。行のすべての列に対して、オブジェクトのゲッターメソッドを介してのみアクセス可能なプライベート変数を作成します。列の値がnullであるとします。このゲッターメソッドは、テーブルにそのような列がないか、またはこのオブジェクトにnull値があるかどうかをどのようにして知るのでしょうか。幸い、私の場合、プライベート変数は実際にはプライベート配列のエントリなので、array_key_existsを使用できますが、これは実際の問題です。
Nathan Long

1
はい、それは新しいバージョンのPHPから削除されました。残念ながら、これはPHPのすべてのデプロイメントから消えたわけではありません。また、配列の要素と変数のどちらについて話しているのかを疑問に思うのは、意味のない意味の詳細のようです。コードが準拠すべき標準に関係なく、PHP言語の不整合を回避する方法を知っておくと役に立ちます。
chazomaticus 2013

2
@chazomaticusしかし、変数と配列要素は根本的に異なるものです。あなたがそれらで同じことのいくつかを行うことができるからといって、それらが100%交換可能である、またはそうでなければならないというわけではありません。ここには「PHP言語の不整合」はなく、あなたがたまたま好き/理解していないものだけです。についてregister_globalsは、HTTPリクエストから登録されたものは常に文字列であり、文字列ではないため、そのような区別が必要になる状況を考えるのに苦労していますnull
IMSoP 2013

回答:


97

チェックしている変数がグローバルスコープ内にある場合、次のことができます。

array_key_exists('v', $GLOBALS) 

3
ああ!今、あなたは話しています!たとえば、クラスプロパティに対してどのようにしますか?
chazomaticus 2009年

22
バリエーションとして、チェックがローカルスコープ変数に対しても機能する必要がある場合、onはa $defined_vars = get_defined_vars();を実行してからを介してテストできますarray_key_exists('v', $defined_vars);
Henrik Opel

1
これは少し見苦しいように見えますが、実際に配列要素をチェックしている場合には、それはもっと理にかなっています:にisset($foo[$bar])なるarray_key_exists($bar, $foo)
Arild

property_existsこれを除いて、有望に思われます:> property_exists()関数は、__ getマジックメソッドを使用して魔法のようにアクセスできるプロパティを検出できません。
alexw

@alexw __getによって「作成」された変数は実際には存在しません。__getは、存在しない変数のフォールバックとして使用される任意のコードであり、関連するデータが保存されたかどうかに関係なく、必要なものを返すことができます。
ブリリアント

46

さまざまなディスカッションと回答の概要を説明しようとしています。

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

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

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

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

4a。変数変数($$foo

変数のセットを連想配列に変換する関数とはまったく異なりますが、「変数変数」を使用するほとんどの場合(「この他の変数に基づいて名前が付けられた変数に割り当てる」)、代わりに連想配列を使用するように変更できます。 。

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

連想配列を使用するように変更すると、コードはソリューション1の影響を受けやすくなります。間接的なオブジェクトプロパティアクセス(例$foo->$property_name:)は、ソリューション2で対処できます。

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

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

それでも、少なくともJavaを作成しているわけではありませんね。;)

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

変数の基本のマニュアルページには、この文が含まれています。

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

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

明らかなことは、初期化されていない変数についてそのページで説明されている動作は、値がである変数の動作と同じであるため、それらの動作に実際的な違いはないということですnull。1つの例を選択する$aと、両方と$bこのコードでは整数になり42ます。

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

$b = null;
$b += 42;

(1つ目は、より優れたコードを作成するために、宣言されていない変数に関する通知を表示しますが、コードの実際の実行方法には何の違いもありません。)

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があります。プログラマーの意図は、一度も設定されていないことを検出することでしたが、PHPでは検出を許可していません。some_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定数

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

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

考えた実験2:割り当てカウンター

ラストifが求めていることについての別の考え方は、「何かが割り当てられたの$resultか」というものです。これをの特別な値と考えるのではなく、Perlの「変数の汚染」に少し似た、変数に関する$result「メタデータ」と考えることができます。だから、のではなく、あなたはそれを呼ぶかもしれない、というより、。issethas_been_assigned_tounsetreset_assignment_state

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

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

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


クラスとプロパティに関して、プロパティが配列の場合、残念ながらproperty_exists()は機能しません。例:Class {public $ property = array()}。エラーをスローします。
Andrew

1
@Andrewは私にとってはうまくいくようです:3v4l.org/TnAY5完全な例を提供するように気を付けますか?
IMSoP 2015年

ええ、それは正常に動作するようですが、私のセットアップに問題がありました。誤警報してすみません:)
Andrew

20

特定の状況でどの比較演算を使用するかを理解しようとして少し迷うことがあります。 isset()初期化されていない、または明示的にnull値にのみ適用されます。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


おそらくこの質問の範囲外ですが"0"empty操作を完全かつ明確にするために、表に追加することをお勧めします
Rik Schaaf 2017年

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や他のほとんどの言語では、そのようなことはないと考えています。


3
PHPはサポートしていませんが、他のほとんどの言語ではサポートしていないとは言えません。変数が宣言されていない場合、変数を宣言するほとんどの言語はエラーをスローしますが、に設定できますNULL。意味的にNULLは、「リソースなし」を意味しますが、変数を定義しないと、プログラマーのエラーになります。
Mミラー

1
@MMillerもちろんですが、「リソースがない」場合は1つのパスをたどり、「プログラマーエラー」の場合は別のパスをたどるコードを書くのはかなり無意味です。デバッグ中に宣言されていない変数を検出する場合は、静的分析ツールを使用します。これは、任意の言語で潜在的なエラーを見つけるのと同じです。
IMSoP 2014年

@MMiller、クール、どうやってこれを考えたの?
Pacerier、2015年

1
@MMillerしかし、回答のステートメントは明示的に「存在しない変数」についてであり、あなたの反例は存在しないオブジェクトプロパティ/ハッシュキーに関するものであるため、反論としては機能しません。これらのケースの違いは単なる偶発的なものではありません。
IMSoP 2015

1
@MMiller-確かにそれはより良い例です。それでも、厳格な言語で20年以上プログラミングした後、区別が必要な状況undefinednull非常にまれな状況を見逃すことはありません。私見、主な用途undefinedは「厳密でない言語でのプログラマエラー」です。厳密な言語では、の明確な状態が必要client did not state a valueな場合は、状況に適した値を宣言してテストします。最悪の場合、別のフラグ変数を追加する必要があります。しかし、それをやってすることはめったにするよりも優れているん常に二つの異なる非値の状態にも対応します!
ToolmakerSteve 2017年

15

論理的に考えてNULLを説明する

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

NULLを適切に扱う

NULLは「存在しない値」として扱われるべきです。これはNULLの意味です。この変数は、どのタイプのエンティティーにしようとしているのか知らされていないため、PHPに既存のものとして分類できません。それも存在しないかもしれないので、PHPは「ファイン、それはとにかくそれに意味がなく、NULLは私の言い方だから」というだけです。

引数

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

誤って、0-FALSE- ''はすべて空の値として分類されますが、これらは何らかのタイプの値または質問に対する所定の回答として指定されています。FALSEはい」またはいいえ」に対する答えであり、「」は誰かが提出したタイトルに対する答えであり、0は数量または時間に対する回答などです。これらは、あるタイプの回答/結果として設定され、設定されたものとして有効になります。

NULLは何の答えもないので、「はい」または「いいえ」は通知されず、時間も通知されず、空の文字列が送信されたことも通知されません。これがNULLを理解するための基本的なロジックです。

概要

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

これを自由に引用してください。それは私の論理的な頭の上にあります:)


5
すばらしい答えです。言語のこの機能やその機能がどのように嫌われているのかについて、人々が怒り狂うのを何度も目にします。しかし、彼らは「私のやり方ではうまくいかないと、壊れている」と想定しているようです。はい、悪い設計上の決定があります。しかし、非常に綿密な開発者もいます!
curtisdf

23
未設定の変数とvariable === nullの間には大きな違いがあります。1つは存在せず、もう1つはnull値を持っています。nullの引数は、値が存在しないことを意味し、真ではありません。Nullは、null型の値です。これは完全に有効な値であり、phpが存在しない値として扱う理由はありません。存在しない変数がnullであり、既存のすべての変数がnullではなく、変数にnullを割り当てると、設定が解除されても問題ありません。ただし、関数が実際の値としてnullを返す状況は数多くあります。次に、それをテストするための血まみれの方法がないので、私たちはめちゃくちゃになっています。
エンリー2013

2
phpで変数の存在を確認することは「想定されていない」ことはわかっていますが、実際に確認する方法さえありません。それに依存するコードを記述するつもりはありません。これは、phpでは不可能だからです。これはphpの制限です。非設定変数とnull変数には明らかに違いがありますが、phpにはそれらを区別する方法がありません。しかし、メタ機能の多くは内部的に依存しています。存在しないvarを読み取ると通知isset($a['x'])が生成され、xnullの場合はfalse と表示されcount($a)ますcompactが、..には表示されますnulls
エンレイ2013

3
この答えには1つの大きな欠陥があります。OOプログラミングでは、「オブジェクトなし」を意味する論理的な選択はnullです。たとえば、関数がオブジェクトを返すか、オブジェクトを返さないような例外的な状況では、nullを選択するのが明白です。技術的にはPHPでは、ブールコンテキストでfalseまたはfalseと見なされるその他の値を使用できますが、セマンティックな純粋さが失われます。このように、ヌルは、それがため、それに変数を初期化するために完全に合理的な値は、最終的にオブジェクトを保持すべきである、なることを意図しているものに関連します。
chazomaticus 2013

3
PHPがnullではなく未定義の変数に対してエラーをスローする限り、違いがあります。nullとundefinedが実際に同じ概念である場合、PHPはデフォルトの未定義/未宣言の変数をnullと想定し、エラーをスローすることは決してありませんが、開発の悪夢なので誰もそれを望んでいません。nullとundefinedは、値のセマンティクスのコンテキストでは実際には異なっていない可能性がありますが、明確でデバッグ可能なコードの記述に関しては非常に異なります。
Chris Middleton、

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

4

greatbigmassiveのNULLの意味に関する説明への追加として、「変数の存在」が実際に意味することを考慮してください。

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

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

PHPでは、変数を宣言する必要はありません。必要なときにすぐに使用できるようになります。変数に初めて書き込むとき、PHPはその変数のエントリをメモリに割り当てます。現在エントリがない変数から読み取る場合、PHPはその変数に値があると見なしますNULL

ただし、最初に変数を「初期化」せずに変数を使用すると、自動コード品質検出機能は一般に警告を表示します。第一に、これは、に割り当てるよう誤字、検出に役立つ$thingIdけれどもからの読み出しを$thing_id、しかし、2つ目は、宣言と同じように、その変数が意味を持つスコープを考慮することを強制します。

変数が「存在する」かどうかがその変数のスコープの一部であるかどうかを気にするコードかどうか -が初期化されているかどうかに関係なく、プログラマーとして、コードのその時点でそのラベルの意味を与えています。あなたがそれを使っているので、それはある意味で「存在する」必要があり、存在する場合は暗黙の値を持つ必要があります。PHPでは、その暗黙の値はnullです。

PHPの動作により、既存の変数の名前空間を、ユーザーが意味を与えたラベルのスコープとしてではなく、ある種のKey-Valueストアとして扱うコードを書くことができます。たとえば、次のようなコードを実行できます$var = $_GET['var_name']; $$var = $_GET['var_value'];できるからといって、それが良い考えだとは限りません。

結局のところ、PHPには連想配列と呼ばれる、キーと値のストアを表現するはるかに優れた方法があります。また、配列の値は変数のように扱うことができますが、配列全体に対して操作を実行することもできます。連想配列がある場合、キーを含むかどうかをテストするには、array_key_exists()

プロパティを動的に設定する同様の方法でオブジェクトを使用することもできます。その場合property_exists()、まったく同じ方法で使用できます。もちろん、クラスを定義する場合は、そのクラスが持つプロパティを宣言できますpublicprivateおよびprotectedスコープ。

初期化されていない(または明示的にされている)変数(配列キーまたはオブジェクトプロパティではない)と値がである変数との間には技術的な違いがunset()ありますがnull、その違いを意味あると見なすコード使用することを意図していない方法で変数を使用しています。


1
質問に対する回答ではありませんが、非常に良い点。
chazomaticus 2013

1
「PHP内の変数の存在を確実にチェックするにはどうすればよいですか?」という明確な質問に対して。私の答えは「あなたは違います、そしてここに理由があります」です。この答えとgreatbigmassiveの答えはどちらも、「なぜそのように動作するのか」という暗黙の質問にも答えisset()ます。
IMSoP 2013

「現在エントリがない変数から読み取る場合、PHPはその変数の値がNULLであると見なします。」これは誤りです。未定義の変数は単に未定義です。アクセスしようとするとnullを返す場合がありますが、それは無関係です。
Hugo Zink

@HugoZink何に関係ない?未定義の変数のをテストすると、値がであることがわかりますnull。あなたがそれを見る前にその価値が存在するかどうかは哲学者にとっての問題ですが、観察可能な行動に関する限り、価値は一貫していnullます。
IMSoP 2015年

3

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

2

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

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

データベースから返された値をチェックしていて、列の1つがNULL値を持っている場合、値がNULLであってもそれが存在するかどうかを知りたいのですが...

同様に

$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()に任せない-ただの愚かな設計


あなたの例は、変数の存在ではなく、配列キーの存在をチェックしています。その解決策がの形で存在しarray_key_existsます。実際の変数が存在するかどうかは、実行時にわからない状況であってはなりません。
IMSoP 2013

@chazomaticusまあ、あなたはregister_globalsがオンになっている状況に決しているべきではないので、私はそのステートメントを支持します。
IMSoP 2013

ああ、同意する。それでも、誰もがコードのデプロイ場所を制御できるわけではありません。あるべき姿であるかどうかに関係なく、あらゆる状況に関する情報があると便利です。
chazomaticus 2013

@chazomaticusあなたの問題がregister_globalsである場合、あなたの答えはへの変更ではありませんisset()PHPマニュアルには、「最初に変数を初期化することは、実際には一般にプログラミングの実践として適切である」とありregister_globals、実行時ではなく設計時に解決されます。実行時に処理する機能を提供するFAQエントリもありunregister_globals()ます。
IMSoP 2013

2

これに2セント追加します。この問題が混乱する理由の1つは、このシナリオではエラー報告が完全ではなく、同じ結果が返されるように見えるためです。

$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には明らかに、null変数と未定義変数を区別する内部機能があります。これをチェックするための組み込み関数があるはずです。

受け入れられた答えはほとんどの場合良いと思いますが、もしそれを実装するつもりなら、そのためのラッパーを書くでしょう。この回答で前述たように、これが問題となっている状況には実際には遭遇していないことに同意する必要があります。ほとんどの場合、変数が設定および定義されているか、そうでない場合(未定義、未設定、null、空白など)のシナリオになります。このような状況が将来発生しないことは言うまでもありませんが、非常にユニークな問題であるように思われるので、PHP開発者がこれを実行することに煩わされていないことは驚くことではありません。


未定義の変数に関する警告は、プログラマがコードで何か間違ったことをしたというヒントです。プログラマーは常にどの変数を宣言しているかを知る必要があるため、デバッグ以外(言語の外にツールがあるツール)では、プログラムがこのような状態を検出する必要はありません。
IMSoP 2015

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定数と同等ではないことにも注意してください。


彼はそのリンクからドキュメントを取得しました。あなたが提供したリンクの説明セクションの最初の文、2番目の段落を読んでくださいそれはまさに彼が上で引用したものです。
Zoredache 2009年

これは単純なスクリプトでは悪い習慣ではありませんが、複雑な(たとえば、大規模なOO)プロジェクトでは実行不可能になります。また、上で述べたように、is_null()は設定されていない変数に対してTRUEを返すため、PHPの警告を回避する以外に、言っていることを実行する理由は実際にはありません。
chazomaticus 2009年

1
適切に設計された「大規模なOO」プロジェクトの場合、なぜこれがまったく問題になるのでしょうか。最初の使用の前に設定されていない可能性があるメソッド本体に$ fooがあるのはなぜですか?
ボーシメンセン、

1

使ってみてください

unset($v)

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


どういう意味かわかりません。1つの要素 'a'の配列がある場合、要素 'b'をPHPに存在させないために要素 'b'をunset()する必要はありません。存在しないだけです。$ GLOBALS配列の要素と考えることができるグローバル変数などと同じです。
chazomaticus 2009年

1
しかし、私はNULL値を持つ変数が実際に存在することに同意します。
chazomaticus 2009年

0

私は長年のPHPプログラミングのすべてにおいてisset()、null変数でfalse を返すことで問題に遭遇したことはありません。OTOH、isset()ヌル配列エントリで失敗する問題が発生しました-しかしarray_key_exists()その場合正しく動作します。

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


0

empty()関数のPHPマニュアルによると、「変数が空であると見なされるかどうかを決定します。存在しない場合、またはその値がFALSEの場合、変数は空であると見なされます。empty()は、変数は存在しません。」(私の強調点)つまり、empty()関数は、タイトルの質問に従って、「PHPでの変数の存在をテストするための最良の方法」としての資格を持つ必要があります。

ただし、存在していてNULLに設定されている変数によってempty()関数が騙される可能性があるため、これでは十分ではありません。

以前の回答を中断して、より良いものを提示します。これは、元の回答(比較のために、この中断に続く)よりも扱いにくいためです。

  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
  }

上記の関数を使用して、2つの単純なコード行で、変数が未定義かどうかを明らかにできます。

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

これらの2行は、次の例のように、適切なもので続けることができます。

    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

もちろん、新しくインスタンス化された変数の値はnullに設定されます。

(中断終了)

だから、いくつかの研究と実験の後、これが動作することが保証されているものです:

 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を含む単純なID比較を行います。未定義の場合、エラーがトリガーされます。ハンドラー関数はエラーをキャプチャし、特にテストして、エラーの原因が変数が未定義であるかどうかを確認します。結果はエラー情報変数$ erに格納されます。後で$ testvarが定義されているかどうかを確認した結果、必要な処理を実行するために後でテストできます。この限られた目的のためにハンドラー関数だけが必要なので、元のエラー処理関数を復元します。「myHndlr」関数は一度だけ宣言する必要があります。他のコードは、$ testvarまたはこの方法でテストしたい他の変数の適切な場所にコピーできます。


1
変数が宣言されていないという警告を回避することを意図している場合、解決策は、変数を適切に宣言するようにコードを修正することです。あなたのinst関数は基本的には@エラー抑制演算子に似ています。「ここで何か問題が発生していることはわかっていますが、コードの操作を実際に変更せずに、そのメッセージを表示したくないだけです」。
IMSoP 2014年

一方、検出方法は独創的ですが、ユーザーがキャッチしている非常に警告メッセージをエコーする以外にそれらを使用するべきではないと、私は確信しています。(おそらく、出力バッファリングバージョンでは、error_reportingを高く設定し、display_errorsをオンにする必要があることを明確にする必要があります。)
IMSoP

0

私は唯一の完全なソリューションをすることだと思うの通知を報告して

error_reporting(E_ALL); // Enables E_NOTICE

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

有効にする通知の報告はすべての状況で良い代替することが、それを有効にするには十分な理由がありますしない場合があります。

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

私の場合、それなしでproyectで1年以上働いていましたが、変数の宣言に注意して使用されていたため、移行が迅速でした。


0

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


1
これは他の多くの人が以前言ったことだと思いますか、それとも私は間違っていますか?
Stephan Vierkant 2017

-1

私は、a)が存在し、b)がnullではない変数の存在を確認する最良の方法として、not emptyを使用することを好みます。

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

2
empty()変数がnullかどうかをチェックせず、false-yかどうかをチェックします。たとえば、""(空の文字列)、0(0は整数)、0.0(0は浮動小数点数)、"0"(0は文字列)NULLFALSEarray()(空の配列)および$var;(宣言された変数ですが、値はありません)。たとえば、値が0との2つの入力を持つフォームに必要な無線フィールドがあるとします1empty()検証に使用し、ユーザーがそれを選択すると、0「必須フィールドを空にすることはできません」という誤ったエラーが発生します。マニュアルphp.net/manual/en/function.empty.phpを
HalilÖzgür12年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.