参照:変数スコープとは何ですか?どの変数にどこからアクセスできますか?「未定義の変数」エラーとは何ですか?


167

注:これは、PHPで変数スコープを処理するための参照質問です。このパターンの複製として、このパターンに当てはまる多くの質問のいずれかを閉じてください。

PHPの「変数スコープ」とは何ですか?ある.phpファイルの変数は別の.phpファイルからアクセスできますか?なぜ「未定義の変数」エラーが発生するのですか?


1
あなたはこれを題した場合は、「未定義の変数は、」あなたは、負荷のより多くのヒットを取得します:)良い仕事けれども
デール

@デール、実際にはありません。2年間の2kビューは......
Pacerier

7
@Pacerier ...ランダムなコメントを残す適切なタイミングについて?
デール

@Pacerier私はあなたがそのコメントで何を言おうとしているのか本当にわかりません。「……」 ……何?:P
だます

@Dale、今がちょうどいいタイミングです。すごい質問が2年間停滞していましたが、「ステートレス」という単語がGoogleDexに追加された後、そのヒット率は文字通りわずか6か月で3倍になりました。
Pacerier

回答:


188

「変数スコープ」とは何ですか?

変数には、「スコープ」または「アクセス可能な場所」に制限があります。アプリケーションのどこか$foo = 'bar';一度書いたからといって、アプリケーション内のどこ$fooからでも参照できるというわけではありません。変数$fooには有効範囲がある特定のスコープがあり、同じスコープ内のコードのみが変数にアクセスできます。

PHPでスコープはどのように定義されますか?

非常にシンプル:PHPには関数スコープがあります。これは、PHPに存在する唯一の種類のスコープセパレータです。関数内の変数は、その関数内でのみ使用できます。関数外の変数は、関数外のどこでも使用できますが、関数内では使用できません。つまり、PHPには1つの特別なスコープ、つまりグローバルスコープがあります。関数の外部で宣言された変数は、このグローバルスコープ内にあります。

例:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$fooであるグローバル、スコープ$bazにあるローカルスコープの内部myFunc。内部のコードのみmyFuncがにアクセスできます$baz外部の コードのみmyFuncがにアクセスできます$foo。どちらも他にアクセスできません:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;

    echo $foo;  // doesn't work
    echo $baz;  // works
}

echo $foo;  // works
echo $baz;  // doesn't work

スコープと含まれるファイル

ファイル境界はスコープを分離ません

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo;  // works!

include他のコードに適用されるのと同じルールがdコードに適用されfunctionます。スコープの目的で、コードのコピーや貼り付けのようなファイルを含めることを考えるかもしれません:

c.php

<?php

function myFunc() {
    include 'a.php';

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

上記の例でa.phpは、がに含まれていたためmyFunc、内部の変数a.phpはローカル関数スコープのみを持ちます。でグローバルスコープにあるように見えるからとa.phpいって、必ずしもそうであるとは限らないため、実際には、コードが含まれている/実行されているコンテキストによって異なります。

関数とクラス内の関数はどうですか?

すべての新しいfunction宣言は新しいスコープを導入し、それはとても簡単です。

関数内の(匿名)関数

function foo() {
    $foo = 'bar';

    $bar = function () {
        // no access to $foo
        $baz = 'baz';
    };

    // no access to $baz
}

クラス

$foo = 'foo';

class Bar {

    public function baz() {
        // no access to $foo
        $baz = 'baz';
    }

}

// no access to $baz

スコープは何に適していますか?

スコーピングの問題に対処するのは面倒なように思えるかもしれませんが、複雑なアプリケーションを作成するには限られた変数スコープが不可欠です。宣言したすべての変数がアプリケーション内の他のどこからでも利用できる場合、何が何を変更するかを追跡する実際の方法がなく、変数をすべてステップオーバーすることになります。変数に付けることができる$name意味のある名前は非常に多く、変数 " "を複数の場所で使用したい場合があります。アプリでこの一意の変数名を1回しか使用できない場合、変数が一意であり、間違った変数を間違ったコードから変更していないことを確認するために、本当に複雑な命名規則に頼る必要があります。

観察する:

function foo() {
    echo $bar;
}

スコープがない場合、上記の関数は何をしますか?どこ$barから来たの?それはどのような状態ですか?それも初期化されていますか?毎回チェックする必要がありますか?これは維持できません。これにより...

スコープの境界を越える

正しい方法:変数の受け渡し

function foo($bar) {
    echo $bar;
    return 42;
}

変数$barは、関数の引数として明示的にこのスコープに入ります。この関数を見ると、関数の値がどこから来ているのかがわかります。次に、明示的値を返します。呼び出し元は、関数が使用する変数とその戻り値がどこから来るかを知る自信があります。

$baz   = 'baz';
$blarg = foo($baz);

変数のスコープを無名関数に拡張する

$foo = 'bar';

$baz = function () use ($foo) {
    echo $foo;
};

$baz();

無名関数は$foo、その周囲のスコープから明示的に含めます。これはグローバルスコープと同じではないことに注意してください。

間違った方法: global

前に述べたように、グローバルスコープはやや特殊で、関数は明示的に変数をインポートできます。

$foo = 'bar';

function baz() {
    global $foo;
    echo $foo;
    $foo = 'baz';
}

この関数は、グローバル変数を使用および変更します$fooこんなことしないで! (あなたが本当に本当に本当に本当に本当に本当にあなたが何をしているかを知っているのでなければ、そしてそれでも:知らないでください!)

この関数の呼び出し元が見るのはこれだけです:

baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!

この関数に副作用があることを示すものはありませんが、副作用はあります。一部の関数は変更続けいくつかのグローバルな状態を必要とするため、これは非常に簡単に混乱します。関数をステートレスにして、入力にのみ作用し、定義された出力を返すようにしますが、何度も呼び出します。

グローバルスコープはできるだけ使用しないでください。ほとんどの場合、変数をグローバルスコープからローカルスコープに「プル」することはできません。


について間違った方法をglobal言っので、いつ使用すべきglobalか教えてください。そして、(少し)static ..?とは何かを説明してください

@stackには「正しい」方法はありませんglobal。それは常に間違っています。関数パラメータを渡すことは正しいです。staticマニュアルでよく説明されており、スコープとはあまり関係がありません。簡単に言えば、「スコープ付きグローバル変数」と考えることができます。ここでkunststube.net/staticの使用法を少し拡張しています。
だます

私の単純な考えは、php変数がグローバルステータスに値するほど重要である場合、データベースの列に値するということです。多分それはやり過ぎかもしれませんが、私の平凡なプログラミングウィットに適したフールプルーフなアプローチです
Arthur Tarasov

@Arthurそこに展開するものはたくさんあります…ಠ_ಠこれはおそらく私が推奨するアプローチではありません。
だます

@deceze今日ここで起こっているちょっとした議論stackoverflow.com/q/51409392-OPは、重複(ここ)については言及していないと述べていますinclude_onceし、多分require_onceまたどこかに追加する必要がありますが、ただ言って。OPも投票を再開することに投票しました。彼らのポストは特別な場合でしょうか、そしてそれについて何をすべきですか?
Funk Forty Niner、

10

関数のスコープ内で定義された変数に外部からアクセスすることはできませんが、その関数が完了した後でその値を使用できないことを意味するわけではありません。PHPには、static静的メソッドとプロパティを定義するためにオブジェクト指向PHPで広く使用されている有名なキーワードがありますが、static静的変数を定義するために関数内でも使用できることに注意してください。

「静的変数」とは何ですか?

静的変数は、プログラムの実行がこのスコープを離れても値が失われない場合に、関数のスコープで定義された通常の変数とは異なります。次の静的変数の使用例を考えてみましょう。

function countSheep($num) {
 static $counter = 0;
 $counter += $num;
 echo "$counter sheep jumped over fence";
}

countSheep(1);
countSheep(2);
countSheep(3);

結果:

1 sheep jumped over fence
3 sheep jumped over fence
6 sheep jumped over fence

$counterなしで定義した場合static、毎回エコーされる値は$num、関数に渡されるパラメーターと同じになります。を使用staticすると、追加の回避策なしでこの単純なカウンタを構築できます。

静的変数の使用例

  1. 関数への後続の呼び出し間で値を格納します。
  2. paramsとして渡す方法がない(または目的がない)場合に、再帰呼び出しの間に値を格納します。
  3. 通常は一度取得するほうがよい値をキャッシュします。たとえば、サーバー上の不変のファイルを読み取った結果。

トリック

静的変数はローカル関数スコープにのみ存在します。定義されている関数の外部からはアクセスできません。そのため、その関数が次に呼び出されるまで、値が変更されないことを確認できます。

静的変数は、スカラーまたはスカラー式としてのみ定義できます(PHP 5.6以降)。これに他の値を割り当てると、少なくともこの記事が書かれた時点では必然的に失敗につながります。それにもかかわらず、コードの次の行でそれを行うことができます。

function countSheep($num) {
  static $counter = 0;
  $counter += sqrt($num);//imagine we need to take root of our sheep each time
  echo "$counter sheep jumped over fence";
}

結果:

2 sheep jumped over fence
5 sheep jumped over fence
9 sheep jumped over fence

静的関数は、同じクラスのオブジェクトのメソッド間で「共有」されています。次の例を見れば、簡単に理解できます。

class SomeClass {
  public function foo() {
    static $x = 0;
    echo ++$x;
  }
}

$object1 = new SomeClass;
$object2 = new SomeClass;

$object1->foo(); // 1
$object2->foo(); // 2 oops, $object2 uses the same static $x as $object1
$object1->foo(); // 3 now $object1 increments $x
$object2->foo(); // 4 and now his twin brother

これは、同じクラスのオブジェクトでのみ機能します。オブジェクトが異なるクラスからのものである場合(互いに拡張している場合でも)、静的変数の動作は期待どおりです。

静的変数は、関数の呼び出し間で値を保持する唯一の方法ですか?

関数呼び出し間で値を保持する別の方法は、クロージャーを使用することです。クロージャーはPHP 5.3で導入されました。つまり、関数スコープ内の変数のセットへのアクセスを、それらにアクセスする唯一の方法となる別の匿名関数に制限することができます。構造化プログラミングでは、クロージャ変数内にいると、「クラス定数」(値によってクロージャで渡された場合)または「プライベートプロパティ」(参照で渡された場合)などのOOP概念を真似ることができます。

後者では、実際には静的変数の代わりにクロージャーを使用できます。何を使用するかは常に決定する開発者次第ですが、静的変数は再帰を処理するときに間違いなく役立ち、開発者に気付かれるに値することを述べておく必要があります。


2

既存の質問とPHPのマニュアルは、このほとんどの説明に役立つため、質問に対する完全な回答は掲載しません。

しかし逃した一人の被験者は、であったスーパーグローバルなど、一般に使用される$_POST$_GET$_SESSION、等が挙げられる。これらの変数はせずに、任意の範囲で、常に利用可能である配列でglobal宣言。

たとえば、この関数はPHPスクリプトを実行しているユーザーの名前を出力します。変数は問題なく関数で使用できます。

<?php
function test() {
    echo $_ENV["user"];
}

「グローバルは悪い」という一般的なルールは、通常、PHPで「グローバルは悪いがスーパーグローバルは大丈夫」に改正されています。(これらの変数はすべて書き込み可能であるため、本当にひどい場合、依存関係の注入を回避するために使用できます。)

これらの変数が存在することは保証されていません。管理者は、使用してそれらの一部またはすべてを無効にすることができvariables_orderディレクティブではphp.ini、これは一般的な動作ではありません。


現在のスーパーグローバルのリスト:

  • $GLOBALS -現在のスクリプトのすべてのグローバル変数
  • $_SERVER -サーバーと実行環境に関する情報
  • $_GET -リクエストに使用されるHTTPメソッドに関係なく、URLのクエリ文字列で渡される値
  • $_POST- application/x-www-form-urlencodedまたはmultipart/form-dataMIMEタイプとともにHTTP POSTリクエストで渡される値
  • $_FILESmultipart/form-data-MIMEタイプのHTTP POSTリクエストで渡されたファイル
  • $_COOKIE -現在のリクエストで渡されたCookie
  • $_SESSION -PHPによって内部的に保存されるセッション変数
  • $_REQUEST-の典型的組み合わせ$_GET$_POST、時には$_COOKIES。内容は、のrequest_orderディレクティブによって決定されphp.iniます。
  • $_ENV -現在のスクリプトの環境変数
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.