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.