PHP global en funciones
language-design (7)
Globales son malvados
Esto es cierto tanto para la palabra clave global
como para todo lo demás que abarca desde un alcance local hasta el ámbito global (estáticas, singletons, registros, constantes). No quieres usarlos. Una llamada de función no debería tener que depender de nada externo, por ejemplo
function fn()
{
global $foo; // never ever use that
$a = SOME_CONSTANT // do not use that
$b = Foo::SOME_CONSTANT; // do not use that unless self::
$c = $GLOBALS[''foo'']; // incl. any other superglobal ($_GET, …)
$d = Foo::bar(); // any static call, incl. Singletons and Registries
}
Todo esto hará que su código dependa del exterior. Lo que significa que debe conocer el estado global completo de su aplicación antes de poder llamar de manera confiable a cualquiera de estos. La función no puede existir sin ese entorno.
Usar los superglobales puede no ser un defecto obvio, pero si llamas a tu código desde una línea de comando, no tienes $_GET
o $_POST
. Si su código depende de la entrada de estos, se está limitando a un entorno web. Simplemente abstrae la solicitud en un objeto y úsala en su lugar.
En el caso de acoplar nombres de clase codificados (estáticos, constantes), su función tampoco puede existir sin que esa clase esté disponible. Eso es menos problemático cuando se trata de clases del mismo espacio de nombres, pero cuando comienzas a mezclar desde diferentes espacios de nombres, estás creando un enredo complicado.
La reutilización se ve gravemente obstaculizada por todo lo anterior. Lo mismo ocurre con las pruebas unitarias .
Además, las firmas de función mienten cuando se combina con el alcance global
function fn()
es un mentiroso, porque dice que puedo llamar a esa función sin pasarle nada. Es solo cuando miro el cuerpo de la función que aprendo que tengo que establecer el ambiente en un cierto estado.
Si su función requiere la ejecución de argumentos, explíquelos y páselos:
function fn($arg1, $arg2)
{
// do sth with $arguments
}
transmite claramente desde la firma lo que requiere ser llamado. No depende del entorno para estar en un estado específico. No tienes que hacer
$arg1 = ''foo'';
$arg2 = ''bar'';
fn();
Es una cuestión de tirar (palabra clave global) frente a empujar (argumentos). Cuando inserta / inserta dependencias, la función ya no depende del exterior. Cuando haces fn(1)
no tienes que tener una variable que sostenga 1 en algún lugar afuera. Pero cuando ingresas $one
global dentro de la función, te unes al alcance global y esperas que tenga una variable definida en alguna parte. La función ya no es independiente.
Peor aún, cuando se cambian los valores globales dentro de su función, su código rápidamente será completamente incomprensible, porque sus funciones están teniendo efectos secundarios en todo el lugar.
A falta de un mejor ejemplo, considere
function fn()
{
global $foo;
echo $foo; // side effect: echo''ing
$foo = ''bar''; // side effect: changing
}
Y luego lo haces
$foo = ''foo'';
fn(); // prints foo
fn(); // prints bar <-- WTF!!
No hay forma de ver que $foo
cambió de estas tres líneas. ¿Por qué llamar a la misma función con los mismos argumentos cambia de repente su salida o cambia un valor en el estado global? Una función debe hacer X para una entrada definida Y. Siempre.
Esto se vuelve aún más severo cuando se usa OOP, porque OOP trata sobre la encapsulación y al llegar al alcance global, está rompiendo la encapsulación. Todos estos Singletons y Registries que ve en frameworks son olores de código que deberían eliminarse a favor de Dependency Injection. Desacoplar su código
Más recursos:
¿Cuál es la utilidad de la palabra clave global ?
¿Hay alguna razón para preferir un método a otro?
- ¿Seguridad?
- ¿Actuación?
- ¿Algo más?
Método 1:
function exempleConcat($str1, $str2)
{
return $str1.$str2;
}
Método 2:
function exempleConcat()
{
global $str1, $str2;
return $str1.$str2;
}
¿Cuándo tiene sentido usar global
?
Para mí, parece ser peligroso ... pero puede ser simplemente una falta de conocimiento. Estoy interesado en documentación (por ejemplo, con código de ejemplo, enlace a la documentación ...) razones técnicas.
¡Gracias por adelantado!
Generosidad
Esta es una buena pregunta general sobre el tema, yo (@Gordon) estoy ofreciendo una recompensa para obtener respuestas adicionales. No importa si tu respuesta está de acuerdo con la mía o da un punto de vista diferente. Dado que el tema global
aparece de vez en cuando, podríamos usar una buena respuesta "canónica" para vincularla.
Creo que todos han explicado bastante sobre los aspectos negativos de los globales. Así que agregaré los aspectos positivos así como las instrucciones para el uso apropiado de los globales:
El objetivo principal de los globales fue compartir información entre funciones. cuando no había nada como una clase, el código php consistía en un conjunto de funciones. Algunas veces necesitaría compartir información entre funciones. Normalmente, se usaba global para hacer esto con el riesgo de corromper los datos haciéndolos globales.
Ahora, antes de que algún feliz sea afortunado, Simpleton comienza un comentario sobre la inyección de dependencia. Me gustaría preguntarle cómo el usuario de una función como el ejemplo
get_post(1)
conocería todas las dependencias de la función. También considere que las dependencias pueden diferir de
versión a versión y servidor a servidor. El principal problema con la inyección de dependencia es que las dependencias deben conocerse de antemano. En una situación donde esto no es posible o las variables globales no deseadas son la única forma de lograr este objetivo.Debido a la creación de la clase, ahora las funciones comunes se pueden agrupar fácilmente en una clase y compartir datos. A través de implementaciones como Mediators, incluso objetos no relacionados pueden compartir información. Esto ya no es necesario.
Otro uso para globales es para propósitos de configuración. Principalmente al comienzo de una secuencia de comandos antes de que se hayan cargado los cargadores automáticos, se hayan realizado conexiones a la base de datos, etc.
Durante la carga de recursos, los globales se pueden usar para configurar los datos (es decir, qué base de datos usar donde se encuentran los archivos de la biblioteca, la URL del servidor, etc.). La mejor manera de hacerlo es mediante el uso de la función de
define()
ya que estos valores no cambian con frecuencia y pueden colocarse fácilmente en un archivo de configuración.El uso final para globales es para mantener los datos comunes (es decir, CRLF, DIRECCIÓN_IMAGENADA, IMAGEN_DIR_URL), banderas de estado legibles por el ser humano (es decir, ITERATOR_IS_RECURSIVE). Aquí, los globales se usan para almacenar información que se debe usar en toda la aplicación, lo que permite que se modifiquen y que estos cambios aparezcan en toda la aplicación.
El patrón singleton se hizo popular en php durante php4 cuando cada instancia de un objeto ocupaba memoria. El singleton ayudó a salvar el ram solo permitiendo que se creara una instancia de un objeto. Antes de las referencias, incluso la inyección de dependencias habría sido una mala idea.
La nueva implementación de php de objetos de PHP 5.4+ soluciona la mayoría de estos problemas, puede pasar objetos con poca o ninguna penalización. Esto ya no es necesario.
Otro uso para singletons es la instancia especial donde solo debe existir una instancia de un objeto a la vez, esa instancia puede existir antes / después de la ejecución del script y ese objeto se comparte entre diferentes scripts / servidores / idiomas, etc. Aquí un patrón singleton resuelve el solución bastante bien.
Entonces, en conclusión, si estás en la posición 1, 2 o 3, usar un global sería razonable. Sin embargo, en otras situaciones se debe usar el Método 1.
Siéntase libre de actualizar cualquier otra instancia en la que se deban usar globales.
En pocas palabras, rara vez hay una razón para global
y nunca una buena en el código PHP moderno en mi humilde opinión. Especialmente si está utilizando PHP 5. Y especialmente si está desarrollando código orientado a objetos.
Los Globals afectan negativamente el mantenimiento, la legibilidad y la capacidad de prueba del código. Muchos usos de global
pueden y deben ser reemplazados con Inyección de Dependencia o simplemente pasando el objeto global como un parámetro.
function getCustomer($db, $id) {
$row = $db->fetchRow(''SELECT * FROM customer WHERE id = ''.$db->quote($id));
return $row;
}
La única gran razón contra global
es que significa que la función depende de otro alcance. Esto se ensuciará muy rápido.
$str1 = ''foo'';
$str2 = ''bar'';
$str3 = exampleConcat();
vs.
$str = exampleConcat(''foo'', ''bar'');
Exigir que $str1
y $str2
sean configurados en el alcance de la llamada para que la función funcione significa que usted introduce dependencias innecesarias. No puede cambiar el nombre de estas variables en este ámbito más sin cambiar el nombre de ellas también en la función, y por lo tanto también en todos los demás ámbitos está utilizando esta función. Esto pronto se convierte en un caos cuando tratas de hacer un seguimiento de tus nombres de variable.
global
es un mal patrón incluso para incluir elementos globales como $db
resources. Llegará el día en que quieras cambiar el nombre de $db
pero no podrás hacerlo, porque toda tu aplicación depende del nombre.
Limitar y separar el alcance de las variables es esencial para escribir cualquier aplicación a medio camino complejo.
No dude en utilizar funciones globales de palabras clave dentro de PHP. Especialmente no tomemos a personas que están predicando de forma extravagante / gritando cómo los globales son "malvados" y demás.
En primer lugar, porque lo que usa depende totalmente de la situación y el problema, y no hay NINGUNA solución / forma de hacer nada en la codificación. Dejando totalmente de lado la falacia de los adjetivos indefinibles, subjetivos y religiosos como "mal" en la ecuación.
Caso en punto :
Wordpress y su ecosistema utilizan palabras clave globales en sus funciones. Sea el código OOP o no OOP.
Y a partir de ahora, Wordpress es básicamente el 18.9% de Internet, y está ejecutando los enormes megasitios / aplicaciones de innumerables gigantes que van desde Reuters a Sony, a NYT, a CNN.
Y lo hace bien.
El uso de palabras clave globales dentro de las funciones libera a Wordpress de la saturación MASIVA que ocurriría dado su enorme ecosistema. Imagine que cada función pregunta / pasa cualquier variable que se necesite de otro complemento, núcleo y retorno. Agregado con interdependencias de plugins, que terminarían en una pesadilla de variables, o una pesadilla de matrices pasadas como variables. UN INFIERNO para rastrear, un infierno para depurar, un infierno para desarrollar. Inanely masiva huella de memoria debido a la hinchazón del código y la hinchazón variable también. Más difícil de escribir también.
Puede haber personas que critiquen a Wordpress, su ecosistema, sus prácticas y lo que ocurre en esas partes.
Inútil, ya que este ecosistema es más o menos el 20% de Internet aproximadamente completo. Aparentemente, SÍ funciona, hace su trabajo y más. Lo que significa que es lo mismo para la palabra clave global.
Otro buen ejemplo es el fundamentalismo de "los iframes son malvados". Hace una década era una herejía usar iframes. Y había miles de personas predicando contra ellos en Internet. Luego viene Facebook, luego viene el aspecto social, ahora los marcos flotantes están en todas partes, desde los cuadros ''me gusta'' hasta la autenticación, y listo, todos se callan. Hay quienes aún no se callaron, con razón o sin ella. Pero ya sabes qué, la vida continúa a pesar de esas opiniones, e incluso los que predicaban en contra de los iframes hace una década ahora tienen que usarlos para integrar varias aplicaciones sociales a las aplicaciones propias de su organización sin decir una palabra.
......
El fundamentalismo codificador es algo muy, muy malo. Un pequeño porcentaje de nosotros puede ser agraciado con el trabajo cómodo en una empresa monolítica sólida que tiene suficiente influencia para soportar el cambio constante en la tecnología de la información y las presiones que trae en cuanto a competencia, tiempo, presupuesto y otras consideraciones, y por lo tanto puede practicar fundamentalismo y estricto apego a los "males" o "bienes" percibidos. Se trata de posiciones cómodas que recuerdan a edades avanzadas, incluso si los ocupantes son jóvenes.
Sin embargo, para la mayoría, el mundo es un mundo en constante cambio en el que deben ser de mente abierta y práctica. No hay lugar para el fundamentalismo, dejemos de lado las palabras clave escandalosas como ''mal'' en las líneas de vanguardia de la tecnología de la información.
Simplemente use lo que sea más conveniente para el problema A MANO, con las consideraciones apropiadas para el futuro a corto, mediano y largo plazo. No dude en utilizar cualquier función o enfoque, ya que tiene una animosidad ideológica rampante contra él, entre cualquier subconjunto de codificador dado.
Ellos no harán tu trabajo. Vas a. Actúa de acuerdo a tus circunstancias.
No tiene sentido hacer una función concat utilizando la palabra clave global.
Se usa para acceder a variables globales como un objeto de base de datos.
Ejemplo:
function getCustomer($id) {
global $db;
$row = $db->fetchRow(''SELECT * FROM customer WHERE id = ''.$db->quote($id));
return $row;
}
Se puede usar como una variación en el patrón de Singleton
Globales son inevitables.
Es una discusión antigua, pero aún me gustaría agregar algunas reflexiones porque las extraño en las respuestas antes mencionadas. Esas respuestas simplifican lo que es global y las soluciones actuales no son soluciones para el problema. El problema es: ¿cuál es la forma correcta de tratar con una variable global y el uso de la palabra clave global? Para eso, primero tenemos que examinar y describir qué es un global.
Eche un vistazo a este código de Zend, y comprenda que no sugiero que Zend esté mal escrito:
class DecoratorPluginManager extends AbstractPluginManager
{
/**
* Default set of decorators
*
* @var array
*/
protected $invokableClasses = array(
''htmlcloud'' => ''Zend/Tag/Cloud/Decorator/HtmlCloud'',
''htmltag'' => ''Zend/Tag/Cloud/Decorator/HtmlTag'',
''tag'' => ''Zend/Tag/Cloud/Decorator/HtmlTag'',
);
Hay muchas dependencias invisibles aquí. Esas constantes son en realidad clases. También puede ver require_once en algunas páginas de este marco. Require_once es una dependencia global, por lo tanto, crea dependencias externas. Eso es inevitable para un marco. ¿Cómo se puede crear una clase como DecoratorPluginManager sin un montón de código externo del que depende? No puede funcionar sin muchos extras. Usando el marco Zend, ¿alguna vez ha cambiado la implementación de una interfaz? Una interfaz es de hecho global.
Otra aplicación utilizada a nivel mundial es Drupal. Están muy preocupados por el diseño adecuado, pero al igual que cualquier gran marco, tienen muchas dependencias externas. Echa un vistazo a los globales en esta página:
/**
* @file
* Initiates a browser-based installation of Drupal.
*/
/**
* Root directory of Drupal installation.
*/
define(''DRUPAL_ROOT'', getcwd());
/**
* Global flag to indicate that site is in installation mode.
*/
define(''MAINTENANCE_MODE'', ''install'');
// Exit early if running an incompatible PHP version to avoid fatal errors.
if (version_compare(PHP_VERSION, ''5.2.4'') < 0) {
print ''Your PHP installation is too old. Drupal requires at least PHP 5.2.4. See the <a href="http://drupal.org/requirements">system requirements</a> page for more information.'';
exit;
}
// Start the installer.
require_once DRUPAL_ROOT . ''/includes/install.core.inc'';
install_drupal();
¿Alguna vez ha escrito una redirección a la página de inicio de sesión? Eso está cambiando un valor global. (Y entonces no está diciendo ''WTF'', que considero como una buena reacción a la mala documentación de su aplicación.) El problema con los globales no es que sean globales, sino que los necesita para tener una aplicación significativa. El problema es la complejidad de la aplicación general, que puede convertirlo en una pesadilla. Las sesiones son globales, $ _POST es un global, DRUPAL_ROOT es un global, el includes / install.core.inc ''es un global no modificable. Hay un gran mundo fuera de cualquier función que se requiera para permitir que esa función haga su trabajo.
La respuesta de Gordon es incorrecta, porque sobrevalora la independencia de una función y llamar a una función mentirosa simplifica demasiado la situación. Las funciones no mienten y cuando le echas un vistazo a su ejemplo, la función está diseñada incorrectamente; su ejemplo es un error. (Por cierto, estoy de acuerdo con esta conclusión de que uno debe desacoplar el código.) La respuesta de engaño no es realmente una definición adecuada de la situación. Las funciones siempre funcionan dentro de un alcance más amplio y su ejemplo es demasiado simplista. Todos estaremos de acuerdo con él en que esa función es completamente inútil, porque devuelve una constante. Esa función es, de todos modos, un mal diseño. Si quiere mostrar que la práctica es mala, por favor, venga con un ejemplo relevante. Renombrar variables a lo largo de una aplicación no es gran cosa teniendo un buen IDE (o una herramienta). La pregunta es sobre el alcance de la variable, no la diferencia en el alcance con la función. Hay un momento adecuado para que una función desempeñe su función en el proceso (es por eso que se crea en primer lugar) y en ese momento adecuado puede influir en el funcionamiento de la aplicación como un todo, por lo tanto, también se trabaja en variables globales . La respuesta de xzyfer es una afirmación sin argumentación. Los Globals están presentes en una aplicación si tiene funciones de procedimiento o diseño OOP. Las siguientes dos formas de cambiar el valor de un global son esencialmente las mismas:
function xzy($var){
global $z;
$z = $var;
}
function setZ($var){
$this->z = $var;
}
En ambas instancias se cambia el valor de $ z dentro de una función específica. En ambos sentidos de programación, puede hacer esos cambios en muchos otros lugares del código. Podría decir que usando global podría llamar a $ z en cualquier lugar y cambiar allí. Sí tu puedes. ¿Pero lo harás? Y cuando se hace en lugares inadecuados, ¿no debería llamarse un error?
Bob Fanger comenta sobre xzyfer.
¿Debería alguien usar algo y especialmente la palabra clave ''global''? No, pero al igual que cualquier tipo de diseño, intente analizar de qué depende y de qué depende. Intenta averiguar cuándo cambia y cómo cambia. Cambiar los valores globales solo debería ocurrir con aquellas variables que pueden cambiar con cada solicitud / respuesta. Es decir, solo a aquellas variables que pertenecen al flujo funcional de un proceso, no a su implementación técnica. La redirección de una URL a la página de inicio de sesión pertenece al flujo funcional de un proceso, la clase de implementación utilizada para una interfaz a la implementación técnica. Puede cambiar esto último durante las diferentes versiones de la aplicación, pero no debe cambiarlas con cada solicitud / respuesta.
Para comprender mejor cuándo es un problema trabajar con globales y con la palabra clave global, cuando no esté, le presentaré la siguiente oración, que viene de Wim de Bie cuando escribo sobre blogs: ''Personal yes, private no''. Cuando una función está cambiando el valor de una variable global en función de su propio funcionamiento, entonces voy a llamar a ese uso privado de una variable global y un error. Pero cuando el cambio de la variable global se hace para el procesamiento correcto de la aplicación como un todo, como la redirección del usuario a la página de inicio de sesión, entonces es que, en mi opinión, posiblemente sea un buen diseño, no por definición malo y ciertamente no un antipatrón.
En retrospectiva a las respuestas de Gordon, deceze y xzyfer: todos tienen ''sí'' (y errores) privados como ejemplos. Es por eso que se oponen al uso de globales. Yo también lo haría Sin embargo, ellos no vienen con "sí personal, no privado", ejemplos como lo he hecho en esta respuesta varias veces.