Escopo de variáveis

O escopo de uma variável é o contexto onde ela está definida. O PHP tem um escopo de função e um escopo global. Qualquer variável definida fora de uma função está limitada ao escopo global. Quando um arquivo é incluído, o código que ele contém herda o escopo da variável da linha na qual a inclusão ocorre.

Example #1 Exemplo de escopo global de variável

<?php
$a = 1;
include 'b.inc'; // Variável $a estará disponível dentro de b.inc
?>

Qualquer variável criada dentro de uma função nomeada ou de uma função anônima é limitada ao escopo do corpo da função. Entretanto, funções de seta vinculam variáveis ​​do escopo pai para disponibilizá-las dentro do corpo. Se uma inclusão de arquivo ocorrer dentro de uma função dentro do arquivo chamador, as variáveis ​​contidas no arquivo chamado estarão disponíveis como se tivessem sido definidas dentro da função chamadora.

Example #2 Exemplo de escopo local de variável

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

function test()
{
    echo $a; // Variável $a é indefinida já que refere-se a uma versão local de $a
}
?>

O exemplo acima irá gerar um E_WARNING de variável indefinida (ou um E_NOTICE antes do PHP 8.0.0). Isto acontece 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 a menos que tenham sido substituídas por uma definição local. Isto pode causar problemas quando uma variável global for inadvertidamente modificada. No PHP, as variáveis globais precisam ser declaradas como globais dentro de uma função, se elas precisarem ser utilizadas na função.

A palavra-chave global

A palavra-chave global é usada para vincular uma variável de um escopo global a um escopo local. A palavra-chave pode ser usada com uma lista de variáveis ​​ou com uma única variável. Uma variável local será criada referenciando a variável global de mesmo nome. Se a variável global não existir, a variável será criada no escopo global e receberá o valor null.

Example #3 Usando global

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

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

    $b = $a + $b;
}

Soma();
echo $b;
?>

O exemplo acima produzirá:

3

Declarar $a e $b como globais na função fará com que todas as referências a essas variáveis refiram-se às versões globais. 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:

Example #4 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, com o nome da variável global na chave e o conteúdo dessa variável no valor do elemento do array. Veja que $GLOBALS existe em qualquer escopo, isto porque $GLOBALS é uma superglobal. Eis um exemplo demonstrando o poder das superglobais:

Example #5 Exemplo demonstrando superglobals e escopos

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

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

Utilizando variáveis static

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

Example #6 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 exibe 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 de contagem útil, que não perderá a contagem atual, a variável $a será declarada como estática:

Example #7 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, exibirá o valor de $a e depois o incrementará.

Variáveis estáticas fornecem uma solução para lidar com funções recursivas. A seguinte função recursiva conta até 10, utilizando a variável estática $count para saber quando parar:

Example #8 Variáveis estáticas em funções recursivas

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

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

Antes do PHP 8.3.0, variáveis estáticas somente poderiam ser inicializadas usando uma expressão constante. A partir do PHP 8.3.0, expressões dinâmicas (por exemplos, chamadas de funções) também são permitidas:

Example #9 Declarando variáveis estáticas

<?php
function foo(){
    static $int = 0;          // correro
    static $int = 1+2;        // correto
    static $int = sqrt(121);  // correto a partir do PHP 8.3

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

Example #10 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)
?>

Referências 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 à 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;
        // Atribui uma referencia à 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;
        // Atribui o objeto à 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 estático: NULL
Objeto estático: NULL

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

Este exemplo demonstra que ao atribuir uma referência a uma variável estática, ela não será lembrada quando a função &get_instance_ref() for chamada uma segunda vez.