Escopo de variáveis

O escopo de uma variável é o contexto onde foi definida. A maioria das variáveis do PHP tem somente um único escopo. Este escopo único abrange também aqruivos inseridos por include e require. Por exemplo:

<?php
$a
= 1;
include
'b.inc';
?>

Aqui a variável $a estará disponível no script incluído b.inc. Entretanto, nas funções definidas pelo usuário, um escopo de função local é introduzido. Qualquer variável utilizada dentro de uma função é, por padrão, limitada ao escopo local da função. Por exemplo:

<?php
$a
= 1; /* escopo global */

function Teste()
{
echo
$a; /* referência uma variável do escopo local (não definida) */
}

Teste();
?>

Este script geraá uma aviso de variável não definida de nível E_WARNING (ou nível E_NOTICE anteriormente ao PHP 8.0.0). Entretanto, se a configuração INI display_errors estiver configurada para esconder tais diagnósticos, então nada será impresso. Isto ocorre porque a instrução echo refere-se a uma versão local da variável $a, e não possui nenhum valor atribuído neste escopo. Pode-se perceber que esta é uma pequena diferença em relação à linguagem C, em que variáveis globais estão automaticamente disponíveis para funções sem sobrescrever uma definição local. Isto pode causar problemas quando inadvertidamente modificar-se uma variável global. No PHP, as variáveis globais precisam ser declaradas como globais dentro de uma função, se for utilizada em uma.

A palavra-chave global

Primeiro, um exemplo de global:

Exemplo #1 Usando global

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

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

$b = $a + $b;
}

Soma();
echo
$b;
?>

O script acima imprimirá 3. Declarando $a e $b globais na função, fará com que todas as referências a essas variáveis referenciam a versão global. Não há um limite para o número de variáveis globais que podem ser manipuladas por uma função.

Uma segunda maneira de acessar variáveis do escopo global é utilizando o array especial $GLOBALS definido pelo PHP. O exemplo anterior poderia ser reescrito como:

Exemplo #2 Usando $GLOBALS no lugar de global

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

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

Soma();
echo
$b;
?>

O array $GLOBALS é um array associativo, sendo o nome da variável global a chave do array e o seu conteúdo da variável como o valor do elemento do array. Veja que $GLOBALS existe em qualquer escopo, isto porque $GLOBALS é uma superglobal. Segue um exemplo demonstrando o poder das superglobais:

Exemplo #3 Exemplo demonstrando superglobals e escopos

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

Nota:

Utilizar a instrução global fora de uma função não é um erro. Deve ser utilizado se um arquivo for incluído dentro de uma função.

Utilizando variáveis static

Outro recurso importante do escopo de variáveis é a variável static. Uma variável estática existe somente no escopo local da função, mas não perde seu valor quando o nível de execução do programa deixa o escopo. Considere o seguinte exemplo:

Exemplo #4 Exemplo demonstrando a necessidade de variáveis estáticas

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

Essa função é inútil, já que cada vez que é chamada, define $a com o valor 0, e imprime 0. A instrução $a++ , que aumenta o valor da variável, não tem sentido já que assim que a função termina a variável $a desaparece. Para fazer um função contadora, que não perderá a conta atual, a variável $a será declarada como estática:

Exemplo #5 Exemplo de uso de variáveis estáticas

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

Agora, a variável $a é inicializada apenas na primeira chamada da função e cada vez que a função test() for chamada, imprimirá o valor de $a e depois o incrementará.

Variáveis estáticas fornecem uma solução para lidar com funções recursivas. Uma função recursiva é aquela que chama a si mesma. Cuidados devem ser tomados quando escrever funções recursivas porque é possível que ela continue na recursão indefinidamente. Deve-se assegurar que há uma maneira adequada de terminar a recursão. A seguinte função recursiva conta até 10, utilizando a variável estática $count para saber quando parar:

Exemplo #6 Variáveis estáticas em funções recursivas

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

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

Variáveis estáticas podem ser declaradas como visto nos exemplos acima. É possível atribuir valores a essas variáveis, que são resultados de expressões, porém não é possível usar nenhuma função, o que causará um erro de interpretação.

Exemplo #7 Declarando variáveis estáticas

<?php
function foo(){
static
$int = 0; // correro
static $int = 1+2; // correto (a partir do PHP 5.6)
static $int = sqrt(121); // errado (é uma expressão)

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

A partir do PHP 8.1.0, quando um método com variáveis estáticas é herdado (mas não sobrescrito), o método herdado não compartilhará as variáveis estáticas do método acima. Isso significa que variáveis estáticas nos métodos agora se comportam da mesma forma que propriedades estáticas.

Exemplo #8 Uso de variáveis estáticas em métodos herdados

<?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), e antes do PHP 8.1.0 int(1)
var_dump(Bar::counter()); // int(4), e antes do PHP 8.1.0 int(2)
?>

Nota:

Declarações estáticas são resolvidas em tempo de compilação.

Referencias em variáveis global e static

O PHP implementa os modificadores static e global para variáveis, em termo de referências. Por exemplo, uma variável global verdadeira, importada dentro do escopo de uma função com a instrução global, na verdade, cria uma referência para a variável global. Isto pode levar a comportamentos imprevisíveis nos seguintes casos:

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

O exemplo acima produzirá:

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

Um comportamento similar se aplica a declaração static. Referências não são armazenadas estaticamente:

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

echo
'Objeto estático: ';
var_dump($obj);
if (!isset(
$obj)) {
$new = new stdClass;
// Assimila uma referencia a variável 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;
// Assimila o objeto para a variável estática
$obj = $new;
}
if (!isset(
$obj->property)) {
$obj->property = 1;
} else {
$obj->property++;
}
return
$obj;
}

$obj1 = get_instance_ref();
$still_obj1 = get_instance_ref();
echo
"\n";
$obj2 = get_instance_noref();
$still_obj2 = get_instance_noref();
?>

O exemplo acima produzirá:

Objeto estatico: NULL
Objeto estatico: NULL

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

Este exemplo demonstra que, atribuir uma referência a uma variável estática, fará com que ela não se lembre quando chamou a função &get_instance_ref() uma segunda vez.