Referencia: ¿Qué es el alcance de la variable, a qué variables se puede acceder desde dónde y qué son los errores de "variable no definida"?
Nota: Esta es una pregunta de referencia para tratar con el alcance variable en PHP. Cierra cualquiera de las muchas preguntas que se ajustan a este patrón como un duplicado de este.
¿Qué es el "alcance variable" en PHP? ¿Se puede acceder a las variables de un archivo .php en otro? ¿ Por qué a veces recibo errores de "variable no definida" ?
¿Qué es el "alcance variable"?
Las variables tienen un "alcance" limitado o "lugares desde los cuales son accesibles". El hecho de que haya escrito $foo = 'bar';
una vez en algún lugar de su aplicación no significa que pueda consultarlo $foo
desde cualquier otro lugar dentro de la aplicación. La variable $foo
tiene un alcance determinado dentro del cual es válida y solo el código en el mismo alcance tiene acceso a la variable.
¿Cómo se define un alcance en PHP?
Muy simple: PHP tiene alcance de función . Ese es el único tipo de separador de alcance que existe en PHP. Las variables dentro de una función solo están disponibles dentro de esa función. Las variables fuera de las funciones están disponibles en cualquier lugar fuera de las funciones, pero no dentro de ninguna función. Esto significa que hay un alcance especial en PHP: el alcance global . Cualquier variable declarada fuera de cualquier función está dentro de este alcance global.
Ejemplo:
<?php
$foo = 'bar';
function myFunc() {
$baz = 42;
}
$foo
está en el ámbito global , $baz
está en el ámbito local dentro myFunc
. Sólo el código interior myFunc
tiene acceso a $baz
. Sólo el código externo myFunc
tiene acceso $foo
. Ninguno tiene acceso al otro:
<?php
$foo = 'bar';
function myFunc() {
$baz = 42;
echo $foo; // doesn't work
echo $baz; // works
}
echo $foo; // works
echo $baz; // doesn't work
Alcance y archivos incluidos
Los límites de los archivos no separan el alcance:
a.php
<?php
$foo = 'bar';
b.php
<?php
include 'a.php';
echo $foo; // works!
Se aplican las mismas reglas al include
código d que a cualquier otro código: solo function
un alcance separado. A efectos del alcance, puede pensar en incluir archivos como copiar y pegar código:
c.php
<?php
function myFunc() {
include 'a.php';
echo $foo; // works
}
myFunc();
echo $foo; // doesn't work!
En el ejemplo anterior, a.php
se incluyó dentro myFunc
, cualquier variable dentro a.php
solo tiene alcance de función local. El hecho de que parezcan estar en el alcance global a.php
no significa necesariamente que lo estén, en realidad depende del contexto en el que se incluye/ejecuta ese código.
¿Qué pasa con las funciones dentro de funciones y clases?
Cada nueva function
declaración introduce un nuevo alcance, así de simple.
funciones (anónimas) dentro de funciones
function foo() {
$foo = 'bar';
$bar = function () {
// no access to $foo
$baz = 'baz';
};
// no access to $baz
}
clases
$foo = 'foo';
class Bar {
public function baz() {
// no access to $foo
$baz = 'baz';
}
}
// no access to $baz
¿Para qué sirve el alcance?
Lidiar con problemas de alcance puede parecer molesto, ¡pero un alcance variable limitado es esencial para escribir aplicaciones complejas! Si cada variable que declara estuviera disponible desde cualquier otro lugar dentro de su aplicación, estaría revisando todas sus variables sin una forma real de rastrear qué cambia qué. Hay tantos nombres sensatos que puede darle a sus variables, probablemente quiera usar la variable " $name
" en más de un lugar. Si solo pudiera tener este nombre de variable único una vez en su aplicación, tendría que recurrir a esquemas de nombres realmente complicados para asegurarse de que sus variables sean únicas y que no esté cambiando la variable incorrecta del código incorrecto.
Observar:
function foo() {
echo $bar;
}
Si no hubiera alcance, ¿qué haría la función anterior? ¿ De dónde $bar
viene? ¿Qué estado tiene? ¿Está siquiera inicializado? ¿Tienes que comprobarlo cada vez? Esto no es mantenible. Lo que nos lleva a...
Cruzando los límites del alcance
La forma correcta: pasar variables dentro y fuera
function foo($bar) {
echo $bar;
return 42;
}
La variable $bar
entra explícitamente en este ámbito como argumento de función. Con solo mirar esta función, queda claro de dónde se originan los valores con los que trabaja. Luego devuelve explícitamente un valor. La persona que llama tiene la confianza de saber con qué variables funcionará la función y de dónde provienen sus valores de retorno:
$baz = 'baz';
$blarg = foo($baz);
Ampliar el alcance de las variables a funciones anónimas
$foo = 'bar';
$baz = function () use ($foo) {
echo $foo;
};
$baz();
La función anónima incluye explícitamente $foo
desde su alcance circundante. Tenga en cuenta que esto no es lo mismo que el alcance global .
La forma incorrecta:global
Como se dijo antes, el alcance global es algo especial y las funciones pueden importar variables explícitamente desde él:
$foo = 'bar';
function baz() {
global $foo;
echo $foo;
$foo = 'baz';
}
Esta función utiliza y modifica la variable global $foo
. ¡No hagas esto! (A menos que realmente sepas lo que estás haciendo, e incluso entonces: ¡no lo sepas!)
Todo lo que ve la persona que llama a esta función es esto:
baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!
No hay indicios de que esta función tenga efectos secundarios , pero los tiene. Esto muy fácilmente se convierte en un lío ya que algunas funciones siguen modificándose y requiriendo algún estado global. Quiere que las funciones sean sin estado , que actúen solo sobre sus entradas y devuelvan una salida definida, sin importar cuántas veces las llame.
Debe evitar utilizar el alcance global en la medida de lo posible; ciertamente no debería "sacar" variables del ámbito global al ámbito local.
Aunque no se puede acceder a las variables definidas dentro del alcance de una función desde el exterior, eso no significa que no pueda usar sus valores después de que se complete esa función. PHP tiene una palabra clave bien conocida static
que se usa ampliamente en PHP orientado a objetos para definir métodos y propiedades estáticos, pero se debe tener en cuenta que static
también se puede usar dentro de funciones para definir variables estáticas.
¿Qué es la 'variable estática'?
La variable estática se diferencia de la variable ordinaria definida en el alcance de la función en el caso de que no pierde valor cuando la ejecución del programa sale de este alcance. Consideremos el siguiente ejemplo de uso de variables estáticas:
function countSheep($num) {
static $counter = 0;
$counter += $num;
echo "$counter sheep jumped over fence";
}
countSheep(1);
countSheep(2);
countSheep(3);
Resultado:
1 sheep jumped over fence
3 sheep jumped over fence
6 sheep jumped over fence
Si hubiéramos definido $counter
sin static
, cada valor repetido sería el mismo que $num
el parámetro pasado a la función. El uso static
permite construir este contador simple sin soluciones adicionales.
Casos de uso de variables estáticas
- Para almacenar valores entre llamadas posteriores a la función.
- Para almacenar valores entre llamadas recursivas cuando no hay forma (o ningún propósito) de pasarlos como parámetros.
- Para almacenar en caché el valor que normalmente es mejor recuperar una vez. Por ejemplo, resultado de leer un archivo inmutable en el servidor.
Trucos
La variable estática existe solo en el alcance de una función local. No se puede acceder a él fuera de la función en la que se ha definido. Por lo tanto, puede estar seguro de que mantendrá su valor sin cambios hasta la próxima llamada a esa función.
La variable estática solo se puede definir como escalar o como expresión escalar (desde PHP 5.6). Asignarle otros valores conduce inevitablemente al fracaso, al menos en el momento de escribir este artículo. Sin embargo, puedes hacerlo en la siguiente línea de tu código:
function countSheep($num) {
static $counter = 0;
$counter += sqrt($num);//imagine we need to take root of our sheep each time
echo "$counter sheep jumped over fence";
}
Resultado:
2 sheep jumped over fence
5 sheep jumped over fence
9 sheep jumped over fence
La función estática es algo "compartida" entre métodos de objetos de la misma clase. Es fácil de entender viendo el siguiente ejemplo:
class SomeClass {
public function foo() {
static $x = 0;
echo ++$x;
}
}
$object1 = new SomeClass;
$object2 = new SomeClass;
$object1->foo(); // 1
$object2->foo(); // 2 oops, $object2 uses the same static $x as $object1
$object1->foo(); // 3 now $object1 increments $x
$object2->foo(); // 4 and now his twin brother
Esto sólo funciona con objetos de la misma clase. Si los objetos son de diferentes clases (incluso se extienden entre sí), el comportamiento de las variables estáticas será el esperado.
¿Es la variable estática la única forma de mantener los valores entre llamadas a una función?
Otra forma de mantener valores entre llamadas a funciones es utilizar cierres. Los cierres se introdujeron en PHP 5.3. En dos palabras, le permiten limitar el acceso a algún conjunto de variables dentro del alcance de una función a otra función anónima que será la única forma de acceder a ellas. Estar en variables de cierre puede imitar (más o menos exitosamente) conceptos de programación orientada a objetos como 'constantes de clase' (si se pasaron en cierre por valor) o 'propiedades privadas' (si se pasaron por referencia) en programación estructurada.
Este último en realidad permite utilizar cierres en lugar de variables estáticas. Qué usar siempre depende del desarrollador, pero debe mencionarse que las variables estáticas son definitivamente útiles cuando se trabaja con recursiones y merecen ser notadas por los desarrolladores.
No publicaré una respuesta completa a la pregunta, ya que los existentes y el manual de PHP hacen un gran trabajo al explicar la mayor parte de esto.
Pero un tema que se pasó por alto fue el de las superglobales , incluidas las comúnmente utilizadas $_POST
, $_GET
, $_SESSION
, etc. Estas variables son matrices que siempre están disponibles, en cualquier ámbito, sin una global
declaración.
Por ejemplo, esta función imprimirá el nombre del usuario que ejecuta el script PHP. La variable está disponible para la función sin ningún problema.
<?php
function test() {
echo $_ENV["user"];
}
La regla general de "los globales son malos" normalmente se modifica en PHP a "los globales son malos pero los superglobales están bien", siempre y cuando no se los esté usando mal. (Todas estas variables se pueden escribir, por lo que podrían usarse para evitar la inyección de dependencia si fuera realmente terrible).
No se garantiza que estas variables estén presentes; un administrador puede deshabilitar algunos o todos usando la variables_order
directiva en php.ini
, pero este no es un comportamiento común.
Una lista de las superglobales actuales:
$GLOBALS
- Todas las variables globales en el script actual.$_SERVER
- Información sobre el servidor y entorno de ejecución.$_GET
- Valores pasados en la cadena de consulta de la URL, independientemente del método HTTP utilizado para la solicitud$_POST
- Valores pasados en una solicitud HTTP POST conapplication/x-www-form-urlencoded
tiposmultipart/form-data
MIME$_FILES
- Archivos pasados en una solicitud HTTP POST con unmultipart/form-data
tipo MIME$_COOKIE
- Cookies pasadas con la solicitud actual.$_SESSION
- Variables de sesión almacenadas internamente por PHP$_REQUEST
- Normalmente es una combinación de$_GET
y$_POST
, pero a veces$_COOKIES
. El contenido está determinado por larequest_order
directiva enphp.ini
.$_ENV
- Las variables de entorno del script actual.