Các biến PHP được truyền theo giá trị hoặc bằng tham chiếu?


Câu trả lời:


311

Đó là theo giá trị theo Tài liệu PHP .

Theo mặc định, các đối số hàm được truyền theo giá trị (để nếu giá trị của đối số trong hàm bị thay đổi, nó không bị thay đổi bên ngoài hàm). Để cho phép một hàm sửa đổi các đối số của nó, chúng phải được truyền bằng tham chiếu.

Để có một đối số cho một hàm luôn được truyền bằng tham chiếu, hãy thêm một dấu và ( & ) vào tên đối số trong định nghĩa hàm.

<?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.'
?>

4
Tôi cũng có nghi ngờ này (người mới) - vì vậy chỉ cần rõ ràng, điều này sẽ giống như $str = add_some_extra($str);khi tôi không sử dụng tài liệu tham khảo, phải không? Vậy giá trị gia tăng thực sự của nó là gì?
Obmerk Kronen

7
@Obmerk Nếu bạn không sử dụng ký hiệu và để biểu thị bằng tham chiếu, nó sẽ không tương đương. Lưu ý cách hàm không có câu lệnh return, vì vậy $strsẽ được gán null, trong trường hợp của bạn.
The Dev Dev

nếu bạn làm theo cách này @ObmerkKronen thì bạn sẽ phải trả lại giá trị và bắt nó thông qua mã của bạn. Các biến ban đầu sẽ vẫn như cũ.
Nabeel Khan

Tôi đang tự hỏi nếu vượt qua tham chiếu có một số lợi ích hiệu suất hay không? Tôi đang vượt qua một mảng lớn và theo mặc định, nó được truyền theo giá trị. Vì vậy, nếu tôi sử dụng cuộc gọi bằng cách tham khảo sau đó, sẽ có một số lợi ích khôn ngoan hiệu suất ??? (giữ các giá trị mảng không thay đổi trong toàn bộ quá trình)
Choxx

4
lợi ích thực sự là bạn có thể thao tác / trả về các giá trị trung gian. Bạn cũng có thể truyền các biến bằng tham chiếu và vẫn để hàm trả về một cái gì đó.
Martin Schneider

65

Trong PHP, theo mặc định các đối tượng được chuyển dưới dạng bản sao tham chiếu đến một Đối tượng mới.

Xem ví dụ này .............

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

Bây giờ hãy xem điều này ..............

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.

Bây giờ hãy xem điều này ..............

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.

tôi hy vọng bạn có thể hiểu điều này


1
Điều này minh họa rất tốt cách xử lý đối tượng PHP hoạt động.
Frederik Krautwald

2
Đầu tiên, giá trị của tham chiếu của obj được truyền cho hàm, thay đổi thành obj bằng tham chiếu mới sẽ phản ánh tới tất cả các tham chiếu khác trỏ đến cùng obj. Lần thứ 2, một lần nữa GIÁ TRỊ của tham chiếu của obj được truyền cho hàm, hàm đang thay đổi giá trị của tham chiếu để trỏ đến một obj mới, obj cũ vẫn giữ nguyên. Ex thứ 3, tham chiếu giá trị của tham chiếu của obj được truyền vào hàm, do đó những thay đổi trong hàm đang hoạt động trên cùng obj.
s-hunter

Ví dụ hoàn hảo và chính xác những gì tôi nghĩ sẽ xảy ra. Tôi là người mới sử dụng PHP (từ nền Javascript / node.js) và đôi khi thật khó để tìm hiểu những gì đang diễn ra, nhưng điều này rất rõ ràng!
TKoL

Ví dụ này là phức tạp không cần thiết. $ykhông cần thiết - không có gì trong function changeValueđó yêu cầu nó phải ở bên trong class Y, do đó, kết hợp hai khái niệm không liên quan với nhau. Tôi sẽ chuyển function changeValueđến phạm vi bên ngoài, ngay phía trên $x = new X();. Xóa tất cả các tham chiếu đến $y. Bây giờ dễ dàng hơn để thấy rằng hai ví dụ đầu tiên để lại $ x một mình và ví dụ thứ ba thay đổi nội dung của nó thành new Y(). Mà sẽ dễ dàng hơn để xem nếu bạn đổ sự typecủa $x- thực tế là cả hai XYxảy ra để bao gồm một $abclĩnh vực cũng là không thích hợp với những gì được thể hiện
ToolmakerSteve

60

Có vẻ như rất nhiều người bị nhầm lẫn bởi cách các đối tượng được truyền đến các hàm và những gì được truyền bởi tham chiếu có nghĩa là gì. Các biến đối tượng vẫn được truyền theo giá trị, nó chỉ là giá trị được truyền trong PHP5 là một điều khiển tham chiếu. Như là bằng chứng:

<?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";

Đầu ra:

a, b

Để vượt qua tham chiếu có nghĩa là chúng ta có thể sửa đổi các biến mà người gọi nhìn thấy. Mà rõ ràng các mã trên không làm. Chúng ta cần thay đổi chức năng hoán đổi thành:

<?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";

Đầu ra:

b, a

để vượt qua bằng cách tham khảo.


18
Tôi nghĩ rằng bằng chứng này là một giải thích sai. Có vẻ như bạn đang cố gắng trao đổi tài liệu tham khảo. Bạn có một vấn đề với cách các đối tượng được chỉ định. Bạn đang chỉ định lại tài liệu tham khảo đó là bất biến. Đó là giá trị mà nó chỉ ra có thể thay đổi từ bên trong. Việc xác định lại trao đổi hiện đang vượt qua ** x, cho phép bạn thay đổi * x. Vấn đề là ở chỗ nghĩ rằng $ x = $ y sẽ thay đổi những gì $ x ban đầu chỉ ra.
Grantwparks

9
Đây không phải là một bằng chứng . Đầu ra của ví dụ đầu tiên chính xác là những gì bạn mong đợi nếu giá trị hiện tại của toàn bộ đối tượng được truyền cho swaphàm; nói cách khác, nếu các đối tượng được "truyền theo giá trị". Mặt khác, đó cũng chính xác là những gì bạn mong đợi nếu một đối tượng xử lý được chuyển đến chức năng, đó thực sự là những gì xảy ra. Mã của bạn không phân biệt giữa hai trường hợp này.
EML

31

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

Trong PHP 5 có một Mô hình đối tượng mới. Việc xử lý các đối tượng của PHP đã được viết lại hoàn toàn, cho phép hiệu năng tốt hơn và nhiều tính năng hơn. Trong các phiên bản trước của PHP, các đối tượng được xử lý như các kiểu nguyên thủy (ví dụ: số nguyên và chuỗi). Hạn chế của phương thức này là về mặt ngữ nghĩa, toàn bộ đối tượng được sao chép khi một biến được gán hoặc được truyền dưới dạng tham số cho phương thức. Trong cách tiếp cận mới, các đối tượng được tham chiếu bằng tay cầm chứ không phải theo giá trị (người ta có thể nghĩ về tay cầm như một định danh của đối tượng).


25

Các biến PHP được gán theo giá trị, được truyền cho các hàm theo giá trị và khi chứa / đại diện cho các đối tượng được truyền bằng tham chiếu. Bạn có thể buộc các biến chuyển qua tham chiếu bằng cách sử dụng &

Được gán bởi giá trị / ví dụ tham chiếu:

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

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

sẽ xuất

var1: test, var2: thử nghiệm cuối cùng, var3: thử nghiệm cuối cùng

Vượt qua bởi giá trị / tham chiếu exampe:

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

changeThem($var1, $var2);

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

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

sẽ xuất ra:

var1: foo, var2 BAR

Các biến đối tượng được truyền bởi exampe tham chiếu:

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";
}

Sẽ xuất:

Thức ăn

(ví dụ cuối cùng có thể tốt hơn có lẽ ...)


2
"Đối tượng" không phải là giá trị trong PHP5 và không thể được "thông qua". Giá trị của $foolà một con trỏ tới một đối tượng . $foođược truyền bởi giá trị cho hàm changeFoo(), vì changeFoo()không khai báo tham số của nó với a &.
newacct

9

Bạn có thể truyền một biến cho một hàm bằng cách tham chiếu. Hàm này sẽ có thể sửa đổi biến ban đầu.

Bạn có thể định nghĩa đoạn văn bằng tham chiếu trong định nghĩa hàm:

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

$result=5;
changeValue($result);

echo $result; // $result is 6 here
?>

6

Bạn có thể làm một trong hai cách.

đặt biểu tượng '&' ở phía trước và biến bạn đang truyền trở thành một và giống với nguồn gốc của nó. tức là: bạn có thể vượt qua bằng cách tham khảo, thay vì tạo một bản sao của nó.

vì thế

    $fred = 5;
    $larry = & $fred;
    $larry = 8;
    echo $fred;//this will output 8, as larry and fred are now the same reference.

2
Điều này không được chấp nhận và tạo ra một cảnh báo
fijiaaron

5

Các biến chứa các kiểu nguyên thủy được truyền theo giá trị trong PHP5. Các biến chứa các đối tượng được truyền bằng tham chiếu. Có một bài viết khá thú vị từ Tạp chí Linux từ năm 2006 trong đó đề cập đến sự khác biệt này và các khác biệt OO khác giữa 4 và 5.

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


Tất cả các biến được truyền theo giá trị trong PHP nếu tham số của hàm không có &.
newacct

1

Các đối tượng được truyền bằng tham chiếu trong PHP 5 và theo giá trị trong PHP 4. Các biến được truyền theo giá trị theo mặc định!

Đọc ở đây: http://www.webeks.net/programming/php/ampersand-operator-use-for-assigning-reference.html


"Đối tượng" không phải là giá trị trong PHP5 và không thể được "thông qua". Tất cả các biến được truyền theo giá trị nếu tham số của hàm được truyền vào không có &.
newacct

@newacct không khá? Đối tượng có phần được thông qua tham chiếu . Tôi nghĩ rằng tôi đã quan sát thấy các đối tượng php có thể được sửa đổi bởi các hàm ngay cả khi không được xác định &trước các tham số trong định nghĩa - nếu chúng được truyền bằng giá trị thì đối tượng chứa trong phạm vi gọi hàm đó là tham số sẽ không bị ảnh hưởng
Félix Gagnon-Grenier

3
@ FélixGagnon-Grenier: Một lần nữa, "đối tượng" không phải là giá trị và không thể được "thông qua". Bạn không thể có một "biến" trong PHP5 có giá trị là một "đối tượng", bạn chỉ có thể có một giá trị là một tham chiếu đối tượng (tức là một con trỏ tới một đối tượng). Tất nhiên, bạn có thể truyền hoặc gán một con trỏ cho một đối tượng theo giá trị và sửa đổi đối tượng mà nó trỏ đến bằng cách gọi một phương thức thực hiện sửa đổi đó. Điều đó không có gì để làm với thông qua tham khảo. Loại giá trị là gì và liệu một tham số là pass-by-value / pass-by-Reference là những thứ độc lập và trực giao.
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>";

Các thuộc tính vẫn có thể sửa đổi khi không được chuyển qua tham chiếu vì vậy hãy cẩn thận.

Đầu ra:

SwapObjects: b, a SwapValues: a, b


1

Đối với bất kỳ ai gặp phải điều này trong tương lai, tôi muốn chia sẻ viên ngọc này từ các tài liệu PHP, được đăng bởi một người dùng ẩn danh :

Dường như có một số nhầm lẫn ở đây. Sự khác biệt giữa con trỏ và tài liệu tham khảo không đặc biệt hữu ích. Hành vi trong một số ví dụ "toàn diện" đã được đăng có thể được giải thích bằng các thuật ngữ thống nhất đơn giản hơn. Mã của Hayley, chẳng hạn, đang thực hiện CHÍNH XÁC những gì bạn nên mong đợi. (Sử dụng> = 5.3)

Nguyên tắc đầu tiên: Một con trỏ lưu địa chỉ bộ nhớ để truy cập một đối tượng. Bất cứ khi nào một đối tượng được chỉ định, một con trỏ được tạo ra. (Tôi chưa đào sâu quá nhiều vào công cụ Zend, nhưng theo như tôi thấy, điều này được áp dụng)

Nguyên tắc thứ 2 và nguồn gốc của sự nhầm lẫn nhất: Truyền một biến cho một hàm được thực hiện theo mặc định là một giá trị vượt qua, tức là bạn đang làm việc với một bản sao. "Nhưng các đối tượng được thông qua tham chiếu!" Một quan niệm sai lầm phổ biến cả ở đây và trong thế giới Java. Tôi chưa bao giờ nói một bản sao. Việc truyền mặc định được thực hiện theo giá trị. Luôn luôn. Những gì đang được sao chép và thông qua, tuy nhiên, là con trỏ. Khi sử dụng "->", tất nhiên bạn sẽ truy cập vào cùng một phần bên trong như biến ban đầu trong hàm người gọi. Chỉ cần sử dụng "=" sẽ chỉ chơi với các bản sao.

Nguyên tắc thứ 3: "&" tự động và đặt vĩnh viễn một tên / con trỏ biến khác vào cùng một địa chỉ bộ nhớ như một thứ khác cho đến khi bạn tách chúng ra. Đó là chính xác để sử dụng thuật ngữ "bí danh" ở đây. Hãy nghĩ về nó như tham gia hai con trỏ ở hông cho đến khi buộc tách ra bằng "unset ()". Chức năng này tồn tại cả trong cùng một phạm vi và khi một đối số được truyền cho một chức năng. Thông thường, đối số được truyền được gọi là "tham chiếu", do sự khác biệt nhất định giữa "truyền theo giá trị" và "chuyển qua tham chiếu" rõ ràng hơn trong C và C ++.

Chỉ cần nhớ: con trỏ tới các đối tượng, không phải chính các đối tượng, được truyền cho các hàm. Những con trỏ này là bản sao của bản gốc trừ khi bạn sử dụng "&" trong danh sách tham số của mình để thực sự vượt qua bản gốc. Chỉ khi bạn đào sâu vào bên trong của một đối tượng thì bản gốc sẽ thay đổi.

Và đây là ví dụ họ cung cấp:

<?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.
}

?>

Tôi đã viết một bài đăng trên blog chi tiết, rộng rãi về chủ đề này cho JavaScript , nhưng tôi tin rằng nó cũng áp dụng tốt cho PHP, C ++ và bất kỳ ngôn ngữ nào khác mà mọi người dường như bị nhầm lẫn về việc vượt qua giá trị so với chuyển qua tham chiếu.

Rõ ràng, PHP, giống như C ++, là một ngôn ngữ hỗ trợ chuyển qua tham chiếu. Theo mặc định, các đối tượng được truyền theo giá trị. Khi làm việc với các biến lưu trữ các đối tượng, sẽ giúp xem các biến đó là con trỏ (bởi vì về cơ bản chúng là gì, ở cấp độ lắp ráp). Nếu bạn chuyển một con trỏ theo giá trị, bạn vẫn có thể "theo dõi" con trỏ và sửa đổi các thuộc tính của đối tượng được trỏ tới. Những gì bạn không thể làm là để nó trỏ đến một đối tượng khác. Chỉ khi bạn tuyên bố rõ ràng một tham số được truyền bằng tham chiếu thì bạn mới có thể làm điều đó.


0

Trên thực tế cả hai phương thức đều hợp lệ nhưng nó phụ thuộc vào yêu cầu của bạn. Các giá trị theo tham chiếu thường làm cho tập lệnh của bạn chậm. Vì vậy, tốt hơn để vượt qua các biến theo giá trị bằng cách xem xét thời gian thực hiện. Ngoài ra, dòng mã phù hợp hơn khi bạn chuyển các biến theo giá trị.


0

Sử dụng hàm này cho các hàm khi bạn muốn thay đổi biến ban đầu và trả lại cho cùng một tên biến với giá trị mới được gán.

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

-7

Phụ thuộc vào phiên bản, 4 là theo giá trị, 5 là theo tham chiếu.


11
Tôi không nghĩ điều này là đúng.
Nick Heiner

@Karl Seguin, nó đúng một phần. Trước khi đối tượng PHP5 theo mặc định được truyền theo giá trị nhưng vì 5 chúng được truyền bằng trình xử lý, trong thực tế có thể được coi là một tham chiếu (với một số ngoại lệ) vì chúng ta có thể sửa đổi các thuộc tính đối tượng thông qua chúng php.net/manual/en/lingu. oop5.references.php .
Adam Faryna
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.