99

およびコンソートを使用して変数の存在が明示的にチェックされないため、E_NOTICE エラー レベルで実行すると、多くの「xyz is undefined」および「undefined offset」メッセージをスローする古いアプリケーションがいくつかありますisset()

欠落している変数やオフセットに関する通知は命の恩人になる可能性があり、いくつかのマイナーなパフォーマンスの改善が得られる可能性があり、全体的にクリーンな方法になる可能性があるため、それらを E_NOTICE 互換にするために作業することを検討しています。

ただし、何百ものisset() empty()and array_key_exists()s をコードに与えることは好きではありません。価値や意味の面で何も得られずに、肥大化し、読みにくくなります。

E_NOTICE 互換でありながら、過剰な変数チェックなしでコードを構築するにはどうすればよいですか?

4

11 に答える 11

130

興味のある方のために、このトピックを小さな記事に拡張しました。この記事では、以下の情報をより構造化された形式で提供しています。PHP の isset And empty の決定的なガイド


アプリを「E_NOTICE互換」にするだけでなく、全体を再構築することを検討する必要があります。コード内に存在しない変数を定期的に使用しようとするポイントが何百もあるということは、かなり構造の悪いプログラムのように聞こえます。存在しない変数にアクセスしようとすることは決して起こらないはずです。他の言語はコンパイル時にこれに躊躇します。PHP でそれができるからといって、そうするべきだというわけではありません。

これらの警告は、あなたを困らせるためではなく、あなたを助けるためにあります。「存在しないものを操作しようとしています!」という警告が表示された場合 、あなたの反応は「おっと、悪い、できるだけ早く修正させてください」であるはずです。「未定義で問題なく機能する変数」、深刻なエラーにつながる可能性のある正直に間違ったコードとの違いを、他にどのように見分けることができますか? これが、常に、常に、エラー報告を 11 にして開発し、エラーが 1 つもなくなるまでコードをプラグインし続ける理由でもあります。NOTICE発行されます。エラー報告をオフにするのは、情報漏えいを回避し、バグのあるコードに直面した場合でもより良いユーザー エクスペリエンスを提供するために、運用環境専用です。


詳しく説明するには:

issetコードのどこかで常にorが必要になりemptyます。それらの発生を減らす唯一の方法は、変数を適切に初期化することです。状況に応じて、次のようにさまざまな方法があります。

関数の引数:

function foo ($bar, $baz = null) { ... }

$baror が関数内で設定されているかどうかを確認する必要はありません$baz。設定するだけなので、心配する必要があるのは、それらの値がtrueor false(またはその他のもの) に評価されるかどうかだけです。

どこでも通常の変数:

$foo = null;
$bar = $baz = 'default value';

変数を使用するコード ブロックの先頭で変数を初期化します。これは!isset問題を解決し、変数が常に既知のデフォルト値を持つことを保証し、読者に次のコードがどのように機能するかについてのアイデアを与え、それによって一種の自己文書化としても機能します.

配列:

$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);

上記と同じように、配列をデフォルト値で初期化し、実際の値で上書きします。

残りのケースでは、コントローラーによって設定される場合と設定されない場合がある値を出力しているテンプレートを考えてみましょう。チェックする必要があるのは次のとおりです。

<table>
    <?php if (!empty($foo) && is_array($foo)) : ?>
        <?php foreach ($foo as $bar) : ?>
            <tr>...</tr>
        <?php endforeach; ?>
    <?php else : ?>
        <tr><td>No Foo!</td></tr>
    <?php endif; ?>
</table>

を定期的に使用してarray_key_existsいる場合は、その使用目的を評価する必要があります。それが違いを生むのはここだけです:

$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true

ただし、上で述べたように、変数を適切に初期化していれば、キーが存在するかどうかを確認する必要はありません。外部ソースから配列を取得している場合、値は、、、またはそのようなものではない可能性nullが高くなります。つまり、意図に応じてまたはで評価できる値です。配列のキーを定期的に に設定し、それ以外の意味を持たせたい場合、つまり上記の例で の結果が異なり、プログラム ロジックに違いがある場合は、その理由を自問する必要があります。変数の単なる存在は重要ではなく、その値だけが重要であるべきです。キーが/フラグの場合は、次を使用します''0'0'falseissetemptynullfalseissetarray_key_existstruefalsetrueまたはfalse、そうではありませんnullnullこれに対する唯一の例外は、何かを意味したいサードパーティのライブラリですがnull、PHP で検出するのが非常に難しいため、これを行うライブラリをまだ見つけていません。

于 2009-12-25T05:57:10.940 に答える
37

そのための関数を書くだけです。何かのようなもの:

function get_string($array, $index, $default = null) {
    if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
        return get_magic_quotes_gpc() ? stripslashes($value) : $value;
    } else {
        return $default;
    }
}

として使用できます

$username = get_string($_POST, 'username');

get_number()、などの些細なことについても同じことget_boolean()get_array()行います。

于 2009-12-25T05:30:47.857 に答える
13

この問題に対処する最善の方法の 1 つは、クラスを介して GET および POST (COOKIE、SESSION など) 配列の値にアクセスすることだと思います。

これらの配列、宣言__get、および__setメソッドのそれぞれに対してクラスを作成します (オーバーロード)。__get値の名前となる 1 つの引数を受け入れます。このメソッドは、isset()またはを使用して、対応するグローバル配列でこの値をチェックしempty()、値が存在する場合は値を返し、そうでない場合はnull(または他のデフォルト値) を返します。

その後、この方法で自信を持って配列値にアクセスできます。必要に応じて、 s またはs$POST->usernameを使用せずに検証を行います。が対応するグローバル配列に存在しない場合はが返されるため、警告や通知は生成されません。isset()empty()usernamenull

于 2009-12-25T13:40:57.990 に答える
6

array_key_exists()関数を使うのは構わない。実際、私はや(感受性を避けるために取り消し線を引いた) のように将来動作が変わる可能性のあるハック関数に頼るよりも、この特定の関数を使用することを好みます。emptyisset


ただし、これで便利な単純な関数と、配列インデックスを処理する他の状況を使用します。

function Value($array, $key, $default = false)
{
    if (is_array($array) === true)
    {
        settype($key, 'array');

        foreach ($key as $value)
        {
            if (array_key_exists($value, $array) === false)
            {
                return $default;
            }

            $array = $array[$value];
        }

        return $array;
    }

    return $default;
}

次の配列があるとします。

$arr1 = array
(
    'xyz' => 'value'
);

$arr2 = array
(
    'x' => array
    (
        'y' => array
        (
            'z' => 'value',
        ),
    ),
);

配列から「値」を取得するにはどうすればよいですか? 単純:

Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');

すでに 1 次元配列と多次元配列をカバーしていますが、他に何ができるでしょうか?


たとえば、次のコードを見てください。

$url = 'https://stackoverflow.com/questions/1960509';
$domain = parse_url($url);

if (is_array($domain) === true)
{
    if (array_key_exists('host', $domain) === true)
    {
        $domain = $domain['host'];
    }

    else
    {
        $domain = 'N/A';
    }
}
else
{
    $domain = 'N/A';
}

かなり退屈ですね。Value()関数を使用した別のアプローチを次に示します。

$url = 'https://stackoverflow.com/questions/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');

追加の例として、テスト用の関数を取り上げます。RealIP()

$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));

いいですね。;)

于 2009-12-25T07:33:43.547 に答える
3

私はこれらの機能を使用します

function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }

$y = load($x); // null, no notice

// this attitude is both readable and comfortable
if($login=POST("login") and $pass=POST("pass")) { // really =, not ==
  // executes only if both login and pass were in POST
  // stored in $login and $pass variables
  $authorized = $login=="root" && md5($pass)=="f65b2a087755c68586568531ad8288b4";
}
于 2011-07-17T12:59:20.577 に答える
3

君と一緒にいるよ。しかし、PHP 設計者はそれよりももっと悪い間違いを犯しています。値を読み取るためのカスタム関数を定義する以外に、それを回避する方法はありません。

于 2009-12-25T06:04:30.643 に答える
3

null 合体演算子へようこそ(PHP >= 7.0.1):

$field = $_GET['field'] ?? null;

PHP は次のように述べています。

null 合体演算子 (??) は、isset() と組み合わせて 3 項を使用する必要がある一般的なケースのシンタックス シュガーとして追加されました。最初のオペランドが存在し、NULL でない場合はそれを返します。それ以外の場合は、2 番目のオペランドを返します。

于 2018-01-05T11:57:24.393 に答える
1

設定されていない場合は返す関数を作成しfalse、指定されているfalse場合は空の場合に返します。有効な場合、変数を返します。以下のコードに示すように、さらにオプションを追加できます。

<?php
function isset_globals($method, $name, $option = "") {
    if (isset($method[$name])) {    // Check if such a variable
        if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty 
        if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); }    // Check length of string -- used when checking length of textareas
        return ($method[$name]);
    } else { return false; }
}

if (!isset_globals("$_post", "input_name", "empty")) {
    echo "invalid";
} else {
    /* You are safe to access the variable without worrying about errors! */
    echo "you uploaded: " . $_POST["input_name"];
}
?>
于 2014-09-07T19:26:08.113 に答える
0

読みやすさの定義が何であるかはわかりませんが、empty()、isset()、try / throw / catchブロックを適切に使用することは、プロセス全体にとって非常に重要です。

E_NOTICEが$_GETまたは$_POSTからのものである場合は、そのデータが合格する必要がある他のすべてのセキュリティチェックとともに、empty()に対してチェックする必要があります。

外部フィードまたはライブラリからのものである場合は、try/catchでラップする必要があります。

データベースからのものである場合は、$ db_num_rows()またはそれに相当するものを確認する必要があります。

内部変数からのものである場合は、適切に初期化する必要があります。多くの場合、これらのタイプの通知は、失敗時にFALSEを返す関数の戻り値に新しい変数を割り当てることから発生します。これらは、失敗した場合に、コードが処理できる許容可能なデフォルト値を変数に割り当てるか、コードが処理できる例外をスローできるテストでラップする必要があります。

これらのことにより、コードが長くなり、ブロックが追加され、テストが追加されますが、間違いなく追加の価値があると思うという点で、私はあなたに同意しません。

于 2009-12-25T06:35:35.650 に答える
0

ソフトウェアは、神の恵みによって魔法のように動作するわけではありません。欠けているものを期待している場合は、適切に処理する必要があります。

これを無視すると、アプリケーションにセキュリティ ホールが作成される可能性があります。静的言語では、未定義の変数にアクセスすることは不可能です。null の場合、単にアプリケーションをコンパイルまたはクラッシュするわけではありません。

さらに、アプリケーションの保守が不可能になり、予期しないことが起こると気が狂いそうになります。言語の厳密さは必須であり、PHP は設計上、非常に多くの点で間違っています。意識しないと、悪いプログラマーになってしまいます。

于 2009-12-25T11:52:00.393 に答える
-2

@演算子の使用についてはどうですか?

例えば:

if(@$foo) { /* Do something */ }

$foo の「内部」で何が起こるかを制御できないため (たとえば、PHP エラーを含む関数呼び出しの場合)、これは悪いと言うかもしれませんが、変数に対してのみこの手法を使用する場合、これは次のようになります。

if(isset($foo) && $foo) { /* ... */ }
于 2012-08-13T20:13:26.380 に答える