Forum und email

Változók hatásköre

[Ezt a fejezetet érdemes elolvasni, még akkor is, ha profi vagy valamilyen programozási nyelvben, mert a PHP tartogat egy-két érdekes meglepetést...]

A változó hatásköre az a környezet, amelyben a változó definiált. A legtöbb esetben minden PHP változónak egyetlen hatásköre van. Ez az egyetlen hatáskör kiterjed az include és a require segítségével használt fájlokra is. Például:

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

Itt az $a változó elérhető lesz az beillesztett b.inc szkriptben is. A felhasználói függvényekkel a lokális függvényhatáskör kerül bevezetésre. Alapértelmezés szerint minden, függvényen belül használt változó ebbe a lokális függvényhatáskörbe tartozik, például:

<?php
$a 
1/* globális hatáskör */

function Test ()
{
    echo 
$a/* egy helyi változót vár */
}

Test();
?>

Ez a szkript nem fog semmilyen kimenetet sem eredményezni, mivel az echo kifejezés az $a változónak egy helyi - függvényen belüli - változatára utal, és ebben a hatáskörben ehhez nem rendeltek értéket. Ez valamelyest különbözik a C nyelv filozófiájától, ahol a globális változók automatikusan elérhetők bármely függvényből, feltéve ha a függvényben újra nem definiáltad azt a változót. Ez problémák forrása lehet, ha az ember véletlenül megváltoztat egy globális változót. A PHP-ben a globális változókat global kulcsszóval kell deklarálni a függvényekben.

A global kulcsszó

Először nézzünk egy példát a global használatára:

Example#1 A global használata

<?php 
$a 
1;
$b 2;

function 
Osszead()
{
    global 
$a$b;

    
$b $a $b;
}

Ossszead();
echo 
$b;
?>

A fenti szkript kiírja, hogy "3". $a és $b global-ként való deklarálásával minden utalás ezekre a változókra a globális változót fogja érinteni. Nincs megkötve, hány globális változót kezelhet egy függvény.

Globális változók elérésének másik módja a PHP által definiált speciális $GLOBALS tömb használata. Az előbbi példával egyenértékű megoldás:

Example#2 A $GLOBALS használata global helyett

<?php
$a 
1;
$b 2;

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

Osszead();
echo 
$b;
?>

A $GLOBALS asszociatív tömb, ahol a globális változó neve jelenti a kulcsot, és a változó értéke a tömbelem értéke. Vedd észre, hogy a $GLOBALS tömb minden hatáskörben létezik, mivel a $GLOBALS egy szuperglobális változó. A következő példa a szuperglobális változók erejét szemlélteti.

Example#3 Példa szuperglobális változók és a hatáskör szemléltetésére

<?php
function global_teszt()
{
    
// A legtöbb előredefiniált változó nem "szuper" és
    // megkövetelik a 'global' használatát, hogy elérhetőek
    // legyenek függvények helyi hatáskörében.
    
global $HTTP_POST_VARS;
    
    echo 
$HTTP_POST_VARS['nev'];
    
    
// A szuperglobális változók elérhetőek bármilyen 
    // hatáskörben, és nem szükséges a 'global' kulcsszó.
    // A szuperglobális változók a PHP 4.1.0-től használhatóak,
    // a HTTP_POST_VARS pedig már elavultnak számít.
    
echo $_POST['nev'];
}
?>

Statikus változók használata

A változók hatáskörének másik fontos lehetősége a static (statikus) változó. A statikus változó csak lokális hatáskörben él - egy függvényen belül, de két függvényhívás közt nem veszti el az értékét, a változó hatásköréből való kilépés esetén is megmarad az értéke:

Example#4 Statikus változók szükségességét szemléltető példa

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

Ez nagyon haszontalan függvény, mivel nem csinál mást, mint minden meghívásakor $a-t 0-ra állítja, aztán kiírja a 0-t. Az $a++ teljesen felesleges, mert amint vége a függvény futásának az $a változó megszűnik. Ahhoz, hogy ebből értelmes számlálófüggvény legyen - megmaradjon a számláló értéke -, az $a változót statikusnak kell deklarálni:

Example#5 Példa statikus változók használatára

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

Most már valahányszor meghívódik a Test() függvény, kiírja $a értékét, majd azt megnöveli eggyel.

A statikus változókat a rekurzív függvények megvalósításában is felhasználhatjuk. Rekurzívnak nevezzük azt a függvényt, amely saját magát hívja meg. Ezt természetesen feltételhez kell kötni, nehogy végtelen rekurzióba kerüljön a vezérlés és meghatározatlan időre a függvényen belül csapdába esik. Mindig meg kell bizonyosodni arról, hogy megfelelő feltétel rendelkezésre áll a rekurzió befejezéséhez. A következő függvény rekurzívan elszámol 10-ig a statikus $count változó segítségével: [A static kulcsszó nagyon fontos!]

Example#6 Statikus változók rekurzív függvényekkel

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

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

Note: Statikus változókat a fenti példákban szereplő módon lehet deklarálni. Ha olyan értéket próbálsz értékül adni neki, amely egy kifejezés eredménye, az parse error-t fog okozni.

Example#7 Statikus változók deklarálása

<?php
function ize(){
    static 
$int 0;          // jó 
    
static $int 1+2;        // rossz (mivel ez egy kifejezés)
    
static $int sqrt(121);  // rossz (szintén kifejezés)

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

Referenciák globális és statikus változókkal

A PHP 4-et működtető Zend Engine 1, a statikus és globális változó módosítókat referenciákkal implementálja. Például egy valódi globális változó beemelve egy függvény hatáskörébe a global utasítással tulajdonképpen létrehoz egy referenciát a globális változóhoz. Ez váratlan viselkedésmódhoz vezethet, amelyet a következő példa illusztrál:

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

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

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

A példában szereplő kód végrahajtása a következő kimenetet produkálja:

NULL
object(stdClass)(0) {
}
   

Hasonló viselkedésmód jellemzi a static utasítást. A referenciák nem statikusan vannak tárolva:

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

    echo 
'Statikus objektum: ';
    
var_dump($obj);
    if (!isset(
$obj)) {
        
// Hozzárendel egy referenciát a statikus változóhoz
        
$obj = &new stdclass;
    }
    
$obj->property++;
    return 
$obj;
}

function &
get_instance_noref() {
    static 
$obj;

    echo 
'Statikus objektum: ';
    
var_dump($obj);
    if (!isset(
$obj)) {
        
// Hozzárendeli az objektumot a statikus változóhoz
        
$obj = new stdclass;
    }
    
$obj->property++;
    return 
$obj;
}

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

A példa végrehajtása a következő kimenetet eredményezi:

Statikus objektum: NULL
Statikus objektum: NULL

Statikus objektum: NULL
Statikus objektum: object(stdClass)(1) {
  ["property"]=>
  int(1)
}
   

A példa azt szemlélteti, hogy amikor hozzárendelsz egy referenciát egy statikus változóhoz, az nem lesz megjegyezve amikor újra meghívod a &get_instance_ref() függvényt.