28 votos

Deje de usar `global` en PHP

Tengo un config.php que se incluye para cada página. En config puedo crear una matriz en la que se ve algo como:

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

Entonces he function.php, que también se incluye a casi cada página en la que tengo que usar global $config para obtener acceso a ella - y esto es lo que me gustaría deshacerse de!

¿Cómo puedo acceder a $config en las otras partes de mi código sin usar global?

Alguien podría explicar, ¿por QUÉ no debería usar global en mi ejemplo? Algunos dicen que es un mal tono, otros dicen que no es seguro?

EDIT 1:

Ejemplo de dónde y cómo puedo usarlo:

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

EDIT 2:

Poniendo todo esto en la clase, según lo sugerido por Vilx, sería genial, pero en este caso, ¿cómo puedo atarlo con el siguiente bucle que es la extracción de config key y value de la base de datos.
Yo la simplificación de la idea de la asignación $config de la matriz, he aquí un ejemplo:

$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:

Además, tengo que acceder a otros vars de las funciones que se establecen en el config y algunos de ellos, por ejemplo:$db, $language y etc.

Si los pongo en la clase que realmente resuelve nada? Si utilizo global ¿qué es realmente el cambio?

EDIT 4:

He leído PHP global en las funciones donde Gordon explica en forma muy agradable de por qué no debería usar global. Estoy de acuerdo en todo, pero yo no uso global en mi caso para reasignar las variables, lo que resultará en que, como él dijo, <-- WTF!!, ;)) sí, de acuerdo, es una locura. Pero si yo sólo necesita acceder a la base de datos a partir de una función sólo mediante el uso de global $db ¿dónde está el problema en este caso? ¿Cómo hacer esto de otra manera, sin el uso de global?

EDIT 5:

En el mismo PHP global en las funciones de deceze dice: "La gran razón en contra de la global es que significa que la función depende de otro ámbito. Este va a ser desordenado, muy rápidamente."

Pero yo estoy hablando aquí de básica "INIT". Yo, básicamente, establecer define pero el uso de vars - bien que está mal en forma técnica. Pero su función no dependía de nada - pero el nombre de un var $db que usted podría tener en cuenta? Es realmente global de la necesidad de utilizar $db, donde es la DEPENDENCIA de aquí y cómo usarlo de otra manera?

P. S. yo sólo tenía un pensamiento, que nos estamos enfrentando el conflicto de dos diferentes mentes, por ejemplo: la mina (pero NO así la comprensión de la programación orientada a objetos) y los que podrían ser llamados gurús (desde mi punto de vista actual) en la programación orientada a objetos - lo que parece obvio para ellos que para mí se plantea nuevas preguntas. Yo creo que por eso esta pregunta se le pide una y otra vez. Personalmente, para mí es mucho más claro después de todo, pero aún hay cosas que aclarar.

34voto

deceze Puntos 200115

El punto en contra de global variables es que la pareja de código muy bien. Su código base completa depende de a) la variable de nombre $config y b) la existencia de esa variable. Si desea cambiar el nombre de la variable (por el motivo que sea), tienes que hacerlo en todas partes a lo largo de su código base. Usted también no puede usar cualquier pieza de código que depende de la variable de forma independiente de ella ya.

Ejemplo global variable:

require 'SomeClass.php';

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

En cualquier lugar en las líneas de arriba usted puede obtener un error porque la clase o algún código en SomeClass.php implícitamente depende de una variable global $config. No hay ninguna indicación de ello en absoluto, aunque sólo mirando a la clase. Para solucionar esto, tienes que hacer esto:

$config = array(...);

require 'SomeClass.php';

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

Este código puede todavía fracasan si no se establecen las teclas correctas en el interior de $config. Ya no es obvio lo que las partes de la configuración de la matriz SomeClass necesita o no necesita y cuando los necesita, es difícil recrear el ambiente adecuado para que se ejecute correctamente. También crea conflictos, si usted pasó a tener ya una variable $config utilizar para otra cosa dondequiera que usted desea utilizar SomeClass.

Así que en lugar de crear implícita, invisible dependencias, inyectar todas las dependencias:

require 'SomeClass.php';

$arbitraryConfigVariableName = array(...);

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

Al pasar el array de configuración de forma explícita como un parámetro, todos los problemas están resueltos. Es tan simple como la entrega de la información requerida alrededor en el interior de su aplicación. También hace que la estructura y el flujo de la aplicación y lo que habla de lo mucho más clara. Para llegar a este estado si su aplicación es en la actualidad una gran bola de barro pueden tomar algunos de reestructuración.


El más grande de su código base, más tienes que separar las piezas individuales de cada uno de los otros. Si cada parte es dependiente de cualquier otra parte en su base de código, simplemente no se puede probar, utilizar o reutilizar cualquier parte de la misma de forma individual. Que simplemente se convirtiera en un caos. Para separar las piezas de cada uno de los otros, de código como clases o funciones que tienen todos sus datos requeridos como parámetros. Que crea limpio costuras (interfaces) entre las diferentes partes de su código.


Tratando de atar a su pregunta en un ejemplo:

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

Lo voy a dejar para la implementación de las clases individuales como ejercicio para el lector. Cuando se trate de ponerlas en práctica, te darás cuenta de que está muy clara y sencilla de implementar y no requiere de una sola global. Cada función y clase pone a todos sus datos necesarios pasado en la forma de los argumentos de la función. También debería ser obvio que los componentes anteriores pueden ser conectados juntos en cualquier otra combinación o que las dependencias pueden ser fácilmente sustituidos por otros. Por ejemplo, la configuración no necesita venir de la base de datos, o el registrador puede iniciar sesión en un archivo en lugar de la base de datos sin Foo::conversion tener que saber acerca de esto.


Ejemplo de aplicación para 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;
    }

}

Es una manera muy simple trozo de código que incluso no hace mucho. Usted puede preguntar por qué te gustaría esto como orientado a objetos de código. El punto es que esto hace que el uso de este código extremadamente flexible, ya que aísla perfectamente de todo lo demás. Usted da una conexión de base de datos en, se obtiene una matriz con una cierta sintaxis de la espalda. De Entrada → Salida. Claro costuras, claro interfaces, mínimo, responsabilidades bien definidas. Usted puede hacer lo mismo con una simple función.

La ventaja adicional de un objeto tiene es que es aún más desacopla el código que llama a loadConfigurationFromDatabase de cualquier particular, la aplicación de esa función. Si usted acaba de utilizar un global function loadConfigurationFromDatabase(), básicamente tiene el mismo problema de nuevo: que la función debe ser definida cuando se trate de llamar y existen conflictos de nomenclatura si desea reemplazarlo con otra cosa. Mediante el uso de un objeto, la parte crítica del código se mueve aquí:

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

Usted puede substituir $configManager aquí para cualquier otro objeto que también tiene un método loadConfigurationFromDatabase. Que el "duck typing". No importa de qué es exactamente $configManager , siempre que se tiene un método loadConfigurationFromDatabase. Si camina como un pato y grazna como un pato, es un pato. O más bien, si se tiene un loadConfigurationFromDatabase método y le da la espalda válido de un array de configuración, se trata de algún tipo de ConfigManager. Se han disociado el código de una determinada variable $config, de un particular, loadConfigurationFromDatabase función y de un particular, ConfigManager. Todas las piezas pueden ser cambiado y cambiado y reemplazado y se cargan de forma dinámica desde cualquier lugar, porque el código no depende de ninguna otra pieza.

El loadConfigurationFromDatabase método en sí misma no depende de una determinada conexión de base de datos, siempre puede llamar a query en él y obtener los resultados. El $db objeto que se pasa en ella puede ser totalmente falso y leer datos desde un archivo XML o en cualquier otro lugar, siempre y cuando su interfaz todavía se comporta de la misma.

7voto

Vilx- Puntos 37939

He solucionado esto con una clase:

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

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

fcortes la sugerencia de utilizar constantes es también un buen lugar. Yo sólo desea sugerir a dar todas las constantes de un prefijo, como CFG_SITE_NAME, por lo que para evitar accidentales nombre de enfrentamientos con otras constantes.

4voto

fcortes Puntos 509

Para tu caso me gustaría crear un solo archivo constants.php con definiciones (si su propósito es que estas "variables" nunca puede cambiarse en tiempo de ejecución):

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

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

...

Incluir esta constants.php en todos los archivos donde usted necesita:

include_once('constants.php');

2voto

irezvin Puntos 91

Hay una gran discutiendo entre orientada a objetos y enfoques del procedimiento (y, más en general, entre declarativo e imperativo) y cada método tiene sus ventajas y desventajas.

He utilizado 'Config' de la clase que era un Singleton (OOP versión de global). Funcionó bien para mí hasta que yo había descubierto la necesidad de utilizar varios de los anteriormente desarrollado soluciones juntos en una aplicación - desde todas las configs fueron global y referido por la misma clase (el mismo nombre de variable, en su caso) entraban en conflicto y tuve que cambiar a una adecuada config cada vez que me llama por el código de otros sub-aplicación.

Tienes dos maneras:

a) el diseño de su aplicación de una manera que te acostumbras y te son familiares (que va a ser mejor, porque usted ya tiene experiencia en esto y puede predecir cuánto tiempo el desarrollo va a tomar y qué problemas pueden o no surgir); y después de que usted se pegó en las limitaciones de su enfoque actual, refactorizar para evitar los globales;

b) mira cómo se realizan en la programación orientada a objetos marcos (ver por lo menos tres o cuatro, es decir, Pastel, CodeIgniter, Zend, Symfony, Flow3) y pedir prestado algo, o cambiar a la utilización de un marco (o tal vez va a ser más seguro de que puedes hacer todo a la derecha).

1voto

Krycke Puntos 1591

He creado un sencillo pequeña clase:

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

esto puede fácil ser refactorizado para tener una función isSet( $key ) o tal vez un setAll( $array ).

EDIT: Ahora la sintaxis debe ser válida.

usted puede modificar fácilmente esta clase como la siguiente:

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

Todavía tiene que incluir el archivo en cualquier otro archivo que utiliza configuraciones, o el uso de un cargador automático.

EDIT 2:

Es prácticamente el mismo que el uso de un mundial, con la diferencia de que no necesita de estado que desea utilizar en el comienzo de cada función. Si desea utilizar Configuraciones a nivel mundial, luego de la Configs tiene que ser, de alguna manera global. A la hora de poner algo en el ámbito global, necesita argumentar si esto puede ser peligroso de la información a una de las otras clases no significaba para ver esta información... las configuraciones por defecto? Yo creo que se puede tener en el ámbito global y, a continuación, sólo se necesita algo que es fácil de modificar y personalizar.

Si usted decide que información es peligrosa, que no debe ser accesible para una clase distinta de la clase que se supone, entonces usted puede ser que desee comprobar en la inyección de Dependencia. Con la dependencia de las inyecciones de una clase tendrá un objeto en su constructor, colocándolo de forma privada en una variable para su uso. Este objeto puede ser un objeto de una clase de configuración y, a continuación, usted necesita una clase contenedora, creando en primer lugar el objeto de configuración y, a continuación, la Plantilla de objeto de la inyección de las configuraciones. Este es un diseño a menudo visto en los más complejos patrones de diseño, como por ejemplo Domain Driven Design.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X