さまざまなディスカッションと回答の概要を説明しようとしています。
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
get_defined_vars
andのように、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_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
定数
PHPは理論的に、null
ここで最終値として使用するための特別な定数を提供できます。おそらく、これを関数から返すことは違法であるか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ランタイムエンジンが、通常のコードを使用して明示的に行うのではなく、そのようなことを追跡することを事前に想定する必要がある理由です。