web-dev-qa-db-ja.com

isset()およびempty()を回避する方法

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

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

ただし、何百ものisset()empty()およびarray_key_exists() sがコードに与える影響は気に入らない。肥大化し、読みにくくなり、価値や意味に関しては何も得られません。

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

96
Pekka 웃

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


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

これらの警告は、helpにあり、あなたを困らせることはありません。警告が表示された場合「存在しないものを使用しようとしています!」、反応はである必要があります、できるだけ早く修正しましょう。」「正常に機能する変数」重大なエラーにつながる可能性のある正直に間違ったコード?これはまた、常に、always、エラー報告 11になった で開発し、単一のNOTICEが発行されます。エラー報告をオフにするのは、情報漏えいを回避し、バグのあるコードに直面した場合でもユーザーエクスペリエンスを向上させるための、本番環境のみです。


詳しく説明するには:

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

関数の引数:

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

$barまたは$bazが関数内で設定されているかどうかをチェックする必要はありません。それらを設定するだけで、値がtrueまたは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'falseになります。またはそのようなもの、つまり、目的に応じてissetまたはemptyで評価できる値。配列キーを定期的にnullに設定し、false以外を意味する場合、つまり上記の例でissetarray_key_existsの結果が異なる場合プログラムロジックを変更する場合は、その理由を自問する必要があります。変数の単なる存在は重要ではなく、その値だけが重要です。キーがtrue/falseフラグである場合、trueではなく、falseまたはnullを使用します。これの唯一の例外は、nullが何かを意味することを望むサードパーティのライブラリですが、nullはPHPこれを行うライブラリを見つけます。

127
deceze

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

_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()などの簡単なものについても同じことを行います。

37
BalusC

この問題に対処する最良の方法の1つは、クラスのGETおよびPOST(COOKIE、SESSIONなど)配列の値にアクセスすることです。

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

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

13
Jamol

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');
_

すでに一次元および多次元の配列がカバーされていますが、他に何ができるでしょうか?


たとえば、次のコードを取り上げます。

_$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() functionを取る

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

きちんとした? ;)

6
Alix Axel

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

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")) // really =, not ==
if($pass=POST("pass"))
if($login=="Admin" && $pass==...) {
  // login and pass are not empty, login is "Admin" and pass is ...
  $authorized = true;
  ...
}
3
Jan Turoň

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

3
vava

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

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

PHPによると:

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

2

設定されていない場合は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"];
}
?>
1
dragonfire

ソフトウェアは神の恵みによって魔法のように動作するわけではありません。不足しているものを期待している場合は、適切に処理する必要があります。無視すると、おそらくアプリケーションにセキュリティホールが作成されます。静的言語では、未定義の変数にアクセスすることは不可能です。nullの場合、アプリケーションを単にコンパイルしたりクラッシュしたりすることはありません。さらに、アプリケーションを維持できなくなり、予期しないことが起こった場合に怒ってしまいます。言語の厳格性は必須であり、PHPは多くの点で設計上間違っています。あなたが知らない場合、それはあなたが悪いプログラマーになります。

0
knoopx

可読性の定義が何であるかはわかりませんが、empty()、isset()、およびtry/throw/catchブロックの適切な使用は、プロセス全体にとって非常に重要です。 E_NOTICEが$ _GETまたは$ _POSTからのものである場合、それらのデータは、そのデータがパスする必要がある他のすべてのセキュリティチェックと一緒にempty()に対してチェックする必要があります。外部フィードまたはライブラリからのものである場合は、try/catchでラップする必要があります。データベースからのものである場合、$ db_num_rows()または同等のものをチェックする必要があります。内部変数からのものである場合は、適切に初期化する必要があります。多くの場合、これらのタイプの通知は、失敗時にFALSEを返す関数の戻り値に新しい変数を割り当てることから発生します。これらは、失敗した場合に変数に許容可能なデフォルト値を割り当てることができるテストでラップする必要がありますコードが処理できること、またはコードが処理できる例外をスローします。これらのことはコードを長くし、ブロックを追加し、テストを追加しますが、間違いなく余分な価値を追加すると思うのであなたに同意しません。

0
Mlutz