46

config.php各ページに含まれている があります。構成では、次のような配列を作成します。

$config = array();
$config['site_name']      = 'Site Name';
$config['base_path']      = '/home/docs/public_html/';
$config['libraries_path'] = $config['base_path'] . '/libraries';
//etc...

次にfunction.php、ほぼすべてのページに含まれており、そこにglobal $configアクセスするために使用する必要があります-これは私が取り除きたいものです!

$configを使用せずにコードの他の部分にアクセスするにはどうすればよいglobalですか?

なぜ私の例で使用すべきではないのか、誰か説明できglobalますか? 口調が悪いと言う人もいれば、安全ではないという人もいますか?

編集1:

どこでどのように使用するかの例:

function conversion($Exec, $Param = array(), $Log = '') {
    global $config;
    $cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec;
    foreach ($Param as $s)
    {
        $cmd .= ' ' . $s;
    }
}

編集2:

Vilxで提案されているように、これらすべてをクラスに入れるのはクールですが、この場合、構成keyvalueデータベースから抽出する次のループとどのように結び付けますか。
配列を割り当てるという考えを単純化しすぎました$config。以下に例を示します。

$sql = "SELECT * from settings";
$rsc = $db->Execute($sql);
if ( $rsc ) {
    while(!$rsc->EOF) {
        $field = $rsc->fields['setting_options'];
        $config[$field] = $rsc->fields['setting_values'];
        @$rsc->MoveNext();
    }
}

編集3:

varsその上、 configで設定されている機能から他の機能にアクセスする必要があり、それらのいくつかは次のとおり$dbです$language

それらをクラスに入れれば、本当に何かが解決しますか? 実際に使用するglobalと何が変わるの?

編集4:

PHP global in functionsを読んだところ、Gordonが を使用すべきではない理由を非常にうまく説明していますglobalglobal私はすべてに同意しますが、変数を再割り当てするために私の場合は使用しません<-- WTF!!。しかし、関数からデータベースにアクセスする必要があるglobal $db場合は、この場合の問題はどこにありますか? を使用せずに、他の方法でこれを行うにはどうすればよいですglobalか?

編集5:

同じ PHP global in functions でdecezeは次のように述べています。

しかし、ここでは基本的な「INIT」について話しています。私は基本的に設定defineしますが、使用しますvars-まあ、それは技術的に間違っています。しかし、あなたの関数は何にも依存していません-しかし、$db覚えておくことができる1つのvarの名前ですか? を使用するのは本当にグローバルな必要性$dbです。ここで DEPENDENCY はどこにあり、それ以外の場合はどのように使用するのですか?

PSここで、私たちは2つの異なる心の対立に直面していると思いました。たとえば、のもの(オブジェクト指向プログラミングをまだよく理解していない)と、OOPの教祖(私の現在の観点から)と呼ばれる可能性のある人たちです。 -私にとって彼らにとって明白に見えることは、新しい質問を引き起こします。それが、この質問が何度も繰り返されている理由だと思います。個人的には、最終的にはより明確になりましたが、まだ明確にしなければならないことがあります。

4

6 に答える 6

55

変数に対するポイントは、global変数がコードを非常に緊密に結合していることです。コードベース全体は、a)変数 $configとb)その変数の存在に依存しています。(何らかの理由で)変数の名前を変更する場合は、コードベース全体で名前を変更する必要があります。また、変数とは無関係に変数に依存するコードを使用することもできなくなります。

global変数の例:

require 'SomeClass.php';

$class = new SomeClass;
$class->doSomething();

上記の行のどこでも、クラスまたは一部のコードがSomeClass.php暗黙的にグローバル変数に依存しているため、エラーが発生する可能性があります$config。クラスを見ているだけでは、これを示すものは何もありません。これを解決するには、次のことを行う必要があります。

$config = array(...);

require 'SomeClass.php';

$class = new SomeClass;
$class->doSomething();

内に正しいキーを設定しないと、このコードはどこかで失敗する可能性$configあります。構成アレイのどの部分がSomeClass必要か、必要でないか、いつ必要になるかが明確でないため、正しく実行するための正しい環境を再作成することは困難です。$configまた、使用したい場所で他の何かに使用されている変数がすでにある場合は、競合が発生しますSomeClass

したがって、暗黙の非表示の依存関係を作成する代わりに、すべての依存関係を挿入します。

require 'SomeClass.php';

$arbitraryConfigVariableName = array(...);

$class = new SomeClass($arbitraryConfigVariableName);
$class->doSomething();

config配列をパラメーターとして明示的に渡すことにより、上記のすべての問題が解決されます。アプリ内で必要な情報を渡すのと同じくらい簡単です。また、アプリケーションの構造とフロー、および何が何を話しているのかがより明確になります。アプリケーションが現在大きな泥だんごである場合にこの状態にするには、再構築が必要になる場合があります。


コードベースが大きくなるほど、個々の部分を互いに切り離す必要があります。すべての部分がコードベース内の他のすべての部分に依存している場合、その一部を個別にテスト、使用、または再利用することはできません。それは単に混乱に発展します。パーツを互いに分離するには、必要なすべてのデータをパラメーターとして受け取るクラスまたは関数としてパーツをコーディングします。これにより、コードのさまざまな部分の間にクリーンなシーム(インターフェイス)が作成されます。


質問を1つの例にまとめようとしています。

require_once 'Database.php';
require_once 'ConfigManager.php';
require_once 'Log.php';
require_once 'Foo.php';

// establishes a database connection
$db = new Database('localhost', 'user', 'pass');

// loads the configuration from the database,
// the dependency on the database is explicit without `global`
$configManager = new ConfigManager;
$config = $configManager->loadConfigurationFromDatabase($db);

// creates a new logger which logs to the database,
// note that it reuses the same $db as earlier
$log = new Log($db);

// creates a new Foo instance with explicit configuration passed,
// which was loaded from the database (or anywhere else) earlier
$foo = new Foo($config);

// executes the conversion function, which has access to the configuration
// passed at instantiation time, and also the logger which we created earlier
$foo->conversion('foo', array('bar', 'baz'), $log);

読者のための演習として、個々のクラスの実装に任せます。それらを実装しようとすると、実装が非常に簡単で明確であり、単一のを必要としないことに気付くでしょうglobal。すべての関数とクラスは、関数の引数の形式で渡されるすべての必要なデータを取得します。また、上記のコンポーネントを他の組み合わせでプラグインできること、または依存関係を他のコンポーネントに簡単に置き換えることができることも明らかです。たとえば、構成をデータベースから取得する必要はまったくありません。または、ロガーはFoo::conversion、これについて何も知らなくても、データベースではなくファイルにログを記録できます。


の実装例ConfigManager

class ConfigManager {

    public function loadConfigurationFromDatabase(Database $db) {
        $result = $db->query('SELECT ...');

        $config = array();
        while ($row = $result->fetchRow()) {
            $config[$row['name']] = $row['value'];
        }

        return $config;
    }

}

これは非常に単純なコードであり、あまり機能しません。なぜこれをオブジェクト指向コードとして必要なのかと疑問に思うかもしれません。重要なのは、このコードを他のすべてから完全に分離するため、このコードの使用が非常に柔軟になるということです。で1つのデータベース接続を指定すると、特定の構文を持つ1つの配列が返されます。入力→出力。明確な継ぎ目、明確なインターフェース、最小限の明確な責任。簡単な関数でも同じことができます。

loadConfigurationFromDatabaseオブジェクトの追加の利点は、その関数の特定の実装から呼び出すコードをさらに分離することです。グローバルfunction loadConfigurationFromDatabase()を使用するだけの場合は、基本的に同じ問題が発生します。関数を呼び出そうとするときにその関数を定義する必要があり、別の関数に置き換える場合は名前の競合が発生します。オブジェクトを使用することにより、コードの重要な部分がここに移動します。

$config = $configManager->loadConfigurationFromDatabase($db);

$configManagerここで、メソッドを持つ他のオブジェクトの代わりに使用できますloadConfigurationFromDatabase。それが「ダックタイピング」です。$configManagerメソッドがある限り、正確に何であるかは関係ありませんloadConfigurationFromDatabase。アヒルのように歩き、アヒルのように鳴くなら、それはアヒルです。むしろ、loadConfigurationFromDatabaseメソッドがあり、有効な構成配列を返す場合、それはある種のConfigManagerです。コードを1つの特定の変数$configから、1つの特定のloadConfigurationFromDatabase関数から、さらには1つの特定のから切り離しConfigManagerました。コードは特定の他の部分に依存しないため、すべての部分を変更、交換、交換、およびどこからでも動的にロードできます。

メソッド自体も、メソッドを呼び出して結果をフェッチloadConfigurationFromDatabaseできる限り、特定のデータベース接続に依存しません。query渡されるオブジェクトは完全に偽物であり、そのインターフェイス$dbが同じように動作する限り、XMLファイルまたはその他の場所からデータを読み取ることができます。

于 2012-09-16T10:52:39.500 に答える
9

私はこれをクラスで解決しました:

class Config
{
    public static $SiteName = 'My Cool Site';
}

function SomeFunction
{
    echo 'Welcome to ' , Config::$SiteName;
}

定数を使用するというfcortesの提案も良いものです。CFG_SITE_NAME他の定数との偶発的な名前の衝突を避けるために、すべての定数にのような接頭辞を付けることを提案したいだけです。

于 2012-09-16T10:09:50.010 に答える
6

あなたの場合、定義constants.phpを含む唯一のファイルを作成します(目的がこれらの「変数」である場合、実行時に変更されることはありません):

define('SITE_NAME','site name');

define('BASE_PATH','/home/docs/public_html/');

...

これconstants.phpを必要とするすべてのファイルに含めます。

include_once('constants.php');
于 2012-09-16T10:03:58.157 に答える
3

オブジェクト指向アプローチと手続き型アプローチの間 (およびより一般的には、宣言型アプローチと命令型アプローチの間) で大きな議論があり、それぞれのアプローチには長所と短所があります。

シングルトン(グローバルのOOPバージョン)である「Config」クラスを使用しました。以前に開発されたソリューションのいくつかを 1 つのアプリケーションで一緒に使用する必要があることを発見するまで、それはうまく機能しました。他のサブアプリケーションからコードを呼び出すたびに適切な構成に切り替えるため。

次の 2 つの方法があります。

a) 慣れ親しんだ方法でアプリケーションを設計する (その方がよいでしょう。既に経験があり、開発にかかる時間や、発生する可能性がある問題と発生しない可能性がある問題を予測できるからです)。現在のアプローチの制限に行き詰まった後は、リファクタリングしてグローバルを回避します。

b) OOP フレームワーク (少なくとも 3 つまたは 4 つ、すなわち Cake、CodeIgniter、Zend、Symfony、Flow3 を参照) でどのように行われているかを調べ、何かを借りるか、フレームワークの使用に切り替えます (または、そうすることがより確実になるでしょう)。すべて正しい)。

于 2012-09-16T14:23:28.183 に答える
1

簡単な小さなクラスを作成しました:

class Config {

    private static $config = array();

    public static function set( $key, $value ) {
        self::$config[$key] = $value;
    }

    public static function get( $key ) {
        return isset( self::$config[$key] ) ? self::$config[$key] : null;
    }
}

Config::set( 'my_config', 'the value' );

echo 'the config value is: ' . Config::get('my_config');

これは簡単にリファクタリングして、関数isSet( $key )またはsetAll( $array ).

編集:これで、構文が有効になるはずです。

このクラスは次のように簡単に変更できます。

class Config {

    private static $config = array();

    public static function set( $key, $value ) {
        self::$config[$key] = $value;
    }

    public static function get( $key ) {
        return isset( self::$config[$key] ) ? self::$config[$key] : null;
    }

    public static function setAll( array $array ) {
        self::$config = $array;
    }

    public static function isKeySet( $key ) {
        return isset( self::$config[ $key ] );
    }
}

Config::setAll( array(
    'key' => 'value',
    'key2' => array( 'value',
                    'can be an',
                    'array' ) ) );
Config::set( 'my_config', 'the value' );

if( Config::isKeySet( 'my_config' ) ) {
    echo 'the config value is: ' . Config::get('my_config');
}

configs を使用する別のファイルにファイルを含めるか、autoloaderを使用する必要があります。

編集2:

これは、グローバルを使用する場合とほとんど同じですが、すべての関数の最初で使用したいことを述べる必要がないという違いがあります。構成をグローバルに使用する場合、構成は何らかの形でグローバルである必要があります。何かをグローバルスコープに入れるとき、これがこの情報を見ることを意図していない他のクラスにとって危険な情報になる可能性があるかどうかを議論する必要があります...デフォルト設定?グローバル スコープで安全に使用できると思います。必要なのは、簡単に変更およびカスタマイズできるものだけです。

それが危険な情報であり、意図されたクラス以外のクラスに到達できないと判断した場合は、依存性注入にチェックインすることをお勧めします。依存性注入を使用すると、クラスはコンストラクターでオブジェクトを取得し、使用する変数にプライベートに配置します。このオブジェクトは構成クラスのオブジェクトである可能性があり、最初に構成オブジェクトを作成し、次に構成を注入する Template オブジェクトを作成するラッパー クラスが必要です。これは、ドメイン駆動設計などのより複雑な設計パターンでよく見られる設計です。

于 2012-09-16T12:25:15.283 に答える
0

config.php

<?php
class config {

    private static $config = array();

    public static function set( $key, $value ) {
        self::$config[$key] = $value;
    }

    public static function get( $key ) {
        if( config::isKeySet( $key ) ) {
            return isset( self::$config[$key] ) ? self::$config[$key] : null;
        }
    }

    public static function setAll( array $array ) {
        self::$config = $array;
    }

    public static function isKeySet( $key ) {
        return isset( self::$config[ $key ] );
    }
}

// set valuable values

config::setAll( array(
    'key' => 'value',
    'key2' => array( 'value', 'can be an', 'array' ),
    'database' => array( "username" => "root", "password" => "root")
                     )
    );
config::set( 'my_config', 'the value' );
?>

config.usage.php

<?php
require_once 'config.php';
$database_credentials = config::get('database');
echo 'the config value for username is ' . $database_credentials['username'];
echo '<br> the config value for password is ' . $database_credentials['password'];

function additionalFunctionality($database_credentials)
{
    echo '<br> the config value for password is ' . $database_credentials['password'];
}
?>

config.usage.too.php

<?php
require_once 'config.php'; // put this first
require_once 'config.usage.php'; // include some functionality from another file
$database_credentials = Config::get('database');
echo 'the config value for username is ' . $database_credentials['username'];

additionalFunctionality($database_credentials); // great
?>
于 2015-07-16T12:21:50.407 に答える