さまざまな議論と回答の概要を説明しようとしています:
使用できるすべての方法を置き換えることができる質問に対する単一の答えはありませんisset
。一部のユースケースは他の機能によって対処されますが、他のユースケースは精査に耐えられないか、コードゴルフを超えた疑わしい価値があります. 「壊れている」または「矛盾している」ということではなく、他の使用例は、isset
に対する の反応null
が論理的な動作である理由を示しています。
実際のユースケース (ソリューション付き)
1.配列キー
配列は、変数のコレクションのように扱うことができunset
ますisset
。ただし、それらは反復、カウントなどできるため、欠損値は、値が である欠損値と同じではありませんnull
。
この場合の答えは、の代わりに使用するarray_key_exists()
isset()
ことです。
これは関数の引数としてチェックする配列を取るため、配列自体が存在しない場合でも、PHP は「通知」を発生させます。場合によっては、各次元が最初に初期化されるべきであると正当に主張できるため、通知はその役割を果たしています。他の場合では、配列の各次元を順番にチェックする「再帰」array_key_exists
関数を使用すると、これを回避できますが、基本的には と同じ@array_key_exists
です。また、値の処理にもやや接していnull
ます。
2. オブジェクトのプロパティ
「オブジェクト指向プログラミング」の伝統的な理論では、カプセル化とポリモーフィズムがオブジェクトの重要な特性です。PHP のようなクラスベースの OOP 実装では、カプセル化されたプロパティがクラス定義の一部として宣言され、アクセス レベル ( public
、protected
、または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
''
true
isset
第二に、値による変数の汚染は、null
これが以前の初期化を上書きする場合にのみ問題になります。初期化されていない変数を で「上書き」するnull
ことは、他の場所のコードが 2 つの状態を区別している場合にのみ問題になるため、この可能性自体は、そのような区別を行うことに対する議論です。
4.get_defined_vars
そしてcompact
や など、PHP でめったに使用されないいくつかの関数を使用するget_defined_vars
とcompact
、変数名を配列内のキーのように扱うことができます。グローバル変数の場合、スーパーグローバル配列$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 ではそれができません。echo
some_test
true
$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
定数
null
PHP は、理論的には、ここで端末値として使用するための特別な定数を提供することができます。おそらく、関数から this を返すことは違法であるか、 に強制されnull
ます。同じことが関数の引数として渡す場合にも当てはまります。これにより、この非常に特殊なケースが少し簡単になりますが、コードをリファクタリングすることを決定するとすぐに (たとえば、内側のループを別の関数に入れるなど)、役に立たなくなります。関数間で定数を渡すことができた場合、 が定数をsome_function
返さないことを保証できないため、ユニバーサル ターミナル値としてはもはや役に立ちません。
この場合、初期化されていない変数を検出するための引数は、その特別な定数の引数に要約されます。コメントを に置き換え、unset($result)
それを とは異なる方法で処理すると、渡すことができない$result = null
「値」が導入され、$result
特定の組み込み関数によって検出されます。
思考実験2:課題カウンター
最後に何を尋ねているかについての別の考え方if
は、「何かが割り当てられました$result
か?」です。これを の特別な値であると考えるのではなく、Perl の「変数汚染」に少し似た、変数に関する$result
「メタデータ」と考えることができます。だから、あなたがそれを呼ぶのではなく、そしてむしろ、.isset
has_been_assigned_to
unset
reset_assignment_state
しかし、もしそうなら、なぜブール値で止まるのですか? テストに合格した回数を知りたい場合はどうしますか。メタデータを整数に単純に拡張してget_assignment_count
、reset_assignment_count
...
明らかに、そのような機能を追加すると、言語の複雑さとパフォーマンスのトレードオフが発生するため、期待される有用性と慎重に比較検討する必要があります。定数と同様very_null
に、非常に狭い状況でのみ有用であり、同様にリファクタリングに耐性があります。
うまくいけば明白な疑問は、通常のコードを使用して明示的に追跡するのではなく、PHP ランタイム エンジンが、そのようなことを追跡することを事前に想定しなければならない理由です。