web-dev-qa-db-ja.com

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

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

PHPの「変数スコープ」とは何ですか?ある.phpファイルの変数は別の.phpファイルからアクセス可能ですか?なぜ"undefined variable"エラーが発生するのですか?

162
deceze

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

変数には、限られた「スコープ」、または「アクセス可能な場所」があります。アプリケーションで$foo = 'bar';を1回somewhereと書いたからといって、$foo fromeverywhereその他のアプリケーション内。変数$fooには有効な特定のスコープがあり、同じスコープ内のコードのみが変数にアクセスできます。

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

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

例:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$fooglobalスコープにあり、$bazlocalmyFunc内のスコープ。 myFunc内のコードのみが$bazにアクセスできます。コードoutsidemyFuncのみが$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!

includedコードには、他のコードに適用されるのと同じルールが適用されます。スコープはfunctionsのみです。スコープの目的で、コードのコピーと貼り付けなどのファイルを含めることを考えることができます。

c.php

<?php

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

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

上記の例では、a.phpmyFunc内に含まれていましたが、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は、関数の引数として明示的にこのスコープに入ります。この関数を見るだけで、それが機能する値がどこから由来するかが明確になります。次に、値を明示的にreturnsします。呼び出し元は、関数が動作する変数と戻り値がどこから来るかを知る自信があります。

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

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

$foo = 'bar';

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

$baz();

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

間違った方法: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?!?!!

この関数が副作用を持っているという兆候はありませんが、まだあります。一部の関数はグローバル状態を変更し、必要とするため、これは非常に簡単に混乱します。関数をstatelessにして、入力に対してのみ動作し、定義された出力を返すようにしたいが、何度呼び出してもかまいません。

可能な限りグローバルスコープを使用しないでください。最も確かに、グローバルスコープからローカルスコープに変数を「プル」しないでください。

175
deceze

関数のスコープ内で定義された変数は外部からアクセスできませんが、その関数が完了した後は値を使用できないという意味ではありません。 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

staticなしで$counterを定義した場合、エコー値は毎回、関数に渡される$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概念を模倣する場合があります。

後者では、実際には静的変数の代わりにクロージャーを使用できます。使用するものは常に開発者が決定しますが、静的変数は再帰で作業する場合に間違いなく有用であり、開発者が気付くに値することに注意する必要があります。

10
alex_edev

既存の質問と PHPマニュアル がこのほとんどを説明するのに素晴らしい仕事をしているので、質問に対する完全な答えを投稿しません。

しかし、見逃された主題の1つは、一般的に使用される$_POST$_GET$_SESSIONなどを含む superglobals です。これらの変数は、常に使用可能な配列です。 、任意のスコープで、global宣言なし。

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

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

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

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


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

  • $GLOBALS-現在のスクリプトのすべてのグローバル変数
  • $_SERVER-サーバーおよび実行環境に関する情報
  • $_GET-要求に使用されるHTTPメソッドに関係なく、URLのクエリ文字列で渡される値
  • $_POST-application/x-www-form-urlencodedまたはmultipart/form-data MIMEタイプのHTTP POSTリクエストで渡される値
  • $_FILES-multipart/form-data MIMEタイプのHTTP POSTリクエストで渡されるファイル
  • $_COOKIE-現在のリクエストで渡されたCookie
  • $_SESSION-PHPによって内部的に保存されたセッション変数
  • $_REQUEST-通常は$_GET$_POSTの組み合わせですが、時には$_COOKIES。コンテンツは、request_orderの-​​ php.iniディレクティブ によって決定されます。
  • $_ENV-現在のスクリプトの環境変数
1
miken32