48 votes

Arrêtez d'utiliser «global» en PHP

J'ai un config.php qui est inclus pour chaque page. Dans la configuration, j'ai créer un tableau qui ressemble à quelque chose comme:

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

Ensuite, je dois function.php, qui est également inclus à presque chaque page, où je dois utiliser global $config pour obtenir l'accès à l'il - et c' est ce que j'aimerais me débarrasser de nous!

Comment puis-je accéder $config dans les autres parties de mon code sans l'aide d' global?

Quelqu'un pourrait-il expliquer, POURQUOI je ne devrais pas utiliser global dans mon exemple? Certains disent que c'est un mauvais ton, d'autres disent qu'il n'est pas sécurisé?

EDIT 1:

Exemple de où et comment je l'utilise:

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

EDIT 2:

Mettre tout cela dans la classe, comme suggéré par Vilx, ce serait cool, mais dans ce cas, comment pourrais-je l'attacher avec la boucle suivante, qui est de l'extraction de config key et value de la base de données.
J'ai simplifié à l'extrême l'idée de l'attribution d' $config tableau, voici un exemple:

$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();
    }
}

EDIT 3:

D'ailleurs, j'ai accès à d'autres vars de fonctions qui sont définies dans le fichier config et quelques-uns d'entre eux, par exemple:$db, $language et etc.

Si je les ai mis dans la classe va vraiment résoudre quoi que ce soit? Si j'utilise global que faut-il vraiment changer?

EDIT 4:

J'ai lu PHP mondial dans les fonctionsGordon explique dans la très belle façon pourquoi vous ne devriez pas utiliser global. Je suis d'accord sur tout, mais je ne l'utilise global dans mon cas, de réaffecter les variables, ce qui aura pour résultat, comme il l'a dit, <-- WTF!!, ;)) ouais d'accord, c'est fou. Mais si j'ai juste besoin de base de données access à partir d'une fonction en utilisant global $db où est le problème dans ce cas? Comment faire autrement, sans l'aide d' global?

EDIT 5:

Dans le même PHP mondial dans les fonctions deceze dit: "L'une des raisons pour laquelle mondiale contre, c'est qu'il signifie que la fonction est dépendante d'une autre portée. Cela va dégénérer très rapidement."

Mais je parle ici de base de la "INIT". En gros, j'ai mis define mais utiliser vars - et bien c'est faux dans la technique. Mais votre fonction n'est pas dépendait de rien, mais le nom d'un var $db que vous pouvez garder à l'esprit? C'est vraiment besoin d'utiliser $db,, où est la DÉPENDANCE d'ici et comment l'utiliser autrement?

P. S. je viens d'avoir une pensée, que nous sommes face à ce conflit de deux esprits différents, par exemple: le mien (mais PAS bien comprendre la programmation orientée objet) et ceux qui pourraient être appelés des gourous (de mon point de vue actuel), en programmation orientée objet - ce qui semble évident pour eux que pour moi se pose de nouvelles questions. Je pense que c'est pourquoi cette question est posée maintes et maintes fois. Personnellement, pour moi, il est devenu plus clair après tout, mais il ya encore des choses à clarifier.

56voto

deceze Points 200115

Le point contre global variables est qu'elles couple de code très serrée. Votre base de code entier dépend de a) la variable nom de la $config , et b) l'existence de cette variable. Si vous souhaitez renommer la variable (pour quelque raison que ce soit), vous devez le faire partout, tout au long de votre base de code. Vous pouvez également de ne pas utiliser n'importe quel morceau de code qui dépend de la variable indépendante de plus.

Exemple avec global variable:

require 'SomeClass.php';

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

N'importe où dans les lignes ci-dessus, vous pouvez obtenir une erreur, car la classe ou du code en SomeClass.php implicitement dépend d'une variable globale $config. Il n'y a aucune indication de ce que ce soit mais simplement à la recherche à la classe. Pour résoudre ce problème, vous devez faire ceci:

$config = array(...);

require 'SomeClass.php';

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

Ce code peut encore échouer quelque part, si vous ne définissez pas les bonnes touches à l'intérieur d' $config. Car il n'est pas évident que les parties du tableau de config SomeClass des besoins ou n'a pas besoin et quand il en a besoin, il est difficile de recréer l'environnement approprié pour qu'il fonctionne correctement. Il crée également des conflits si vous est arrivé d'avoir déjà une variable $config utilisé pour autre chose, où que vous souhaitez utiliser SomeClass.

Donc au lieu de créer implicite, invisible dépendances, injecter toutes les dépendances:

require 'SomeClass.php';

$arbitraryConfigVariableName = array(...);

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

En passant par le tableau de config explicitement comme un paramètre, tous les problèmes sont résolus. C'est aussi simple que de remettre les informations nécessaires autour de l'intérieur de votre application. Il fait également la structure et le flux de l'application et de ce qui parle de ce que beaucoup plus claire. Pour arriver à cet état si votre demande est actuellement une grosse boule de boue peut prendre un certain restructuration.


Plus votre base de code, plus vous avez à découpler les composants les uns des autres. Si chaque partie dépend de chaque partie à l'autre dans votre base de code, vous ne peut tout simplement pas de test, utiliser ou réutiliser une partie individuellement. Que simplement retombe dans le chaos. Pour séparer les éléments les uns des autres, le code comme des classes ou des fonctions qui prennent toutes leurs données requises en tant que paramètres. Qui crée les coutures propres (interfaces) entre les différentes parties de votre code.


Essayer de relier votre question en un seul exemple:

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);

Je vais le laisser à la mise en œuvre des classes individuelles comme un exercice pour le lecteur. Lorsque vous essayez de les mettre en œuvre, vous remarquerez qu'ils sont très clair et facile à mettre en œuvre et ne nécessitent pas un seul global. Chaque fonction et la classe obtient toutes les données nécessaires passé sous la forme d'arguments de la fonction. Il devrait être aussi évident que les composantes ci-dessus peuvent être branchés ensemble dans une autre combinaison ou que les dépendances peuvent facilement être remplacées par d'autres. Par exemple, la configuration n'a pas besoin de venir de la base de données, ou de l'enregistreur de données peut se connecter à un fichier au lieu de la base de données sans Foo::conversion d'avoir à connaître à propos de tout cela.


Exemple de mise en œuvre pour l' 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;
    }

}

C'est un très simple morceau de code qui n'a même pas d'en faire beaucoup. Vous pouvez vous demander pourquoi vous voulez ce que le code orienté objet. Le point est que cela rend l'utilisation de ce code extrêmement flexible, car il isole parfaitement de tout le reste. Vous donnez une connexion de base de données, vous obtenez un tableau avec une certaine syntaxe de retour. Entrée → Sortie. Claire coutures, des interfaces claires, minimal, des responsabilités bien définies. Vous pouvez faire la même chose avec une fonction simple.

L'avantage supplémentaire d'un objet a, c'est que même plus loin et découple le code qui appelle loadConfigurationFromDatabase partir de n'importe quel particulier de la mise en œuvre de cette fonction. Si vous voulez juste utiliser une approche globale function loadConfigurationFromDatabase(), en gros, vous avez le même problème: cette fonction doit être définie lorsque vous essayez de l'appeler et il y a des conflits de noms si vous voulez la remplacer par quelque chose d'autre. Par l'utilisation d'un objet, la partie critique du code passe ici:

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

Vous pouvez substituer $configManager ici pour tout autre objet qui possède également une méthode loadConfigurationFromDatabase. C'est "duck typing". Vous ne vous souciez pas quoi exactement, $configManager est, tant qu'il a une méthode loadConfigurationFromDatabase. Si ça marche comme un canard et des charlatans comme un canard, c'est un canard. Ou plutôt, si, il a un loadConfigurationFromDatabase méthode et redonne un valide tableau de config, c'est une sorte de ConfigManager. Vous avez découplé votre code à partir d'une variable particulière $config, à partir de l'un particulier loadConfigurationFromDatabase fonction et même d'un particulier ConfigManager. Toutes les pièces peuvent être modifiées ou changées et remplacées et chargé dynamiquement à partir de n'importe où, parce que le code ne dépend pas d'une autre pièce.

L' loadConfigurationFromDatabase méthode elle-même ne dépend pas de toute connexion de base de données, tant qu'il peut, appel query sur il et aller chercher des résultats. L' $db objet passé en elle pourrait être entièrement faux et lire ses données à partir d'un fichier XML ou n'importe où ailleurs au lieu de cela, aussi longtemps que son interface toujours le même comportement.

9voto

Vilx- Points 37939

J'ai résolu cela avec une classe:

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

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

La suggestion de fcortes d'utiliser des constantes est également bonne. Je voudrais seulement suggérer de donner à toutes les constantes un préfixe, comme CFG_SITE_NAME , afin d'éviter les conflits de noms accidentels avec d'autres constantes.

6voto

fcortes Points 509

Pour votre cas, je créerais un seul fichier constants.php avec des définitions (si votre objectif est que ces "variables" ne soient jamais modifiées en temps d'exécution):

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

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

...
 

Incluez ce constants.php dans tous les fichiers où vous en aurez besoin:

 include_once('constants.php');
 

3voto

irezvin Points 91

Il y a un grand discuter entre procédurale et orientée objet approches (et, plus généralement, entre le déclaratif et impératif) et chaque approche a ses avantages et inconvénients.

J'ai utilisé 'Config' classe qui a un Singleton (une OOP version de global). Il a travaillé très bien pour moi, jusqu'à ce que j'avais découvert sur la nécessité d'utiliser plusieurs d'précédemment développé des solutions ensemble dans une même application depuis toutes les configs sont mondiaux et renvoyées par la même classe (le même nom de variable, dans votre cas), ils entraient en conflit et j'ai dû passer à la bonne config à chaque fois que j'ai appelé le code d'autres sous-application.

Vous avez deux façons:

a) soit de la conception de votre application de manière à vous vous êtes habitué à et vous êtes familier avec (qui sera le mieux parce que vous avez déjà une expérience à elle, on peut prédire combien de temps le développement et quels sont les problèmes qui peuvent ou peuvent ne pas se produire); et après vous serez coincé dans les limites de votre approche actuelle, refactoriser pour éviter les variables globales;

b) regardez comment il faut faire en POO cadres (voir au moins trois ou quatre, c'est à dire Gâteau, CodeIgniter, Zend, Symfony, Flow3) et emprunter quelque chose, ou d'un commutateur à l'aide d'un cadre (ou peut-être que vous serez plus sûr de vous faire tout droit).

1voto

Krycke Points 1591

J'ai créé un simple petit de la classe:

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');

cela peut facilement être remaniée pour avoir une fonction isSet( $key ) ou peut-être un setAll( $array ).

EDIT: Maintenant, la syntaxe doit être valide.

vous pouvez facilement modifier cette classe comme suit:

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');
}

Vous avez encore le besoin d'inclure le fichier dans un autre fichier qui utilise des configs, ou l'utilisation d'un autochargeur.

EDIT 2:

C'est à peu près le même que l'aide mondiale, avec la différence que vous n'avez pas besoin de préciser que vous souhaitez l'utiliser en début de chaque fonction. Si vous souhaitez utiliser des Configs à l'échelle mondiale, puis les Configs être, d'une certaine façon globale. Quand on met quelque chose dans la portée globale, vous avez besoin d'argumenter si cela peut être dangereux de l'information à une autre classe ne signifie pas pour voir cette information... configurations par défaut? Je pense qu'il est sûr d'avoir une portée globale, et ensuite, vous avez juste besoin de quelque chose qui est facile à modifier et à personnaliser.

Si vous décidez qu'il est dangereux de l'information, qui ne doit pas être accessible pour les classes autres que la classe c'est le but, alors vous pourriez vouloir vérifier dans l' injection de Dépendance. Avec la dépendance injections à une classe de prendre un objet dans son constructeur, le plaçant en privé, dans une variable pour l'utiliser. Cet objet peut être un objet d'une classe de configuration, puis vous avez besoin d'une classe wrapper de la création de la première à la configuration de l'objet, puis le Modèle objet de l'injection de configurations. Cette conception est souvent vu dans plus complexes, des modèles de conception, comme par exemple le Domain Driven Design.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X