Ámbito de las variables

El ámbito de una variable es el contexto en el cual la variable está definida. PHP tiene un ámbito de función y un ámbito global. Cualquier variable difinida fuera de una función está limitada al ámbito global. Cuando se incluye un archivo, el código contenido hereda el ámbito de la variable de la línea en la cual se incluye el archivo.

Ejemplo #1 Ejemplo de una variable de ámbito global

<?php
$a
= 1;
include
'b.inc'; // La variable $a estará disponible en el interior de b.inc
?>

Cualquier variable declarada dentro de una función o una funcíón anónima está limitada al ámbito del cuerpo de dicha función. Sin embargo, las funciones de flecha vinculan las variables desde el ámbito padre haciendo que estén disponibles dentro de la función. Si se incluye un archivo dentro de una función, las variables contenidas en el archivo llamado estarán disponibles como si se hubieran definido dentro de la función que realiza la llamada.

Ejemplo #2 Ejemplo de una variable de ámbito local

<?php
$a
= 1; // ámbito global

function test()
{
echo
$a; // La variable $a no está definida ya que se refiere a una versión local de $a
}
?>

El ejemplo anterior producirá un E_WARNING por una variable no definida (o un E_NOTICE antes de PHP 8.0.0). Esto se debe a la expresión echo hace referencia a una versión local de la variable $a, a la cual no se le ha asignado un valor dentro de su ámbito. Puede que usted note que hay una pequeña diferencia con el lenguaje C, en el que las variables globales están disponibles automáticamente dentro de la función a menos que sean expresamente sobreescritas por una definición local. Esto puede causar algunos problemas, ya que la gente podría cambiar variables globales sin darse cuenta. En PHP, las variables globales deben ser declaradas globales dentro de la función si van a ser utilizadas dentro de dicha función.

La palabra clave global

La palabra clave global se usa para vincular una variable desde el ámbito global a un ámbito local. La palabra clave puede ser usada con una lista de variables o con una sola variable. Una variable local será creada haciendo referendia a una variable global con el mismo nombre. Si no existe la variable global, la variable será creada en el ámbito global y asignado el valor null.

Ejemplo #3 Uso de global

<?php
$a
= 1;
$b = 2;

function
Suma()
{
global
$a, $b;

$b = $a + $b;
}

Suma();
echo
$b;
?>

El resultado del ejemplo sería:

3

Al declarar las variables $a y $b globales dentro de la función, todas las referencias a tales variables se referirán a la versión global. No hay límite al número de variables globales que se pueden manipular dentro de una función.

Un segundo método para acceder a las variables desde un ámbito global es usando el array especial definido por PHP $GLOBALS. El ejemplo anterior se puede reescribir así:

Ejemplo #4 Uso de $GLOBALS en lugar de global

<?php
$a
= 1;
$b = 2;

function
Suma()
{
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}

Suma();
echo
$b;
?>

El array $GLOBALS es un array asociativo con el nombre de la variable global como clave y los contenidos de dicha variable como el valor del elemento del array. $GLOBALS existe en cualquier ámbito, esto ocurre ya que $GLOBALS es una superglobal. Aquí hay un ejemplo que demuestra el poder de las superglobales:

Ejemplo #5 Ejemplo que demuestra las superglobales y el ámbito

<?php
function test_superglobal()
{
echo
$_POST['name'];
}
?>

Nota:

Utilizar una clave global fuera de una función no es un error. Esta puede ser utilizada aún si el fichero está incluido desde el interior de una función.

Uso de variables static

Otra característica importante del ámbito de las variables es la variable estática. Una variable estática existe sólo en el ámbito local de la función, pero no pierde su valor cuando la ejecución del programa abandona este ámbito. Consideremos el siguiente ejemplo:

Ejemplo #6 Ejemplo que demuestra la necesidad de variables estáticas

<?php
function test()
{
$a = 0;
echo
$a;
$a++;
}
?>

Esta función tiene poca utilidad ya que cada vez que es llamada asigna a $a el valor 0 e imprime un 0. La sentencia $a++, que incrementa la variable, no sirve para nada, ya que en cuanto la función finaliza, la variable $a desaparece. Para hacer una función útil para contar, que no pierda la pista del valor actual del conteo, la variable $a debe declararse como estática:

Ejemplo #7 Ejemplo del uso de variables estáticas

<?php
function test()
{
static
$a = 0;
echo
$a;
$a++;
}
?>

Ahora, $a se inicializa únicamente en la primera llamada a la función, y cada vez que la función test() es llamada, imprimirá el valor de $a y lo incrementa.

Las variables estáticas también proporcionan una forma de manejar funciones recursivas. La siguiente función cuenta recursivamente hasta 10, usando la variable estática $count para saber cuándo parar:

Ejemplo #8 Variables estáticas con funciones recursivas

<?php
function test()
{
static
$count = 0;

$count++;
echo
$count;
if (
$count < 10) {
test();
}
$count--;
}
?>

Antes de PHP 8.3.0, las variables estáticas solo podían ser inicializadas usando expresiones constantes. A partir de PHP 8.3.0, expresiones dinámicas (por ejemplo, llamadas a funciones) también están permitidas:

Ejemplo #9 Declarando variables estáticas

<?php
function foo(){
static
$int = 0; // correcto
static $int = 1+2; // correcto
static $int = sqrt(121); // correcto a partir de PHP 8.3.0

$int++;
echo
$int;
}
?>

Las variables estáticas dentro de funiones anónimas también persisten solo dentro de esa instancia específica de la función. Si la función anónima es recreada en cada llamada, la variable estática será reinicializada.

Ejemplo #10 Variables estáticas en funciones anónimas

<?php
function exampleFunction($input) {
$result = (static function () use ($input) {
static
$counter = 0;
$counter++;
return
"Entrada: $input, Contador: $counter\n";
});

return
$result();
}

// Las llamadas a exampleFunction recrearán la función anónima, por tanto
// la variable estática no retendrá su valor.
echo exampleFunction('A'); // Devolverá: Entrada: A, Contador: 1
echo exampleFunction('B'); // Devolverá: Entrada: B, Contador: 1
?>

A partir de PHP 8.1.0, cuando un método que usa variables estáticas es heredado (pero no sobrescrito), el método heredado compartirá ahora las variables estáticas con el método padre. Esto significa que las variables estáticas en los métodos ahora se comportan de la misma manera que las propiedades estáticas.

A partir de PHP 8.3.0, las variables estáticas pueden ser inicializadas con expresiones arbitrarias. Esto significa que las llamadas a métodos, por ejemplo, pueden ser usadas para inicializar variables estáticas.

Ejemplo #11 Uso de variables estáticas en métodos heredados

<?php
class Foo {
public static function
counter() {
static
$counter = 0;
$counter++;
return
$counter;
}
}
class
Bar extends Foo {}
var_dump(Foo::counter()); // int(1)
var_dump(Foo::counter()); // int(2)
var_dump(Bar::counter()); // int(3), antes de PHP 8.1.0 int(1)
var_dump(Bar::counter()); // int(4), antes de PHP 8.1.0 int(2)
?>

Referencias con variables global y static

PHP implementa los modificadores static y global para variables en términos de referencias. Por ejemplo, una variable global verdadera importada dentro del ámbito de una función con global crea una referencia a la variable global. Esto puede ser causa de un comportamiento inesperado, tal y como podemos comprobar en el siguiente ejemplo:

<?php
function test_global_ref() {
global
$obj;
$new = new stdClass;
$obj = &$new;
}

function
test_global_noref() {
global
$obj;
$new = new stdClass;
$obj = $new;
}

test_global_ref();
var_dump($obj);
test_global_noref();
var_dump($obj);
?>

El resultado del ejemplo sería:

NULL
object(stdClass)#1 (0) {
}

Un comportamiento similar se aplica a static. Las referencias no son almacenadas estáticamente.

<?php
function &get_instance_ref() {
static
$obj;

echo
'Objeto estático: ';
var_dump($obj);
if (!isset(
$obj)) {
$new = new stdClass;
// Asignar una referencia a la variable estática
$obj = &$new;
}
if (!isset(
$obj->property)) {
$obj->property = 1;
} else {
$obj->property++;
}
return
$obj;
}

function &
get_instance_noref() {
static
$obj;

echo
'Objeto estático: ';
var_dump($obj);
if (!isset(
$obj)) {
$new = new stdClass;
// Asignar el objeto a la variable estática
$obj = $new;
}
if (!isset(
$obj->property)) {
$obj->property = 1;
} else {
$obj->property++;
}
return
$obj;
}

$obj1 = get_instance_ref();
$aun_obj1 = get_instance_ref();
echo
"\n";
$obj2 = get_instance_noref();
$aun_obj2 = get_instance_noref();
?>

El resultado del ejemplo sería:

Objeto estático: NULL
Objeto estático: NULL

Objeto estático: NULL
Objeto estático: object(stdClass)#3 (1) {
  ["property"]=>
  int(1)
}

Este ejemplo demuestra que al asignar una referencia a una variable estática, esta no es recordada cuando se invoca la funcion &obtener_instancia_ref() por segunda vez.