¿Las variables PHP se pasan por valor o por referencia?

Respuestas:

312

Es por valor de acuerdo con la documentación de PHP .

Por defecto, los argumentos de la función se pasan por valor (de modo que si se cambia el valor del argumento dentro de la función, no se cambia fuera de la función). Para permitir que una función modifique sus argumentos, se deben pasar por referencia.

Para tener un argumento para una función siempre pasado por referencia, anteponga un signo de y comercial ( & ) al nombre del argumento en la definición de la función.

<?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
fuente
44
También tuve esta duda (novato), así que para ser claros, esto sería lo mismo que $str = add_some_extra($str);si no estuviera usando la referencia, ¿verdad? entonces, ¿cuál es el verdadero valor agregado de eso?
Obmerk Kronen
77
@Obmerk Si fue no utiliza el símbolo de unión para significar por referencia, no sería equivalente. Observe cómo la función no tiene una declaración de retorno, por $strlo que se asignaría nulo, en su caso.
The Unknown Dev
si lo hace de esta manera @ObmerkKronen, tendrá que devolver el valor y atraparlo también a través de su código. La variable inicial permanecerá igual de lo contrario.
Nabeel Khan
Me pregunto si pasar por referencia tiene algunos beneficios de rendimiento o no. Estoy pasando una gran matriz y por defecto es pasar por valor. Entonces, si uso la llamada por referencia, ¿habrá algún beneficio en cuanto al rendimiento? (manteniendo los valores de la matriz sin cambios durante todo el proceso)
Choxx
44
El beneficio real es que puede manipular / devolver valores múltiples. También puede pasar variables por referencia y aún así dejar que la función devuelva algo.
Martin Schneider
65

En PHP, por defecto, los objetos se pasan como copia de referencia a un nuevo objeto.

Ver este ejemplo .............

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

Ahora mira esto ..............

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.

Ahora mira esto ..............

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.

Espero que puedas entender esto.

hardik
fuente
1
Esto ilustra muy bien cómo funcionan los manejadores de objetos PHP.
Frederik Krautwald
2
1st ex, el valor de la referencia del obj se pasa a la función, los cambios en el obj utilizando la nueva referencia se reflejarán en todas las demás referencias que apuntan al mismo obj. 2do ej., Nuevamente el VALOR de la referencia del obj se pasa a la función, la función está cambiando el valor de la referencia para apuntar a un nuevo obj, el viejo obj permanece igual. 3er ex, la referencia del valor de la referencia del obj se pasa a la función, por lo tanto, los cambios en la función están operando en el mismo obj.
s-hunter
Ejemplos perfectos y exactamente lo que pensé que sucedería. Soy un recién llegado a PHP (desde Javascript / node.js background) y a veces es difícil entender lo que está sucediendo, ¡pero esto está muy claro!
TKoL
Este ejemplo es innecesariamente complicado. $yno es necesario, no hay nada function changeValueque requiera que esté adentro class Y, de modo que se enreden dos conceptos no relacionados. Me mudaría function changeValueal alcance exterior, justo arriba $x = new X();. Eliminar todas las referencias a $y. Ahora es más fácil ver que los dos primeros ejemplos dejan solo $ x, y el tercero cambia su contenido a new Y(). Lo que sería más fácil para ver si se vació el typede $x- el hecho de que tanto Xy Ypasan a incluir un $abccampo también es irrelevante para lo que se demuestra
ToolmakerSteve
60

Parece que mucha gente se confunde por la forma en que se pasan los objetos a las funciones y por lo que pasa por referencia. Las variables de objeto todavía se pasan por valor, es solo el valor que se pasa en PHP5 es un identificador de referencia. Como prueba:

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

Salidas:

a, b

Para el paso por referencia significa que podemos modificar las variables que son vistos por la persona que llama. Lo que claramente no hace el código anterior. Necesitamos cambiar la función de intercambio a:

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

Salidas:

b, a

para pasar por referencia.

grom
fuente
18
Creo que esta prueba es una mala interpretación. Parece que estabas tratando de intercambiar referencias. Tiene un problema con cómo se asignan los objetos. Está reasignando la referencia que es inmutable. Es el valor al que apunta que se puede cambiar desde adentro. La redefinición de intercambio ahora está pasando ** x, lo que le permite cambiar * x. El problema está en pensar que $ x = $ y cambiaría lo que $ x apunta originalmente.
grantwparks
99
Esto no es una prueba . El resultado del primer ejemplo es exactamente lo que cabría esperar si el valor actual de todo el objeto se pasara a la swapfunción; en otras palabras, si los objetos fueron "pasados ​​por valor". Por otro lado, también es exactamente lo que esperaría si se pasara un identificador de objeto a la función, que en realidad es lo que sucede. Su código no distingue entre estos dos casos.
EML
31

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

En PHP 5 hay un nuevo modelo de objetos. El manejo de objetos de PHP se ha reescrito por completo, lo que permite un mejor rendimiento y más funciones. En versiones anteriores de PHP, los objetos se manejaban como tipos primitivos (por ejemplo, enteros y cadenas). El inconveniente de este método fue que semánticamente todo el objeto se copió cuando se asignó una variable o se pasó como parámetro a un método. En el nuevo enfoque, se hace referencia a los objetos por identificador y no por valor (se puede pensar en un identificador como el identificador de un objeto).

Karl Seguin
fuente
25

Las variables PHP se asignan por valor, se pasan a funciones por valor, y cuando contienen / representan objetos se pasan por referencia. Puede forzar que las variables pasen por referencia usando un &

Asignado por valor / ejemplo de referencia:

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

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

saldría

var1: prueba, var2: prueba final, var3: prueba final

Aprobado por valor / referencia examen:

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

changeThem($var1, $var2);

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

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

daría salida:

var1: foo, var2 BAR

Variables de objeto pasadas por examen de referencia:

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

Daría salida:

FOO

(ese último ejemplo podría ser mejor probablemente ...)

cmcculloh
fuente
2
Los "objetos" no son valores en PHP5 y no se pueden "pasar". El valor de $fooes un puntero a un objeto . $foose pasa por valor a la función changeFoo(), ya changeFoo()que no declaró su parámetro con a &.
newacct
9

Puede pasar una variable a una función por referencia. Esta función podrá modificar la variable original.

Puede definir el pasaje por referencia en la definición de la función:

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

$result=5;
changeValue($result);

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

Puedes hacerlo de cualquier manera.

ponga un símbolo '&' al frente y la variable que está pasando se convierte en una y la misma que su origen. es decir: puede pasar por referencia, en lugar de hacer una copia de la misma.

entonces

    $fred = 5;
    $larry = & $fred;
    $larry = 8;
    echo $fred;//this will output 8, as larry and fred are now the same reference.
Bingy
fuente
2
Esto está en desuso y genera una advertencia
fijiaaron
5

Las variables que contienen tipos primitivos se pasan por valor en PHP5. Las variables que contienen objetos se pasan por referencia. Hay un artículo bastante interesante de Linux Journal de 2006 que menciona esta y otras diferencias OO entre 4 y 5.

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

Polsonby
fuente
Todas las variables se pasan por valor en PHP si el parámetro de la función no tiene &.
newacct
1

Los objetos se pasan por referencia en PHP 5 y por valor en PHP 4. ¡Las variables se pasan por valor por defecto!

Lea aquí: http://www.webeks.net/programming/php/ampersand-operator-used-for-assigning-reference.html

Miha
fuente
Los "objetos" no son valores en PHP5 y no se pueden "pasar". Todas las variables se pasan por valor si el parámetro de la función pasada a no tiene &.
newacct
@newacct no del todo? Los objetos se pasan algo por referencia . Creo que he observado que los objetos php pueden modificarse mediante funciones incluso cuando no se definen &delante de los parámetros en la definición; si se pasaran por valor, el objeto contenido en el ámbito que llamaba a la función como parámetro lo haría No ser afectado.
Félix Gagnon-Grenier
3
@ FélixGagnon-Grenier: Una vez más, los "objetos" no son valores y no se pueden "pasar". No puede tener una "variable" en PHP5 cuyo valor sea un "objeto", solo puede tener un valor que sea una referencia de objeto (es decir, un puntero a un objeto). Por supuesto, puede pasar o asignar un puntero a un objeto por valor y modificar el objeto al que señala llamando a un método que realiza dicha modificación. Eso no tiene nada que ver con pasar por referencia. De qué tipo es un valor y si un parámetro es paso por valor / paso por referencia son cosas independientes y ortogonales.
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>";

Los atributos siguen siendo modificables cuando no se pasan por referencia, así que tenga cuidado.

Salida:

SwapObjects: b, a SwapValues: a, b

Ricardo Saracino
fuente
1

Para cualquiera que se encuentre con esto en el futuro, quiero compartir esta joya de los documentos PHP, publicados por un usuario anónimo :

Parece que hay algo de confusión aquí. La distinción entre punteros y referencias no es particularmente útil. El comportamiento en algunos de los ejemplos "completos" ya publicados puede explicarse en términos unificadores más simples. El código de Hayley, por ejemplo, está haciendo EXACTAMENTE lo que debe esperar. (Usando> = 5.3)

Primer principio: un puntero almacena una dirección de memoria para acceder a un objeto. Cada vez que se asigna un objeto, se genera un puntero. (Todavía no he profundizado demasiado en el motor Zend, pero por lo que puedo ver, esto se aplica)

Segundo principio y fuente de la mayor confusión: el paso de una variable a una función se realiza de forma predeterminada como un paso de valor, es decir, está trabajando con una copia. "¡Pero los objetos se pasan por referencia!" Un error común tanto aquí como en el mundo de Java. Nunca dije una copia de lo que. El paso predeterminado se realiza por valor. Siempre. Lo que se copia y se pasa, sin embargo, es el puntero. Cuando utilice "->", por supuesto accederá a los mismos elementos internos que la variable original en la función de llamada. Simplemente usando "=" solo jugará con copias.

3er principio: "&" establece automática y permanentemente otro nombre / puntero de variable en la misma dirección de memoria que otra cosa hasta que los desacople. Es correcto usar el término "alias" aquí. Piense en ello como unir dos punteros en la cadera hasta que se separen por la fuerza con "unset ()". Esta funcionalidad existe tanto en el mismo ámbito como cuando se pasa un argumento a una función. A menudo, el argumento pasado se denomina "referencia", debido a ciertas distinciones entre "pasar por valor" y "pasar por referencia" que eran más claras en C y C ++.

Solo recuerde: los punteros a los objetos, no los objetos en sí, se pasan a las funciones. Estos punteros son COPIAS del original a menos que use "&" en su lista de parámetros para pasar los originales. Solo cuando profundice en el interior de un objeto, los originales cambiarán.

Y aquí está el ejemplo que brindan:

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

?>

Escribí una publicación de blog extensa y detallada sobre este tema para JavaScript , pero creo que se aplica igualmente bien a PHP, C ++ y cualquier otro lenguaje donde la gente parece estar confundida acerca de pasar por valor frente a pasar por referencia.

Claramente, PHP, como C ++, es un lenguaje que admite paso por referencia. Por defecto, los objetos se pasan por valor. Cuando se trabaja con variables que almacenan objetos, ayuda ver esas variables como punteros (porque eso es fundamentalmente lo que son, a nivel de ensamblaje). Si pasa un puntero por valor, aún puede "rastrear" el puntero y modificar las propiedades del objeto al que apunta. Lo que no puede hacer es que apunte a un objeto diferente. Solo si declara explícitamente que un parámetro se pasa por referencia podrá hacerlo.

AleksandrH
fuente
0

En realidad, ambos métodos son válidos, pero depende de sus requisitos. Los valores de referencia por referencia a menudo hacen que su secuencia de comandos sea lenta. Por lo tanto, es mejor pasar las variables por valor considerando el tiempo de ejecución. Además, el flujo de código es más consistente cuando pasa variables por valor.

atif
fuente
0

Use esto para funciones cuando desee simplemente alterar la variable original y devolverla nuevamente al mismo nombre de variable con su nuevo valor asignado.

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
fuente
-7

Depende de la versión, 4 es por valor, 5 es por referencia.

Karl Seguin
fuente
11
No creo que esto sea cierto.
Nick Heiner
@Karl Seguin, es en parte cierto. Antes de PHP5, los objetos se pasaban por valor de manera predeterminada, pero desde 5 se pasan por manejador, que en la práctica se puede tratar como una referencia (con algunas excepciones) porque podemos modificar las propiedades de los objetos a través de ellos php.net/manual/en/language. oop5.references.php .
Adam Faryna