Референции в PHP.
Публикувана от ko6rata на January 19 2011 10:38:24

Разширена новина
Много спорове за референциите в PHP започват с „референциите са объркващи”. Всъщност , референциите са много елементарна концепция за схващане.

Референциите в PHP са средство за достигане до съдържанието на променлива чрез различни имена. Тези имена ще са псевдоними на базовата променлива и ще сочат към нея. Освен това те могат да бъдат използвани за по-кратко записване на имена на променливи. Така те ще бъдат достъпни по-удобно и по-практично до съдържанието на техните базови променливи, ако имената на базовите променливи е дълго и сложно.

Тези разработчици , които са запознати с езици за програмиране, като C++ или Java ще забележат, че референциите в PHP не са аналогични с указателите.

Всички имена на променливи в PHP са автоматично свързани със стойности в паметта. Използвайки референции, ние може да свържем две имена на променлива към същата стойност в паметта. Трябва да знаем , че име на променлива и съдържание на променлива са две различни неща.

За да разберем как работят референциите трябва да знаем първо как PHP работи с променливите, които не са достъпни като референции, а по стойност.


Взимане на стойност от друга променлива


Пример 1 :

<?php


$a = 1;

$b = $a;

$b++;

еcho $a; // ще изведе 1

еcho $b; // ще изведе 2

?>


Предаване на стойност като аргумент на функция


Пример 2 :

<?php

function addFive( $num ) {

$num += 5;

}


$original_num = 10;


addFive( $original_num);


print( $original_num); // Ще изведе 10

?>


Предаване на стойност като аргумент на метод от обект


Пример 3 :

<?php

class DefensiveAttack {


/**

* My IP address

*

* @access protected

* @var string

*/

var $mMyIpAddress = '127.0.0.1';


/**

* Set my IP address

*

* @access public

* @param string $ip

*/

function SetMyIpAddress($ip) {


$this->mMyIpAddress = $ip;

}


/**

* Clear my ip

*

* @access public

*/

function ClearMyIP() {


$this->mMyIpAddress = '';

}

}


$my_ip = '212.116.142.*';


$object = new DefensiveAttack();


$object->SetMyIpAddress($my_ip);


echo $object->mMyIpAddress;// Ще изведе 212.116.142.*


echo $my_ip;// Ще изведе 212.116.142.*


$object->ClearMyIP(); //Изтриваме съдържанието на $mMyIpAddress


echo $object->mMyIpAddress;// Няма да изведе нищо


echo $my_ip;// Ще изведе 212.116.142.*


?>


Сега започнем да разглеждаме предаването на стойности като референция.


Достъп до стойност на друга променлива като референция към нея.


Пример 1:


<?php


$a = 1;

$b = &$a;

$b++;

еcho $a; // ще изведе 2. При увеличаването на стойността на $b променяме стойността на $a

еcho $b; // ще изведе 2

?>


Пример 2:

<?php

$A=array('a' => 'aaa', 'b' => 'bbb');


$b=&$A['b']; // $b сочи към елемент “b” от масив $A


$b=0; // $A['b']=0;


echo $A['b']; // изкарва на екрана 0

?>


За да се създаде референция , не е нужно да съществува стойност към която да сочи.


<?php


$A=array('a' => 'aaa', 'b' => 'bbb');

$b=&$A['c']; // $b сочи към 'c' индекса на масива $A


// Сега масива $A съдържа ‘c’ индекс, но той е празен.

echo " Съдържанието на 'c' индекса от масива $A : (".$A['c'].")";


?>


Когато дадена променлива съдържа име на друга променлива, това се нарича символна референция.


Пример 3:

<?php

$a=10;


$b=20;


$c=30;


$p="a"; // съдържа името на друга променлива (в случая на ‘a’)


echo $$p; // това е референция към $a и ще изведе 10


$$p=100; // тука задаваме стойност не на $p, а на $a. Задаваме 100.


echo $a; // ще изведе 100 вместо 10

?>



Предаване на аргумент като референция към функция


Пример1 :


<?php

function addFive( &$num ) {

// Увеличаваме $original_num с 5

$num += 5;

}


$original_num = 10;


// Увеличаваме $original_num с 5

addFive( $original_num );


// $original_num е вече 15

print( $original_num );

?>


Връщане на референция от функция


<?php

//Връща референция

function &addFive( &$num ) {

$num+=5;


return $num;

}


$original_num = 10;


//референция , която сочи към върнатата стойност от addFive

$returned_num = & addFive( $original_num );


//Променяме $original_num и в същото време променяме върнатата стойност от addFive.

//В този случай $returned_num

$original_num += 10;


print( $returned_num ); // показва на екрана 25


?>

Предаване на референция като аргумент на метод от обект


Тука това е почти същото, както при предаването на референция като аргумент или връщане на референция при функциите.


Пример :


<?php

class DefensiveAttack {


/**

* My IP address

*

* @access protected

* @var string

*/

var $mMyIpAddress = '127.0.0.1';


/**

* Set my IP address by reference

*

* @access public

* @param string $ip

*/

function SetMyIpAddress(&$rIp) {


$this->mMyIpAddress = &$rIp;

}


/**

* Clear my ip

*

* @access public

*/

function ClearMyIP() {


$this->mMyIpAddress = '';

}


/**

* Get My IP

*

* @access public

*/

function &GetIp() {


return $this->mMyIpAddress;

}

}


$my_ip = '212.116.142.*';


$object = new DefensiveAttack();


//Предаваме променлива като референция

$object->SetMyIpAddress($my_ip);


//Връща референция към mMyIpAddress, която сочи към $my_ip

$i_will_get_ip =& $object->GetIp();


echo ('Съдържанието на $i_will_get_ip:') . $i_will_get_ip . '<br />';// Ще изведе 212.116.142.*


echo ('Съдържанието на $object->mMyIpAddress:') . $object->mMyIpAddress . '<br />';// Ще изведе 212.116.142.*


echo ('Съдържанието на $my_ip:') . $my_ip . '<br />';// Ще изведе 212.116.142.*


$object->ClearMyIP(); //Изтриваме съдържанието на $mMyIpAddress


echo ('Съдържанието на $object->mMyIpAddress след $object->ClearMyIP():') . $object->mMyIpAddress . '<br />';// Няма да изведе нищо


echo ('Съдържанието на $my_ip след $object->ClearMyIP():') . $my_ip . '<br />';// Няма да изведе нищо


echo ('Съдържанието на $i_will_get_ip след $object->ClearMyIP():') . $i_will_get_ip . '<br />';// // Няма да изведе нищо


?>


Особености при обектите в PHP5


В PHP5 обектите се предават като референция. За да направите две различни инстанции на даден обект е нужно да го клонирате . В противен случай , при промяна на инстанцията от обекта вие ще променяте базовия обект. Това става много лесно.


Пример:

$object = new DefensiveAttack();


$object->mMyIpAddress = '212.116.142.*';


echo ('Съдържанието на $object->mMyIpAddress:') . $object->mMyIpAddress . '<br />';// Ще изведе 212.116.142.*


//Предава се чрез референция

$object1 = $object;


//Променяме $object->mMyIpAddress

$object1->mMyIpAddress = '127.0.0.1';


echo ('Съдържанието на $object->mMyIpAddress:') . $object->mMyIpAddress . '<br />';// Ще изведе 127.0.0.1


echo ('Съдържанието на $object1->mMyIpAddress:') . $object1->mMyIpAddress . '<br />';// Ще изведе 127.0.0.1


Клонирането става със запазената дума на PHP “clone”


Пример :


$object = new DefensiveAttack();


$object->mMyIpAddress = '212.116.142.*';


echo ('Съдържанието на $object->mMyIpAddress:') . $object->mMyIpAddress . '<br />';// Ще изведе 212.116.142.*


//Клонираме обекта

$object1 = clone $object;


//Не променяме $object->mMyIpAddress, а само $object1->mMyIpAddress

$object1->mMyIpAddress = '127.0.0.1';


echo ('Съдържанието на $object->mMyIpAddress:') . $object->mMyIpAddress . '<br />';// Ще изведе 127.0.0.1


echo ('Съдържанието на $object1->mMyIpAddress:') . $object1->mMyIpAddress . '<br />';// Ще изведе 212.116.142.*


Референцията като удобство


Споменах , че също така референциите може да се използват и за удобство, като заменим именуваните вече с дълги имена на променливи с техни по-къси псевдоними.


Пример :


<?php

// Създаваме масив от стойности

$arrNamesThatAreFairlyCommon = Array("Joe", "Bob", "Sarah", "Bill", "Suzy");


// Показваме съдържанието на масива

print_r($arrNamesThatAreFairlyCommon);


// Референция към $arrNamesThatAreFairlyCommon

$arrNames =& $arrNamesThatAreFairlyCommon;


// Показваме съдържанието на масива достъпен от $arrNames

print_r($arrNames);

?>


Премахване на променлива


Какво става когато се премахне променливата съдържаща референция, която сочи към някакви данни?

Ако унищожим такава променлива ще прекъснем единствено връзката към данните, а те си остават.


Пример :


<?php

$a = 1;

$b =& $a;

unset($a);


print $b;

?>


Добри и лоши практики


Понякога , когато използваме референции, кода може да стане много объркан, труден за четене и разбиране.


Пример 1 - Лоша практика:


<?php

function msg($message) {

echo $message;

}


$my_message = 'Hello World!';


// Задаването на аргумент като референция по този начин е лоша практика и може да ни обърка

msg(&$my_message);


?>


Предвидена е една директива в конфигурационния файл на PHP „allow_call_time_pass_reference = Off” за да ни пази от такъв вид програмиране. По подразбиране стойността и е Off .


Пример 2 - Добра практика:


<?php

// Приемането на референция от функцията е добра практика

function msg(&$message)

{

echo $message;

}


$my_message = 'Hello World!';


msg($my_message);



?>


Кога да използваме референции и кога не?


В зависимост от приложението , вие трябва внимателно да прецените кога и къде да използвате референции. Примерно , когато желаете да спестите ненужно изразходване на памет и увеличите бързодействието на приложението си. Това става тогава, когато вместо да копирате една променлива(масиви,...) в друга, вие само задавате референция , която сочи към базовата променлива. Има смисъл да се използват за големи по обем данни(масиви, обекти,..) , но не е нужно да се употребяват референциите за нищожни данни. Примерно:

$a = ‘Няма бира!’;

$b = $a; // Няма нужда от $b =& $a;