PHPでのグローバル変数の使い方を詳しく解説

巴扎黑
リリース: 2023-03-07 21:48:02
オリジナル
3438 人が閲覧しました

この記事は、PHP でグローバル変数を使用するいくつかの方法の詳細な分析と紹介です。必要な方は参照してください

はじめに
新しい大規模な PHP プログラムを開発する場合でも、必然的に一部のデータはコードのさまざまな部分で使用する必要があるため、グローバル データを使用します。一般的なグローバル データには、プログラム設定クラス、データベース接続クラス、ユーザー情報などが含まれます。このデータをグローバル データにする方法は数多くありますが、最も一般的に使用される方法は、「global」キーワード宣言を使用することです。これについては、この記事の後半で詳しく説明します。
「global」キーワードを使用してグローバル データを宣言する唯一の欠点は、これが実際には非常に不適切なプログラミング方法であり、グローバル データによってコードが分離されるため、後でプログラムでより大きな問題が発生することがよくあることです。相互にリンクされているため、コードの一部を変更すると、他の部分に問題が発生する可能性があります。したがって、コード内に多くのグローバル変数がある場合、プログラム全体の保守が困難になります。

この記事では、さまざまな手法や設計パターンを通じてこのグローバル変数の問題を防ぐ方法を説明します。もちろん、最初にグローバル データに「global」キーワードを使用する方法とその仕組みを見てみましょう。

グローバル変数と「global」キーワードを使用する
PHP はデフォルトでいくつかの「スーパーグローバル」変数を定義します。これらは自動的にグローバル化され、$_GET や $_REQUEST など、プログラム内のどこからでも呼び出すことができます。通常、これらの変数はデータまたはその他の外部データから取得されますが、これらの変数は基本的に書き込み可能ではないため、通常は使用しても問題は発生しません。

ただし、独自のグローバル変数を使用することもできます。キーワード「global」を使用すると、グローバル データを関数のローカル スコープにインポートできます。 「変数の使用範囲」がわからない場合は、PHP マニュアルの該当する説明を参照してください。
「global」キーワードを使用したデモ例を次に示します:

コードは次のとおりです:

<?php
$my_var = &#39;Hello World&#39;;
test_global();
function test_global() {
    // Now in local scope
    // the $my_var variable doesn&#39;t exist
    // Produces error: "Undefined variable: my_var"
    echo $my_var;
    // Now let&#39;s important the variable
    global $my_var;
    // Works:
    echo $my_var;
}
?>
ログイン後にコピー

上の例でわかるように、「global」キーワードはグローバル変数をインポートするために使用されます。うまく機能し、シンプルであるように見えますが、グローバル データを定義するために「global」キーワードを使用することをなぜ心配するのでしょうか?
ここに 3 つの正当な理由があります:

1. コードの再利用はほぼ不可能です。
関数がグローバル変数に依存している場合、この関数を異なる環境で使用することはほぼ不可能です。もう 1 つの問題は、この関数を抽出して他のコードで使用できないことです。

2. デバッグと問題の解決は非常に困難です。
グローバル変数の追跡は、非グローバル変数の追跡よりもはるかに困難です。グローバル変数は、不明瞭なインクルード ファイルで再定義されている可能性があり、非常に優れたプログラム エディタ (または IDE) を使用していても、問題を発見するまでに数時間かかる場合があります。

3. これらのコードを理解するのは非常に困難です。
グローバル変数がどこから来て何に使われるのかを理解するのは困難です。開発プロセス中はすべてのグローバル変数を知っているかもしれませんが、約 1 年後には、少なくとも一部のグローバル変数を忘れてしまう可能性があります。その時点で、非常に多くのグローバル変数を使用したことを後悔するでしょう。
では、グローバル変数を使用しない場合は何を使用すればよいでしょうか?以下にいくつかの解決策を見てみましょう。
関数パラメーターを使用する
グローバル変数の使用をやめる 1 つの方法は、以下に示すように、単純に変数をパラメーターとして関数に渡すことです:

コードは次のとおりです:

<?php
$var = &#39;Hello World&#39;;
test ($var);
function test($var) {
    echo $var;
}
?>
ログイン後にコピー

グローバル変数 の場合、これは非常に優れた、または優れたソリューションですらありますが、多くの値を渡したい場合はどうすればよいでしょうか?
たとえば、データベース クラス、プログラム設定クラス、ユーザー クラスを使用する場合。私たちのコードでは、これら 3 つのクラスはすべてのコンポーネントで使用されるため、これらをすべてのコンポーネントに渡す必要があります。関数パラメーター メソッドを使用する場合は、これを行う必要があります:

コードは次のとおりです:

<?php
$db = new DBConnection;
$settings = new Settings_XML;
$user = new User;
test($db, $settings, $user);
function test(&$db, &$settings, &$user) {
    // Do something
}
?>
ログイン後にコピー


明らかに、これには価値がありません。追加する新しいオブジェクトができたら、追加する必要があります。各関数に新しいオブジェクトを追加 関数パラメータを 1 つ追加します。したがって、別の方法を使用してそれを解決する必要があります。

シングルトンを使用する 関数パラメーターの問題を解決する 1 つの方法は、シングルトンを使用して関数パラメーターを置き換えることです。シングルトンは、一度だけインスタンス化できるオブジェクトの特別なクラスであり、オブジェクトのインターフェイスを返す静的メソッドが含まれています。次の例は、単純なシングルトンを構築する方法を示しています:

代码如下:

<?php
// Get instance of DBConnection
$db =& DBConnection::getInstance();
// Set user property on object
$db->user = &#39;sa&#39;;
// Set second variable (which points to the same instance)
$second =& DBConnection::getInstance();
// Should print &#39;sa&#39;
echo $second->user;
Class DBConnection {
    var $user;
    function &getInstance() {
        static $me;
        if (is_object($me) == true) {
            return $me;
        }
        $me = new DBConnection;
        return $me;
    }
    function connect() {
        // TODO
    }
    function query() {
        // TODO
    }
}
?>
ログイン後にコピー


上面例子中最重要的部分是函数getInstance()。这个函数通过使用一个静态变量$me来返回这个类的实例,从而确保了只有一个DBConnection类的实例。
使用单件的好处就是我们不需要明确的传递一个对象,而是简单的使用getInstance()方法来获取到这个对象,就好像下面这样:

代码如下:

<?php
function test() {
    $db = DBConnection::getInstance();
    // Do something with the object
}
?>
ログイン後にコピー


然而使用单件也存在一系列的不足。首先,如果我们如何在一个类需要全局化多个对象呢?因为我们使用单件,所以这个不可能的(正如它的名字是单件一样)。另外一个问题,单件不能使用个体测试来测试的,而且这也是完全不可能的,除非你引入所有的堆栈,而这显然是你不想看到的。这也是为什么单件不是我们理想中的解决方法的主要原因。

注册模式
让一些对象能够被我们代码中所有的组件使用到(译者注:全局化对象或者数据)的最好的方法就是使用一个中央容器对象,用它来包含我们所有的对象。通常这种容器对象被人们称为一个注册器。它非常的灵活而且也非常的简单。一个简单的注册器对象就如下所示:

代码如下:

<?php
Class Registry {
    var $_objects = array();
    function set($name, &$object) {
        $this->_objects[$name] =& $object;
    }
    function &get($name) {
        return $this->_objects[$name];
    }
}
?>
ログイン後にコピー

使用注册器对象的第一步就是使用方法set()来注册一个对象:

代码如下:

<?php
$db = new DBConnection;
$settings = new Settings_XML;
$user = new User;
// Register objects
$registry =& new Registry;
$registry->set (&#39;db&#39;, $db);
$registry->set (&#39;settings&#39;, $settings);
$registry->set (&#39;user&#39;, $user);
?>
ログイン後にコピー

现在我们的寄存器对象容纳了我们所有的对象,我们指需要把这个注册器对象传递给一个函数(而不是分别传递三个对象)。看下面的例子:

代码如下:

<?php
function test(&$registry) {
    $db =& $registry->get(&#39;db&#39;);
    $settings =& $registry->get(&#39;settings&#39;);
    $user =& $registry->get(&#39;user&#39;);
    // Do something with the objects
}
?>
ログイン後にコピー

注册器相比其他的方法来说,它的一个很大的改进就是当我们需要在我们的代码中新增加一个对象的时候,我们不再需要改变所有的东西(译者注:指程序中所有用到全局对象的代码),我们只需要在注册器里面新注册一个对象,然后它(译者注:新注册的对象)就立即可以在所有的组件中调用。

为了更加容易的使用注册器,我们把它的调用改成单件模式(译者注:不使用前面提到的函数传递)。因为在我们的程序中只需要使用一个注册器,所以单件模式使非常适合这种任务的。在注册器类里面增加一个新的方法,如下所示:

代码如下:

<?
function &getInstance() {
    static $me;
    if (is_object($me) == true) {
        return $me;
    }
    $me = new Registry;
    return $me;
}
?>
ログイン後にコピー

这样它就可以作为一个单件来使用,比如:

代码如下:

<?php
$db = new DBConnection;
$settings = new Settings_XML;
$user = new User;
// Register objects
$registry =& Registry::getInstance();
$registry->set (&#39;db&#39;, $db);
$registry->set (&#39;settings&#39;, $settings);
$registry->set (&#39;user&#39;, $user);
function test() {
    $registry =& Registry::getInstance();
    $db =& $registry->get(&#39;db&#39;);
    $settings =& $registry->get(&#39;settings&#39;);
    $user =& $registry->get(&#39;user&#39;);
    // Do something with the objects
}
?>
ログイン後にコピー

正如你看到的,我们不需要把私有的东西都传递到一个函数,也不需要使用“global”关键字。所以注册器模式是这个问题的理想解决方案,而且它非常的灵活。

请求封装器
虽然我们的注册器已经使“global”关键字完全多余了,在我们的代码中还是存在一种类型的全局变量:超级全局变量,比如变量$_POST,$_GET。虽然这些变量都非常标准,而且在你使用中也不会出什么问题,但是在某些情况下,你可能同样需要使用注册器来封装它们。
一个简单的解决方法就是写一个类来提供获取这些变量的接口。这通常被称为“请求封装器”,下面是一个简单的例子:

代码如下:

<?php
Class Request {
    var $_request = array();
    function Request() {
        // Get request variables
        $this->_request = $_REQUEST;
    }
    function get($name) {
        return $this->_request[$name];
    }
}
?>
ログイン後にコピー

上面的例子是一个简单的演示,当然在请求封装器(request wrapper)里面你还可以做很多其他的事情(比如:自动过滤数据,提供默认值等等)。
下面的代码演示了如何调用一个请求封装器:

代码如下:

<?php
$request = new Request;
// Register object
$registry =& Registry::getInstance();
$registry->set (&#39;request&#39;, &$request);
test();
function test() {
    $registry =& Registry::getInstance();
    $request =& $registry->get (&#39;request&#39;);
    // Print the &#39;name&#39; querystring, normally it&#39;d be $_GET[&#39;name&#39;]
    echo htmlentities($request->get(&#39;name&#39;));
}
?>
ログイン後にコピー

正如你看到的,现在我们不再依靠任何全局变量了,而且我们完全让这些函数远离了全局变量。
结论
在本文中,我们演示了如何从根本上移除代码中的全局变量,而相应的用合适的函数和变量来替代。注册模式是我最喜欢的设计模式之一,因为它是非常的灵活,而且它能够防止你的代码变得一塌糊涂。
另外,我推荐使用函数参数而不是单件模式来传递注册器对象。虽然使用单件更加轻松,但是它可能会在以后出现一些问题,而且使用函数参数来传递也更加容易被人理解。

以上がPHPでのグローバル変数の使い方を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
php
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!