スーパーグローバル変数に直接アクセスしない方がいい理由
解決済
回答 4
投稿
- 評価
- クリップ 16
- VIEW 15K+
$_GET, $_POST などの値を使用する場合、よくサンプルとして
$str = isset($_POST['str']) ? $_POST['str'] : '';
のようにスーパーグローバル変数を直接扱っているものを見かけます。
$str = $_POST['str'];
なんて記述は論外ですが、上の三項演算子のモノでも、IDE によっては「スーパーグローバル変数には直接アクセスしないでね。」と警告をクラってしまいます。
少し調べてみたのですが、理由は以下のようでした。
スーパーグローバル変数は、変更可能な変数であるため、直接アクセスすべきではない。
利便性も良いので、filter_input()
を利用することは嫌ではないのですが、警告内容に納得がいっていません。
スーパーグローバル変数に直接アクセスしない方がいい理由とはなんなんでしょうか?
できれば具体的な内容も交えて説明いただけると助かります。
少しとんがった回答でもありがたいです。よろしくお願いいたします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+11
$_GET
などに限らず、グローバル変数を多用することは、コードを密結合にしてしまいます。
たとえば、あるメソッドのユニットテストをするとしたときに、「リクエストパラメータは$request
引数で渡す」としておけば、その$request
さえ差し替えればうまくテストができます。
一方で、$_GET
に依存するままコードを書いていると、$_GET
を差し替えるという荒技を取らなければテストできませんし、さらに「$_GET
を書き換える」ことで動作するようなコードがあった場合、うまく動作しなくなってしまいます。
実際、フレームワークでも、スーパーグローバルをラッピングして、インスタンスメソッドとして実装していることが、よくあります。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+5
maisumakun さんと raccy さんの回答で非常に理解が進みました。
スーパーグローバル変数に直接アクセスしない方がいい理由に関して、以下の通り理解したのでまとめておきます。
書き換え可能なグローバル変数を利用しない
プログラムを疎結合とするため。疎結合であるべき理由はググれ。
filter_input関数を利用する理由
filter_input関数はスーパーグローバル変数のただの代替ではない。
<?php
//test.php?a=a
echo $_GET['a'] ;//a
$_GET['a'] = 'b';
echo $_GET['a'] ;//b
echo filter_input(INPUT_GET, 'a');//a
直接 HTTP header から値を取ってくるため、グローバル変数が変更されても影響を受けない。結果、めっちゃ疎。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+4
推奨されない理由はスーパーグローバル変数だからではなく変更可能なグローバル変数だからだと思われます。これはPHPだけではなく、他のプログラミング言語においてもグローバル変数はなるべく使用すべきでは無いとされています。
グローバル変数が使うべきでは無いという理由は、複数のモジュールやライブラリを呼び出すようなある程度の規模があるプログラムでは管理が難しくなるからです。
実際のアプリケーションを作成するとき、簡単なツールでなければ、機能の増加に伴いプログラムはどんどん大きくなっていきます。やがて一つのソースコードで管理することは不可能になります。そこで行うのはファイルの分割です。それぞれの機能を複数のファイルに分けて管理しやすくします。この時点でグローバル変数の扱いが難しくなります。いま書いているファイルとは別のファイルでグローバル変数を書き替えていた場合はどうなるでしょうか?また、実は名前が被っていてうまく動かなかったらどうなるでしょうか?ファイル間で同じグローバル変数を全く別の用途に使っていれば、きっと予想もしない動作をすることになるでしょう。
具体例を見てみましょう。呼び出す度に1ずつカウントしていく関数f()
を作成し、別のファイルother.phpとして用意したとします。other.phpのドキュメントには「関数f()
は呼び出す度に1から1ずつカウントする関数」と書いておきましょう。
other.php
<?php
$x = 1;
function f() {
global $x;
return $x++;
}
では、メイン側であるmain.phpで使ってみましょう。
main.php
<?php
require_once('other.php');
$x = 2;
var_dump(f());
var_dump($x);
果たしてこれは期待通りの動作でしょうか?main.phpをコーディングしているときにother.phpのソースコードは見ません。参考にするのはother.phpに関する「関数f()
は呼び出す度に1から1ずつカウントする関数」という情報だけです。しかし、実際はグローバル変数$x
の名前が被っているため、カウントは2から始まってしまいますし、$x
も3になってしまっています。このような動作を変数汚染といい、グローバル変数は最も汚染されやすい変数です。
この例は単純なため、一つのファイルにすれば問題ないと思うかも知れません。しかし、実際はもっとたくさんの処理が入るため、適切にファイルを分割しないと難しくなります。ファイルを分割すると、今度はグローバル変数が同じだった場合に問題が起きてしまうため、グローバル変数はコード全体を通してなるべく使うべきではありません。
さらに規模が大きくなると、プログラム全体をモジュール化して、ライブラリとして使用するようになります。よく使うような機能はライブラリとして分離しておくと再利用することも可能ですし、そのようなライブラリが多く公開されていて、誰でも利用できるようになっています。しかし、ライブラリのソースコードを一つ一つ見てから使用するには非現実的です。実際はライブラリのドキュメント、つまり「関数f()
は呼び出す度に1から1ずつカウントする関数」のような情報だけを参考にライブラリを使用することになります。
もし、ライブラリが上のような作りだった場合はどうなるでしょうか?ましてや、自分が作ったのでは無い誰かが作って公開しているライブラリを使う場合はどうなるでしょうか?もはや、安全にグローバル変数を使うことはできません。どのような動作をするのか予測することができなくなり、重大なバグのきっかけとなることでしょう。
実際の所、ライブラリの作者達はそのような危険性を知っているため、行儀の良いライブラリはグローバル変数を使うことが全く無いか、グローバル変数を使う場合は、ライブラリのドキュメントに明記しています。
話は変わってスーパーグローバル変数について考えましょう。$_POST
はPHP開発者であればスーパーグローバル変数であることはわかっているはずです。まさか、それを変更しようなどと言う人はいない…と思いたいところがそうでもありません。例えばPOSTで渡されたファイルを自動的に判断して便利になるように書きかえるようなライブラリを作ったとします。ファイルのエンコードを自動判断して、Shift_JISやEUC-JP等であったら、UTF-8に書きかえておくなんてあると便利だと思いませんか?ライブラリを使っても使わなくても便利なように$_POST
を書きかえておけばさらに便利だと思いませんか?もちんろ、グローバルへ数が汚染されてしまいますので、そのようなことはすべきではありません。しかし、自分が作った物ではないライブラリではそのような行儀が悪い実装をしている可能性があります。むしろ、自分が昔作ったライブラリでそのような行儀が悪い実装をしてしまっているかもしれません。そのようなときに、$_POST
を使おうとしたら、きっと予測ができない動作が起きてしまうかも知れません。
このように、グローバル変数はなるべく使うべきではありません。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
-1
$_GET
や$_POST
などにはユーザからの入力が入っています。
この入力には悪意のあるものが入っているかもしれません。
仮にJavaScriptでチェックしてあっても容易に回避できるので
信用してはいけません。
悪い例として、
$str = isset($_POST['str']) ? $_POST['str'] : '';
で取得した$str
を他の利用者の画面にそのまま表示したとします。
$str
に
<script type="text/javascript">
// 悪意のあるJavaScript
</script>
のようなものが入っていた場合、そのままJavaScriptとして実行されてしまいますので、
<
を<
に変えるなどの処理が必要になります。
以上のような処理が必要であることを気づかせるための警告だと認識しています。
スーパーグローバル変数に直接アクセスしていても、
自分でちゃんとチェックしていれば無視してもいいと思います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.09%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2017/01/29 13:21 編集
もう少し教えてください。
(User Contributed Notes からの抜粋です)
$b = isset($_GET['b']) && is_string($_GET['b']) ? $_GET['b'] : '';
と
$b = (string)filter_input(INPUT_GET, 'b');
は同意義であると認識しています。
この場合、結合レベルは変わらないのではないかと思いますが、スーパーグローバルをラッピングした filter_input() を利用する意味合いはあるのでしょうか?
2017/01/29 13:26
何かしらのライブラリやバグなどで$_GETが書き換わっても、filter_input()は影響しません。つまり、filter_input()の返り値は、他のプログラムから影響されることはありません。
2017/01/29 13:34 編集
ありがとうございました!