web-dev-qa-db-de.com

Werden Arrays in PHP als Wert oder als Referenz übergeben?

Wenn ein Array als Argument an eine Methode oder Funktion übergeben wird, wird es als Referenz übergeben?

Was ist damit:

$a = array(1,2,3);
$b = $a;

Ist $b ein Verweis auf $a?

241
Frank

Den zweiten Teil Ihrer Frage finden Sie auf der Array-Seite des Handbuchs . Dort steht (zitiert) :

Die Zuweisung von Arrays beinhaltet immer das Kopieren von Werten. Verwenden Sie den Referenzoperator, um ein Array nach Referenz zu kopieren.

Und das gegebene Beispiel:

<?php
$arr1 = array(2, 3);
$arr2 = $arr1;
$arr2[] = 4; // $arr2 is changed,
             // $arr1 is still array(2, 3)

$arr3 = &$arr1;
$arr3[] = 4; // now $arr1 and $arr3 are the same
?>


Für den ersten Teil ist der beste Weg, sicher zu sein, es zu versuchen ;-)

Betrachten Sie dieses Codebeispiel:

function my_func($a) {
    $a[] = 30;
}

$arr = array(10, 20);
my_func($arr);
var_dump($arr);

Es wird diese Ausgabe geben:

array
  0 => int 10
  1 => int 20

Dies bedeutet, dass die Funktion das als Parameter übergebene "outside" -Array nicht geändert hat: Es wird als Kopie und nicht als Referenz übergeben.

Wenn Sie möchten, dass es als Referenz übergeben wird, müssen Sie die Funktion folgendermaßen ändern:

function my_func(& $a) {
    $a[] = 30;
}

Und die Ausgabe wird:

array
  0 => int 10
  1 => int 20
  2 => int 30

Dieses Mal wurde das Array als "Referenz" übergeben.


Zögern Sie nicht, den Abschnitt References Explained des Handbuchs zu lesen: Er sollte einige Ihrer Fragen beantworten ;-)

259
Pascal MARTIN

In Bezug auf Ihre erste Frage wird das Array als Referenz übergeben, es sei denn, es wurde innerhalb der von Ihnen aufgerufenen Methode/Funktion geändert. Wenn Sie versuchen, das Array innerhalb der Methode/Funktion zu ändern, wird zuerst eine Kopie davon erstellt, und dann wird nur die Kopie geändert. Dies sieht so aus, als ob das Array als Wert übergeben wird, obwohl dies nicht der Fall ist.

Beispiel: In diesem ersten Fall wird Ihre Funktion, die $ my_array nicht als Referenz akzeptiert (mithilfe des & -Zeichens in der Parameterdefinition), weiterhin als Referenz übergeben (dh Sie verschwenden keinen Speicherplatz) mit einer unnötigen Kopie).

function handle_array($my_array) {  

    // ... read from but do not modify $my_array
    print_r($my_array);

    // ... $my_array effectively passed by reference since no copy is made
}

Wenn Sie das Array jedoch ändern, wird zuerst eine Kopie davon erstellt (wodurch mehr Arbeitsspeicher belegt wird, Ihr ursprüngliches Array jedoch nicht betroffen ist).

function handle_array($my_array) {

    // ... modify $my_array
    $my_array[] = "New value";

    // ... $my_array effectively passed by value since requires local copy
}

Zu Ihrer Information - dies ist als "Lazy Copy" oder "Copy-on-Write" bekannt.

106
Kosta Kontos

TL; DR

a) Die Methode/Funktion liest nur das Array-Argument => implizite (interne) Referenz
b) die Methode/Funktion ändert das Array-Argument => Wert
c) Das Argument für das Methoden-/Funktionsarray wird explizit als Referenz (mit kaufmännischem Und) gekennzeichnet. => explizite Referenz (user-land)

Oder dieses:
- Array-Parameter ohne kaufmännisches Und : wird als Referenz übergeben; Die Schreiboperationen ändern eine neue Kopie des Arrays, eine Kopie, die beim ersten Schreiben erstellt wird.
- kaufmännisches Und-Zeichen-Array-Parameter : wird als Referenz übergeben; Die Schreibvorgänge ändern das ursprüngliche Array.

Denken Sie daran, dass - PHP macht eine Wertekopie in dem Moment, in dem Sie schreiben an den nicht-kaufmännischen Array-Parameter. Das ist es, was copy-on-write bedeutet. Ich würde Ihnen gerne die C-Quelle dieses Verhaltens zeigen, aber es ist beängstigend. Verwenden Sie besser xdebug_debug_zval () .

Pascal MARTIN hatte recht. Kosta Kontos war noch mehr.

Antworten

Es hängt davon ab, ob.

Lange Version

Ich denke, ich schreibe das für mich selbst auf. Ich sollte einen Blog haben oder so ...

Wenn Leute von Referenzen (oder Zeigern) sprechen, landen sie normalerweise in einer Logomachie (sieh dir das an thread !).
Da PHP eine ehrwürdige Sprache ist, dachte ich, ich sollte die Verwirrung vergrößern (auch wenn dies eine Zusammenfassung der obigen Antworten ist). Denn obwohl zwei Personen gleichzeitig Recht haben können, ist es besser, wenn Sie ihre Köpfe zu einer Antwort zusammenfassen.

Zunächst einmal sollten Sie wissen, dass Sie kein Pedant sind, wenn Sie nicht schwarz-weiß antworten. Die Dinge sind komplizierter als "Ja/Nein".

Wie Sie sehen werden, hängt das Ganze nach Wert/Referenz sehr stark davon ab, was genau Sie mit diesem Array in Ihrem Methoden-/Funktionsumfang tun: Lesen oder Ändern?

Was bedeutet PHP sagt? (Aka "change-wise")

Das Handbuch sagt dies (Hervorhebung von mir):

Standardmäßig werden Funktionsargumente als Wert übergeben (Wenn also der Wert des Arguments in der Funktion geändert wird , wird er nicht geändert außerhalb der Funktion). Damit eine Funktion ihre Argumente ändern kann , müssen sie als Referenz übergeben werden.

Wenn ein Argument für eine Funktion immer als Referenz übergeben werden soll, stellen Sie dem Argumentnamen in der Funktionsdefinition ein kaufmännisches Und (&) voran

Soweit ich sagen kann, sprechen große, seriöse und ehrliche Programmierer von Referenzen, normalerweise davon, den Wert dieser Referenz zu verändern. Und genau darüber spricht das Handbuch: hey, if you want to CHANGE the value in a function, consider that PHP's doing "pass-by-value".

Es gibt jedoch einen anderen Fall, den sie nicht erwähnen: Was ist, wenn ich nichts ändere - einfach lesen?
Was passiert, wenn Sie ein Array an eine Methode übergeben, die keinen Verweis explizit markiert, und wir dieses Array im Funktionsumfang nicht ändern? Z.B.:

<?php
function readAndDoStuffWithAnArray($array) 
{
    return $array[0] + $array[1] + $array[2];
}

$x = array(1, 2, 3);

echo readAndDoStuffWithAnArray($x);

Lesen Sie weiter, mein Mitreisender.

Was macht PHP eigentlich? (Aka "memory-wise")

Dieselben großen und seriösen Programmierer sprechen, wenn sie noch seriöser werden, von "Speicheroptimierungen" in Bezug auf Referenzen. PHP auch. Weil PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting, das ist warum .

Es wäre nicht ideal, HUGE-Arrays an verschiedene Funktionen zu übergeben und PHP um Kopien davon zu erstellen (das macht schließlich "pass-by-value"):

<?php

// filling an array with 10000 elements of int 1
// let's say it grabs 3 mb from your RAM
$x = array_fill(0, 10000, 1); 

// pass by value, right? RIGHT?
function readArray($arr) { // <-- a new symbol (variable) gets created here
    echo count($arr); // let's just read the array
}

readArray($x);

Nun, wenn dies tatsächlich ein vorübergehender Wert wäre, wären 3 MB + RAM verschwunden, weil es zwei Kopien dieses Arrays gibt, richtig ?

Falsch. Solange wir die Variable $arr nicht ändern, ist dies eine Referenz in Bezug auf den Speicher . Du siehst es einfach nicht. Deshalb PHP Erwähnungen user-land referenzen wenn es um &$someVar geht, zwischen internen und expliziten (mit kaufmännischem Und) zu unterscheiden.

Fakten

Also, when an array is passed as an argument to a method or function is it passed by reference?

Ich habe mir drei (ja, drei) Fälle ausgedacht:
a) Die Methode/Funktion liest nur das Array-Argument
b) Die Methode/Funktion modifiziert das Array-Argument
c) Das Argument der Methode/des Funktionsarrays wird explizit als Referenz gekennzeichnet (mit einem Et-Zeichen).


Lassen Sie uns zunächst sehen, wie viel Speicher das Array tatsächlich belegt (run here ):

<?php
$start_memory = memory_get_usage();
$x = array_fill(0, 10000, 1);
echo memory_get_usage() - $start_memory; // 1331840

So viele Bytes. Groß.

a) die Methode/Funktion liest nur das Array-Argument

Lassen Sie uns nun eine Funktion erstellen, die liest nur das Array als Argument verwendet, und wir werden sehen, wie viel Speicher die Leselogik benötigt:

<?php

function printUsedMemory($arr) 
{
    $start_memory = memory_get_usage();

    count($arr);       // read
    $x = $arr[0];      // read (+ minor assignment)
    $arr[0] - $arr[1]; // read

    echo memory_get_usage() - $start_memory; // let's see the memory used whilst reading
}

$x = array_fill(0, 10000, 1); // this is 1331840 bytes
printUsedMemory($x);

Willst du raten? Ich bekomme 80! Überzeugen Sie sich selbst . Dies ist der Teil, den das Handbuch PHP= weglässt. Wenn der Parameter $arr tatsächlich als Wert übergeben wurde, sehen Sie etwas Ähnliches wie 1331840 Bytes. Es scheint, dass $arr sich wie eine Referenz verhält. Das liegt daran, dass es sich bei um interne Referenzen handelt.

b) die Methode/Funktion ändert das Array-Argument

Nun schreiben wir in diesen Parameter, anstatt ihn zu lesen:

<?php

function printUsedMemory($arr)
{
    $start_memory = memory_get_usage();

    $arr[0] = 1; // WRITE!

    echo memory_get_usage() - $start_memory; // let's see the memory used whilst reading
}

$x = array_fill(0, 10000, 1);
printUsedMemory($x);

Wieder sehen Sie selbst , aber für mich ist das ziemlich nahe daran, 1331840 zu sein. In diesem Fall wird also das Array tatsächlich nach $arr kopiert. .

c) Das Argument der Methode/des Funktionsarrays wird explizit als Referenz gekennzeichnet (mit einem Et-Zeichen).

Nun wollen wir sehen, wie viel Speicher ein Schreibvorgang auf eine explizite Referenz benötigt (run here ) - Beachten Sie das kaufmännische Und in der Funktionssignatur:

<?php

function printUsedMemory(&$arr) // <----- explicit, user-land, pass-by-reference
{
    $start_memory = memory_get_usage();

    $arr[0] = 1; // WRITE!

    echo memory_get_usage() - $start_memory; // let's see the memory used whilst reading
}

$x = array_fill(0, 10000, 1);
printUsedMemory($x);

Meine Wette ist, dass Sie maximal 200 bekommen! Das verbraucht also ungefähr so ​​viel Speicher wie das Lesen eines nicht-kaufmännischen und-Parameters .

72
nevvermind

Standardmäßig

  1. Primitive werden als Wert übergeben. Es ist unwahrscheinlich, dass Java String in PHP primitiv ist
  2. Arrays von Primitiven werden als Wert übergeben
  3. Objekte werden mit Referenz übergeben
  4. Arrays von Objekten werden als Wert (das Array) übergeben, aber jedes Objekt wird als Referenz übergeben.

    <?php
    $obj=new stdClass();
    $obj->field='world';
    
    $original=array($obj);
    
    
    function example($hello) {
        $hello[0]->field='mundo'; // change will be applied in $original
        $hello[1]=new stdClass(); // change will not be applied in $original
        $
    }
    
    example($original);
    
    var_dump($original);
    // array(1) { [0]=> object(stdClass)#1 (1) { ["field"]=> string(5) "mundo" } } 
    

Hinweis: Als Optimierung wird jeder einzelne Wert als Referenz übergeben, bis er innerhalb der Funktion geändert wird. Wenn es geändert wurde und der Wert als Referenz übergeben wurde, wird es kopiert und die Kopie wird geändert.

11
magallanes

Wenn ein Array in PHP an eine Methode oder Funktion übergeben wird, wird es als Wert übergeben, es sei denn, Sie übergeben es explizit als Referenz.

function test(&$array) {
    $array['new'] = 'hey';
}

$a = $array(1,2,3);
// prints [0=>1,1=>2,2=>3]
var_dump($a);
test($a);
// prints [0=>1,1=>2,2=>3,'new'=>'hey']
var_dump($a);

In Ihrer zweiten Frage $b ist kein Verweis auf $a, aber eine Kopie von $a.

Ähnlich wie im ersten Beispiel können Sie $a indem Sie wie folgt vorgehen:

$a = array(1,2,3);
$b = &$a;
// prints [0=>1,1=>2,2=>3]
var_dump($b);
$b['new'] = 'hey';
// prints [0=>1,1=>2,2=>3,'new'=>'hey']
var_dump($a);
4
Corey Ballou

Dieser Thread ist etwas älter, aber hier ist etwas, auf das ich gerade gestoßen bin:

Versuchen Sie diesen Code:

$date = new DateTime();
$arr = ['date' => $date];

echo $date->format('Ymd') . '<br>';
mytest($arr);
echo $date->format('Ymd') . '<br>';

function mytest($params = []) {
    if (isset($params['date'])) {
        $params['date']->add(new DateInterval('P1D'));
    }
}

http://codepad.viper-7.com/gwPYMw

Beachten Sie, dass es keinen Verstärker für den Parameter $ params gibt und trotzdem den Wert von $ arr ['date'] ändert. Dies stimmt nicht wirklich mit all den anderen Erklärungen hier und dem überein, was ich bisher gedacht habe.

Wenn ich das Objekt $ params ['date'] klone, bleibt das 2. ausgegebene Datum gleich. Wenn ich es nur auf einen String setze, hat es auch keinen Einfluss auf die Ausgabe.

1
robbash

Um eine der Antworten zu erweitern, werden auch Subarrays mehrdimensionaler Arrays als Wert übergeben, sofern dies nicht ausdrücklich als Referenz übergeben wird.

<?php
$foo = array( array(1,2,3), 22, 33);

function hello($fooarg) {
  $fooarg[0][0] = 99;
}

function world(&$fooarg) {
  $fooarg[0][0] = 66;
}

hello($foo);
var_dump($foo); // (original array not modified) array passed-by-value

world($foo);
var_dump($foo); // (original array modified) array passed-by-reference

Das Ergebnis ist:

array(3) {
  [0]=>
  array(3) {
    [0]=>
    int(1)
    [1]=>
    int(2)
    [2]=>
    int(3)
  }
  [1]=>
  int(22)
  [2]=>
  int(33)
}
array(3) {
  [0]=>
  array(3) {
    [0]=>
    int(66)
    [1]=>
    int(2)
    [2]=>
    int(3)
  }
  [1]=>
  int(22)
  [2]=>
  int(33)
}
0
K.Karamazen

In PHP werden Arrays standardmäßig als Wert an Funktionen übergeben, es sei denn, Sie übergeben sie explizit als Referenz, wie das folgende Snippet zeigt:

$foo = array(11, 22, 33);

function hello($fooarg) {
  $fooarg[0] = 99;
}

function world(&$fooarg) {
  $fooarg[0] = 66;
}

hello($foo);
var_dump($foo); // (original array not modified) array passed-by-value

world($foo);
var_dump($foo); // (original array modified) array passed-by-reference

Hier ist die Ausgabe:

array(3) {
  [0]=>
  int(11)
  [1]=>
  int(22)
  [2]=>
  int(33)
}
array(3) {
  [0]=>
  int(66)
  [1]=>
  int(22)
  [2]=>
  int(33)
}
0
John Sonderson