Czy zmienne PHP są przekazywane przez wartość czy przez odniesienie?

Odpowiedzi:

310

Jest pod względem wartości, zgodnie z Dokumentacją PHP .

Domyślnie argumenty funkcji są przekazywane przez wartość (tak, że jeśli wartość argumentu w funkcji zostanie zmieniona, nie zostanie zmieniona poza funkcją). Aby umożliwić funkcji modyfikowanie jej argumentów, muszą zostać przekazane przez odwołanie.

Aby argument funkcji był zawsze przekazywany przez odwołanie, wstaw znak ampersand ( & ) do nazwy argumentu w definicji funkcji.

<?php
function add_some_extra(&$string)
{
    $string .= 'and something extra.';
}

$str = 'This is a string, ';
add_some_extra($str);
echo $str;    // outputs 'This is a string, and something extra.'
?>
Michael Stum
źródło
4
Miałem również tę wątpliwość (nowicjusz) - tak dla jasności, byłoby to tak samo, jak $str = add_some_extra($str);gdybym nie używał referencji, prawda? jaka jest zatem prawdziwa wartość dodana tego?
Obmerk Kronen
7
@Obmerk Jeśli nie używasz znaku handlowego i do oznaczenia przez odniesienie, nie będzie to równoważne. Zwróć uwagę, że funkcja nie ma instrukcji return, więc $strw twoim przypadku zostanie przypisana null.
The Unknown Dev
jeśli zrobisz to w ten sposób @ObmerkKronen, będziesz musiał zwrócić wartość i przechwycić ją również w kodzie. W przeciwnym razie zmienna początkowa pozostanie taka sama.
Nabeel Khan
Zastanawiam się, czy przekazanie przez odniesienie ma jakieś korzyści w zakresie wydajności, czy nie? Mijam dużą tablicę i domyślnie jest ona przekazywana według wartości. Więc jeśli użyję połączenia przez odniesienie, to czy będzie jakaś korzyść pod względem wydajności? (utrzymywanie wartości tablic bez zmian podczas całego procesu)
Choxx
4
prawdziwą korzyścią jest to, że można manipulować / zwracać wiele wartości. Możesz również przekazywać zmienne przez referencję i nadal pozwalać, aby funkcja coś zwróciła.
Martin Schneider,
65

W PHP obiekty są domyślnie przekazywane jako kopia referencyjna do nowego obiektu.

Zobacz ten przykład .............

class X {
  var $abc = 10; 
}

class Y {

  var $abc = 20; 
  function changeValue($obj)
  {
   $obj->abc = 30;
  }
}

$x = new X();
$y = new Y();

echo $x->abc; //outputs 10
$y->changeValue($x);
echo $x->abc; //outputs 30

Teraz zobacz to ..............

class X {
  var $abc = 10; 
}

class Y {

  var $abc = 20; 
  function changeValue($obj)
  {
    $obj = new Y();
  }
}

$x = new X();
$y = new Y();

echo $x->abc; //outputs 10
$y->changeValue($x);
echo $x->abc; //outputs 10 not 20 same as java does.

Teraz zobacz to ..............

class X {
  var $abc = 10; 
}

class Y {

  var $abc = 20; 
  function changeValue(&$obj)
  {
    $obj = new Y();
  }
}

$x = new X();
$y = new Y();

echo $x->abc; //outputs 10
$y->changeValue($x);
echo $x->abc; //outputs 20 not possible in java.

mam nadzieję, że to zrozumiesz.

hardik
źródło
1
To bardzo dobrze ilustruje działanie uchwytów obiektów PHP.
Frederik Krautwald
2
1st ex, wartość odniesienia obj jest przekazywana do funkcji, zmiany w obj przy użyciu nowego odwołania odzwierciedlają wszystkie inne odniesienia wskazujące na ten sam obj. Drugi przykład, ponownie WARTOŚĆ referencji obj jest przekazywana do funkcji, funkcja zmienia wartość referencji na punkt na nowy obj, stary obj pozostaje taki sam. Trzeci przykład, odwołanie wartości referencji obj jest przekazywane do funkcji, a zatem zmiany w funkcji działają na tym samym obj.
s-hunter
Idealne przykłady i dokładnie to, co myślałem, że się wydarzy. Jestem nowicjuszem w PHP (z javascript / node.js) i czasem ciężko mi się zorientować, co się dzieje, ale to bardzo jasne!
TKoL
Ten przykład jest niepotrzebnie skomplikowany. $ynie jest potrzebne - nie ma w function changeValuetym nic, co wymagałoby, aby było w środku class Y, więc splata się dwie niezwiązane ze sobą koncepcje. Chciałbym przejść function changeValuedo zakresu zewnętrznego, tuż powyżej $x = new X();. Usuń wszystkie odniesienia do $y. Teraz łatwiej zauważyć, że pierwsze dwa przykłady pozostawiają $ x w spokoju, a trzeci zmienia jego zawartość na new Y(). Co byłoby łatwiej sprawdzić, czy wyrzucił typez $x- fakt, że zarówno Xi Ystało się zawierać $abcpole ma również znaczenia, co zostanie wykazane
ToolmakerSteve
60

Wygląda na to, że wielu ludzi jest zdezorientowanych sposobem, w jaki obiekty są przekazywane do funkcji i co przekazuje się przez odniesienie. Zmienne obiektowe są nadal przekazywane przez wartość, tylko wartość przekazywana w PHP5 jest uchwytem referencyjnym. Jako dowód:

<?php
class Holder {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }
}

function swap($x, $y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

Wyjścia:

a, b

Aby przekazać przez referencję , możemy zmodyfikować zmienne widoczne dla dzwoniącego. Które wyraźnie powyższy kod nie działa. Musimy zmienić funkcję wymiany na:

<?php
function swap(&$x, &$y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

Wyjścia:

b, a

w celu przekazania przez odniesienie.

grom
źródło
18
Myślę, że ten dowód jest błędną interpretacją. Wygląda na to, że próbowałeś zamienić odniesienia. Masz problem z przypisywaniem obiektów. Ponownie przypisujesz odwołanie, które jest niezmienne. Jest to wartość, którą wskazuje, którą można zmienić od wewnątrz. Nowa definicja zamiany przechodzi teraz ** x, co pozwala zmienić * x. Problem polega na tym, że $ x = $ y zmieniłoby to, na co pierwotnie wskazuje $ x.
grantwparks
9
To nie jest dowód . Wynik pierwszego przykładu jest dokładnie taki, jak można oczekiwać, jeśli bieżąca wartość całego obiektu zostałaby przekazana do swapfunkcji; innymi słowy, jeśli obiekty zostały „przekazane przez wartość”. Z drugiej strony jest to dokładnie to, czego można oczekiwać, jeśli uchwyt obiektu zostałby przekazany do funkcji, co faktycznie się dzieje. Twój kod nie rozróżnia tych dwóch przypadków.
EML
31

http://www.php.net/manual/en/migration5.oop.php

W PHP 5 istnieje nowy model obiektowy. Obsługa obiektów przez PHP została całkowicie przepisana, co pozwala na lepszą wydajność i więcej funkcji. W poprzednich wersjach PHP obiekty były traktowane jak typy pierwotne (na przykład liczby całkowite i łańcuchy). Wadą tej metody było to, że semantycznie cały obiekt został skopiowany, gdy zmienna została przypisana lub przekazana jako parametr do metody. W nowym podejściu do obiektów odwołuje się uchwyt, a nie wartość (uchwyt można traktować jako identyfikator obiektu).

Karl Seguin
źródło
25

Zmienne PHP są przypisywane przez wartość, przekazywane do funkcji przez wartość, a gdy zawierają / reprezentują obiekty są przekazywane przez odwołanie. Możesz wymusić przekazywanie zmiennych przez referencję za pomocą &

Przypisany przez przykład wartości / odniesienia:

$var1 = "test";
$var2 = $var1;
$var2 = "new test";
$var3 = &$var2;
$var3 = "final test";

print ("var1: $var1, var2: $var2, var3: $var3);

wyszedłby

var1: test, var2: test końcowy, var3: test końcowy

Przekazywane przez przykład wartości / odniesienia:

$var1 = "foo";
$var2 = "bar";

changeThem($var1, $var2);

print "var1: $var1, var2: $var2";

function changeThem($var1, &$var2){
    $var1 = "FOO";
    $var2 = "BAR";
}

wyprowadziłby:

var1: foo, var2 BAR

Zmienne obiektowe przekazywane przez przykład odniesienia:

class Foo{
    public $var1;

    function __construct(){
        $this->var1 = "foo";
    }

    public function printFoo(){
        print $this->var1;
    }
}


$foo = new Foo();

changeFoo($foo);

$foo->printFoo();

function changeFoo($foo){
    $foo->var1 = "FOO";
}

Wyprowadziłby:

BLA

(ten ostatni przykład może być lepszy prawdopodobnie ...)

cmcculloh
źródło
2
„Obiekty” nie są wartościami w PHP5 i nie można ich „przekazać”. Wartość $foojest wskaźnikiem do obiektu . $foojest przekazywany przez wartość do funkcji changeFoo(), ponieważ changeFoo()nie zadeklarował swojego parametru za pomocą a &.
newacct
9

Możesz przekazać zmienną do funkcji przez odwołanie. Ta funkcja będzie mogła modyfikować oryginalną zmienną.

Możesz zdefiniować przejście przez odniesienie w definicji funkcji:

<?php
function changeValue(&$var)
{
    $var++;
}

$result=5;
changeValue($result);

echo $result; // $result is 6 here
?>
Mahsin
źródło
6

Możesz to zrobić w obie strony.

umieść symbol „&” z przodu, a przekazywana zmienna stanie się jednym i tym samym co jej pochodzenie. tzn .: możesz przekazać referencję zamiast robić jej kopię.

więc

    $fred = 5;
    $larry = & $fred;
    $larry = 8;
    echo $fred;//this will output 8, as larry and fred are now the same reference.
Bingy
źródło
2
Jest to przestarzałe i generuje ostrzeżenie
fijiaaron
5

Zmienne zawierające pierwotne typy są przekazywane przez wartość w PHP5. Zmienne zawierające obiekty są przekazywane przez odniesienie. Jest dość interesujący artykuł z Linux Journal z 2006 roku, który wspomina o tej i innych różnicach OO między 4 a 5.

http://www.linuxjournal.com/article/9170

Polsonby
źródło
Wszystkie zmienne są przekazywane przez wartość w PHP, jeśli parametr funkcji nie ma &.
newacct
1

Obiekty są przekazywane przez odniesienie w PHP 5 i przez wartość w PHP 4. Zmienne są domyślnie przekazywane przez wartość!

Przeczytaj tutaj: http://www.webeks.net/programming/php/ampersand-operator-used-for-assigning-reference.html

Miha
źródło
„Obiekty” nie są wartościami w PHP5 i nie można ich „przekazać”. Wszystkie zmienne są przekazywane przez wartość, jeśli parametr funkcji przekazanej nie ma &.
newacct
@newacct nie całkiem? Obiekty są nieco przekazywane przez odniesienie . Wydaje mi się, że zauważyłem, że obiekty php mogą być modyfikowane przez funkcje, nawet jeśli nie zostały zdefiniowane &przed parametrami w definicji - gdyby zostały przekazane przez wartość, obiekt zawarty w zakresie, który wywołał funkcję z tym parametrem nie ma to wpływu.
Félix Gagnon-Grenier
3
@ FélixGagnon-Grenier: Znów „obiekty” nie są wartościami i nie można ich „przekazać”. Nie możesz mieć „zmiennej” w PHP5, której wartością jest „obiekt”, możesz mieć tylko wartość, która jest odwołaniem do obiektu (tj. Wskaźnikiem do obiektu). Oczywiście możesz przekazać lub przypisać wskaźnik do obiektu według wartości i zmodyfikować obiekt, na który wskazuje, wywołując metodę, która dokonuje takiej modyfikacji. To nie ma nic wspólnego z przekazywaniem referencji. Jakim typem jest wartość i czy parametr jest przekazywany przez wartość / przekazywany przez odniesienie, są niezależnymi i ortogonalnymi rzeczami.
newacct
1
class Holder
{
    private $value;

    public function __construct( $value )
    {
        $this->value = $value;
    }

    public function getValue()
    {
        return $this->value;
    }

    public function setValue( $value )
    {
        return $this->value = $value;
    }
}

class Swap
{       
    public function SwapObjects( Holder $x, Holder $y )
    {
        $tmp = $x;

        $x = $y;

        $y = $tmp;
    }

    public function SwapValues( Holder $x, Holder $y )
    {
        $tmp = $x->getValue();

        $x->setValue($y->getValue());

        $y->setValue($tmp);
    }
}


$a1 = new Holder('a');

$b1 = new Holder('b');



$a2 = new Holder('a');

$b2 = new Holder('b');


Swap::SwapValues($a1, $b1);

Swap::SwapObjects($a2, $b2);



echo 'SwapValues: ' . $a2->getValue() . ", " . $b2->getValue() . "<br>";

echo 'SwapObjects: ' . $a1->getValue() . ", " . $b1->getValue() . "<br>";

Atrybuty można modyfikować, jeśli nie zostaną przekazane przez odniesienie, więc uważaj.

Wynik:

SwapObjects: b, a SwapValues: a, b

Ricardo Saracino
źródło
1

Dla każdego, kto spotka się z tym w przyszłości, chcę udostępnić ten klejnot z dokumentów PHP, opublikowanych przez anonimowego użytkownika :

Wydaje się, że jest tu trochę zamieszania. Rozróżnienie wskaźników i odniesień nie jest szczególnie pomocne. Zachowanie w niektórych opublikowanych już „kompleksowych” przykładach można wyjaśnić w prostszy sposób ujednolicający. Na przykład kod Hayley robi DOKŁADNIE to, czego należy się spodziewać. (Używanie> = 5,3)

Pierwsza zasada: Wskaźnik przechowuje adres pamięci w celu uzyskania dostępu do obiektu. Za każdym razem, gdy obiekt jest przypisany, generowany jest wskaźnik. (Nie zagłębiłem się zbyt głęboko w silnik Zend, ale o ile widzę, dotyczy to)

Druga zasada i źródło największego zamieszania: Przekazywanie zmiennej do funkcji jest domyślnie wykonywane jako przekazywanie wartości, tzn. Pracujesz z kopią. „Ale obiekty są przekazywane przez odniesienie!” Powszechne nieporozumienie zarówno tutaj, jak i w świecie Java. Nigdy nie powiedziałem kopii CO. Domyślne przekazywanie odbywa się według wartości. Zawsze. CO jest jednak kopiowane i przekazywane jest wskaźnikiem. Korzystając z „->”, oczywiście uzyskujesz dostęp do tych samych elementów wewnętrznych, co oryginalna zmienna w funkcji wywołującej. Wystarczy użyć „=”, aby odtwarzać tylko kopie.

Trzecia zasada: „&” automatycznie i na stałe ustawia inną nazwę / wskaźnik zmiennej na ten sam adres pamięci, co coś innego, dopóki ich nie oddzielisz. Prawidłowe jest użycie tutaj terminu „alias”. Pomyśl o tym jako o połączeniu dwóch wskaźników na biodrze, aż do wymuszonego oddzielenia przez „unset ()”. Ta funkcjonalność istnieje zarówno w tym samym zakresie, jak i po przekazaniu argumentu do funkcji. Często przekazywany argument nazywany jest „referencją” z powodu pewnych rozróżnień między „przekazywaniem przez wartość” i „przekazywaniem przez referencję”, które były wyraźniejsze w C i C ++.

Pamiętaj tylko: wskaźniki do obiektów, a nie same obiekty, są przekazywane do funkcji. Wskaźniki te są KOPIAMI oryginału, chyba że użyjesz „&” na liście parametrów, aby faktycznie przekazać oryginały. Dopiero po wbiciu się w wnętrze obiektu oryginały się zmienią.

Oto przykład, który podają:

<?php

//The two are meant to be the same
$a = "Clark Kent"; //a==Clark Kent
$b = &$a; //The two will now share the same fate.

$b="Superman"; // $a=="Superman" too.
echo $a;
echo $a="Clark Kent"; // $b=="Clark Kent" too.
unset($b); // $b divorced from $a
$b="Bizarro";
echo $a; // $a=="Clark Kent" still, since $b is a free agent pointer now.

//The two are NOT meant to be the same.
$c="King";
$d="Pretender to the Throne";
echo $c."\n"; // $c=="King"
echo $d."\n"; // $d=="Pretender to the Throne"
swapByValue($c, $d);
echo $c."\n"; // $c=="King"
echo $d."\n"; // $d=="Pretender to the Throne"
swapByRef($c, $d);
echo $c."\n"; // $c=="Pretender to the Throne"
echo $d."\n"; // $d=="King"

function swapByValue($x, $y){
$temp=$x;
$x=$y;
$y=$temp;
//All this beautiful work will disappear
//because it was done on COPIES of pointers.
//The originals pointers still point as they did.
}

function swapByRef(&$x, &$y){
$temp=$x;
$x=$y;
$y=$temp;
//Note the parameter list: now we switched 'em REAL good.
}

?>

Napisałem obszerny, szczegółowy post na blogu na ten temat dla JavaScript , ale uważam, że ma on zastosowanie równie dobrze do PHP, C ++ i każdego innego języka, w którym ludzie wydają się być zdezorientowani w kwestii przekazywania według wartości i przekazywania przez odniesienie.

Oczywiście PHP, podobnie jak C ++, jest językiem, który obsługuje przekazywanie przez referencję. Domyślnie obiekty są przekazywane według wartości. Podczas pracy ze zmiennymi, które przechowują obiekty, pomaga postrzegać te zmienne jako wskaźniki (ponieważ jest to zasadniczo to, co one są, na poziomie zespołu). Jeśli przekażesz wskaźnik według wartości, nadal możesz „śledzić” wskaźnik i modyfikować właściwości wskazywanego obiektu. To, czego nie możesz zrobić, to wskazanie innego obiektu. Tylko jeśli wyraźnie zadeklarujesz parametr jako przekazany przez referencję, będziesz w stanie to zrobić.

AleksandrH
źródło
0

Właściwie obie metody są poprawne, ale zależy to od twoich wymagań. Wartości referencyjne często powodują spowolnienie skryptu. Dlatego lepiej jest przekazywać zmienne według wartości, biorąc pod uwagę czas wykonania. Również przepływ kodu jest bardziej spójny, gdy zmienne są przekazywane według wartości.

atif
źródło
0

Użyj tego dla funkcji, gdy chcesz po prostu zmienić oryginalną zmienną i przywrócić ją ponownie do tej samej nazwy zmiennej z przypisaną nową wartością.

function add(&$var){ // The &amp; is before the argument $var
   $var++;
}
$a = 1;
$b = 10;
add($a);
echo "a is $a,";
add($b);
echo " a is $a, and b is $b"; // Note: $a and $b are NOT referenced
PPL
źródło
-7

Zależy od wersji, 4 to wartość, 5 to wartość referencyjna.

Karl Seguin
źródło
11
Nie sądzę, że to prawda.
Nick Heiner
@Karl Seguin, to częściowo prawda. Przed PHP5 obiekt był domyślnie przekazywany przez wartość, ale od 5 są one przekazywane przez moduł obsługi, który w praktyce może być traktowany jako odniesienie (z pewnymi wyjątkami), ponieważ możemy modyfikować właściwości obiektu za ich pośrednictwem php.net/manual/en/language. oop5.references.php .
Adam Faryna