Референции в 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;