PHP
downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Klassen und Objekte (PHP 5)> <Klassen und Objekte
Last updated: Fri, 15 Aug 2008

view this page in

Referenzen innerhalb des Konstruktors

Referenzen innerhalb des Konstruktors können zu verwirrenden Resultaten führen. Dieser Abschnitt hilft, Probleme zu vermeiden.

class Foo
{
    function Foo($name)
    {
        // eine Referenz innerhalb des globalen Arrays  $globalref erstellen
        global $globalref;
        $globalref[] = &$this;
        // setze den Namen auf den übergebenen Wert
        $this->setName($name);
        // und gib' ihn aus
        $this->echoName();
    }

    function echoName()
    {
        echo "<br>",$this->name;
    }
    
    function setName($name)
    {
        $this->name = $name;
    }
}

Prüfen wir, ob zwischen $bar1, die mittels dem Operator zum Kopieren = erstellt wurde, und $bar2, die mittels dem Referenzoperator =& erstellt wurde, ein Unterschied besteht...

$bar1 = new Foo('set in constructor');
$bar1->echoName();
$globalref[0]->echoName();

/* Ausgabe:
set in constructor
set in constructor
set in constructor */

$bar2 =& new Foo('set in constructor');
$bar2->echoName();
$globalref[1]->echoName();

/* Ausgabe:
set in constructor
set in constructor
set in constructor */

Scheinbar besteht kein Unterschied, aber tatsächlich existiert ein signifikanter: $bar1 und $globalref[0] sind NICHT referenziert, d.h. sie sind NICHT die selbe Variable. Das kommt daher, dass "new" nicht automatisch eine Referenz, sondern eine Kopie zurückgibt.

Hinweis: Das zurückgeben von Kopien anstatt von Referenzen stellt keinen Performanceverlust dar (da PHP 4 und höher Reference Counting verwendet). Andererseits ist es sehr oft besser, einfach mit Kopien anstatt mit Referenzen zu arbeiten, da die Erstellung von Referenzen etwas Zeit in Anspruch nimmt, während das Erstellen von Kopien so gut wie keine Zeit braucht (sofern keine von ihnen ein großes Array oder Objekt ist, und eines davon geändert wird und das/die andere/n nachträglich. In diesem Fall wäre es besser, Referenzen zu verwenden, um sie alle gleichzeitig zu ändern).

Um das zuvor geschriebene zu beweisen, sehen wir uns den folgenden Code an.
// nun werden wir den Namen ändern. Was erwarten Sie?
// ...dass sowohl $bar1, als auch $globalref[0] ihre Namen ändern...
$bar1->setName('set from outside');

// wie bereits zuvor erwähnt, ist dies nicht der Fall
$bar1->echoName();
$globalref[0]->echoName();

/* Ausgabe:
set from outside
set in constructor */

// lassen Sie uns den Unterschied zwischen $bar2 and $globalref[1] ansehen
$bar2->setName('set from outside');

// glücklicherweise sind sie nicht nur nicht gleich, sondern auch die selbe
// Variable; demnach sind $bar2->name und $globalref[1]->name ebenfalls gleich
$bar2->echoName();
$globalref[1]->echoName();

/* Ausgabe:
set from outside
set from outside */

Ein anderes, letztes Beispiel zum Verständnis:

class A
{
    function A($i)
    {
        $this->value = $i;
        // finden Sie heraus, warum wir hier keine Referenz benötigen
        $this->b = new B($this);
    }

    function createRef()
    {
        $this->c = new B($this);
    }

    function echoValue()
    {
        echo "<br>","class ",get_class($this),': ',$this->value;
    }
}


class B
{
    function B(&$a)
    {
        $this->a = &$a;
    }

    function echoValue()
    {
        echo "<br>","class ",get_class($this),': ',$this->a->value;
    }
}

// überlegen Sie, warum hier die Verwendung einer einfachen Kopie in der
// mit * markierten Zeile zu einem unerwünschten Ergebnis führen würde
$a =& new A(10);
$a->createRef();

$a->echoValue();
$a->b->echoValue();
$a->c->echoValue();

$a->value = 11;

$a->echoValue();
$a->b->echoValue(); // *
$a->c->echoValue();

/*
Ausgabe:
class A: 10
class B: 10
class B: 10
class A: 11
class B: 11
class B: 11
*/



Klassen und Objekte (PHP 5)> <Klassen und Objekte
Last updated: Fri, 15 Aug 2008
 
add a note add a note User Contributed Notes
Referenzen innerhalb des Konstruktors
homo dot alpinus at gmail dot com
06-Apr-2008 04:17
Because new returns a reference since PHP 5.x using =& references the reference to the class.
mentepollo phpero
28-Aug-2007 05:20
Continuing aib's note (31-May-2006 11:20) on the new behavior of PHP 5.x:
Not only the "new" operator now returns a reference of the object (instead of a copy of the object as in PHP4), but more importantlly: now the assignment operator when acting on objects assigns by reference not by value.
This means operator =& is equal to operator = , but only when acting on objects. If you want to copy (clone) an object use: $B = clone $A;

Java creators got it right from the begining :)
geronimo
28-Nov-2006 04:44
To set properties of a class dynamically:
....

foreach($arr_properties as $key_prop=>$value_prop) {
    $temp = &$this->$key_prop;//property reference
        $temp ="new value";
}

....
aib
31-May-2006 08:20
Note that the "new" operator no longer returns a copy of the constructed object, but the object itself. This behavior may be as of PHP 5.x.

This means that
$foo = new class();
and
$foo =& new class();
are equivalent now, and you don't have to worry about the problems stated on this page.

Consider the following example:

<?php
class foo
{
    var
$num;

    function
foo()
    {
        global
$globalref;
       
$globalref[] = &$this;
    }
}

$bar = new foo();
$bar->num = 1;
$globalref[0]->num = 2;

echo
"\$bar->num = ".$bar->num."\n";
echo
"\$globalref[0]->num = ".$globalref[0]->num."\n";
?>

On PHP 5.0.1, it will print:
$bar->num = 2
$globalref[0]->num = 2

Whereas on PHP 4.4.0, it would print:
$bar->num = 1
$globalref[0]->num = 2
unless you assigned $bar to a new foo using a reference.
info at keenkelly dot com
11-Feb-2005 02:24
Regarding the use of assignment versus reference operators as they pertain to using classes which register shutdown functions.

Here's an example:
<?php
class testclass {
       var
$value;
       function
testclass() {
              
register_shutdown_function(array(&$this, 'tcshutdown'));
              
$this->value = 1;
               print
'Construction value=[' . $this->value ."]\n";
       }

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

       function
tcshutdown() {
               print
'Shutdown value=[' . $this->value ."]\n";
       }
}

$testobj = new testclass();
$testobj->setvalue(2);
?>

Despite changing the value to 2 after the object is constructed, the above code will result in the following output:

Construction value=[1]
Shutdown value=[1]

The reason is new testclass() constructs the object and registers tcshutdown function from that copy of the object. Then assigning it to $testobj makes a copy of the object accessible as $testobj. Altering $testobj values confines those changes to the $testobj copy and doesn't affect the original ("new") copy of the object. When shutdown time comes around and the tcshutdown() function is called, it calls the original ("new") copy of tcshutdown() NOT $testobj's copy. Thus tcshutdown() sees only the original member value that was initialized by the constructor before the object was copied to $testobj.

The way to resolve this is to, again, convert from assignment to a reference with the following alteration:

<?php
$testobj
=& new testclass();
?>

With that change, running the code again will result in what we expected:

Construction value=[1]
Shutdown value=[2]

This of course allows the tcshutdown() function to perform shutdown operations based on parameters which were altered subsequent to the construction of the object.
denis at yum
01-Sep-2004 12:22
Construction Methods

In case of PHP versions earlier 5.x (or both) you have to consider, that an instance of a class is not fully allocated while the construction method(s) aren't finished.

For example, this solution could fail:

class Child
{
   var $c;
  
   function Child(&$parent)
   {
      $this->c = &$parent;
   }
}

class Test
{
   var $a;
   var $var;
  
   function Test ()
   {
      $this->var = 123;
      $this->a = &new Child($this);
   }
}

$b = &new Test();
$b->a->c->var = 456;
echo $b->var;

[outputs: 123]

In order of really allocated instance references, append referenced objects after (and not during) the construction.
phpnotes at robertdhill dot com
03-Jul-2004 09:29
An earler note asked why the equal operator doesn't just make a reference, assuming that a direct reference would always be desired.

The answer is because a direct reference is *not* always desired.

When you instantiate a class, you are creating an object that has its own copy of all the class variables, access to the class methods, etc.

Now say you've created an instance of a class "MyClass" called "myObjectA" and you've done some processing to it, set some variables or whatever. Now say you want to create a duplicate of the object to do some changes, but you want to *keep the original.*

That is what the assignment (=) operator is for.

If you'd made a reference (&=), then any changes you make to the duplicate are also reflected in the original. This is undesireable.

So there are circumstances where the assignment (=) operator is required, and there are circumstances where the reference (&=) operator is required.

It just depends on what you're doing.

Hope this helps.
php dot net at game-point dot net
16-Jun-2004 09:02
As a bit of a newbie to objects, I found the final example very hard to work out at first.  I'd like to try and explain more clearly:

It all centres around what the $this variable actually is.  The answer is, it's an object.  It's the object that the $this variable is currently inside.

So let's say we call the first instance of class A that is created by my script "Instance1".  If I say,
<?php
   
class A {
        function
test() {
           
$foo = $this;
        }
    }

   
$myclass = new A();
?>

... then $this is the object Instance1.  Note that it is *NOT* the same as the object $myclass, as $myclass is simply a *COPY* of the object Instance1.  This means that if you create an object using the copy (=) operator, there is an instance of the object (in this case, Instance1) hanging around unreferenced.

Whereas if we use the reference (=&) operator to create an object, the first instance (Instance1) is directly referenced by the variable specified, and no copy is created.  eg.
<?php
   
class A {
        function
test() {
           
$foo = $this;
        }
    }
   
   
$myclass =& new A();
?>

... this assigns the variable $myclass directly to Instance1, with no need for Instance1 to be copied.  There is only one instance of the class created, and it is assigned to $myclass.

In brief, if we use <--> to indicate a reference between a variable and an instance of a class:
Statement               | Results in...
$myclass = new A();     | Instance1 Instance2<-->$myclass      (Instance2 is a copy)
$myclass =& new A();    | Instance1<-->$myclass

All seems simple when you see the diagram, eh?  :-)  Of course, you may be asking the question, "Why doesn't the '=' operator just make $myclass reference first object Instance1?  Why would you ever need Instance1 to be copied, surely all you'd ever want or need is a direct reference to the newly created object Instance1?"  Well, that's the same question I and others are asking.  Hopefully this behaviour will change in a later version of PHP.

Hope that helps.
blueshadow822 at hotmail dot com
10-Feb-2004 12:20
Original : rick_php_net at roundeye dot net
Date : 01-Oct-2002 05:37
Modify : bitmore.co.kr

class StaticList {
    var $list = 0;

    function StaticList($name = '') {
        global $master;

        if (!isset($master)) {
            print "creating new list $name<Br>\n";
            //$master = $this;
            $master =& $this;
            $master->list = array();
        }else {
            print "creating new list $name... using global list<br>\n";
        }
        //$this->list =& $master->list;
        //$this->list = $master->list;
        return ($this);
    }

    function show() {
        if (is_array($this->list)) {
            if (count($this->list)) {
                reset($this->list);
                while (list($key, $value) = each($this->list)) {
                    print "list[$key] => $value<br>\n";
                }
            }else {
                print "list empty.<br>\n";
            }
        }else {
            print "list is not a list.<br>\n";
        }
    }

    function append($val) {
        if (!is_array($this->list)) {
            print "forced to create list...<br>\n";
            $this->list = array();
        }else {
            print "appending $val to list<br>\n";
            $this->list[] = $val;
        }
    }
}

$a = new StaticList("a");
$a->append(1);
$a->append(2);

$b = new StaticList("b");
$b->append(3);
$b->append(4);
$a->append(5);
$b->append(6);

print "Contents of a:<br>\n";
$a->show();

print "Contents of a:<br>\n";
$b->show();

this Show Printing

creating new list a
appending 1 to list
appending 2 to list
creating new list b
appending 3 to list
appending 4 to list
appending 5 to list
appending 6 to list
Contents of a:
list[0] => 1
list[1] => 2
list[2] => 5
Contents of a:
list[0] => 3
list[1] => 4
list[2] => 6
kyong
30-Jan-2004 03:45
Here's a reminder to those of you are going to use
PHP 5.0.0b3. You don't need to add & before $this any more
to get that object reference.
jjh at gmx dot at
25-Apr-2003 10:52
This is another sample script to demonstrate the use of references on objects.
To test the script first replace all <b r> (note the space!) tags by normal
HTML-linke-brakes! Normal line brakes (what a shame!) cannot be shown inside a
php.net-note!!!

<?
// Class C contains instances of class A and class B.
//
// A should "know" B and B should "know" A.
// In other words: Object A should have a Reference on
// object B and vice versa.

class A {
    var $a;
    var $a_b; // this links to object B
    function A() {
        $this->a="set by constructor of A";
    }
   
    function getToKnowB(&$b) {
        $this->a_b =& $b;
    }
}
class B {
    var $b;
    var $b_a; // this links to object A
    function B() {
        $this->b="set by constructor of B";
    }
   
    function getToKnowA(&$a) {
        $this->b_a =& $a;
    }
}
class C {
    var $myA;
    var $myB;
   
    function C() {
        // Create Objects A and B
        $this->myA = new A;
        $this->myB = new B();
       
        // "Introduce" A and B to each other
        $this->myA->getToKnowB($this->myB);
        $this->myB->getToKnowA($this->myA);
       
        // Prove that A and B are using References (not copies!)
        echo "Prove that myA->a equals myB->b_a->a<br>";
        $this->myA->a = "one";
        echo "this->myA->a is set to ".$this->myA->a."<br>";
       
        $this->myB->b_a->a = "two";
        echo "this->myB->b_a->a is set to ".$this->myB->b_a->a."<br>";
       
        echo "this->myA->a: ".$this->myA->a."<br>";
        echo "this->myB->b_a->a: ".$this->myB->b_a->a."<br>";

        echo "<br>";
       
        echo "Prove that myB->b equals myA->a_b->b<br>";
        $this->myB->b = "one";
        echo "this->myB->b is set to ".$this->myB->b."<br>";
       
        $this->myA->a_b->b = "two";
        echo "this->myA->a_b->b is set to ".$this->myA->a_b->b."<br>";
       
        echo "this->myB->b: ".$this->myB->b."<br>";
        echo "this->myA->a_b->b: ".$this->myA->a_b->b."<br>";

    }
}

$myC = new C;

/*
evaluates to:

Prove that myA->a equals myB->b_a->a
this->myA->a is set to one
this->myB->b_a->a is set to two
this->myA->a: two
this->myB->b_a->a: two

Prove that myB->b equals myA->a_b->b
this->myB->b is set to one
this->myA->a_b->b is set to two
this->myB->b: two
this->myA->a_b->b: two
*/
?>
rick_php_net at roundeye dot net
01-Oct-2002 01:37
I was attempting to find a way to share data structures between objects and ideally wanted to be able to have a single shared object that would be returned as a reference from the constructor -- without having the caller use a different syntax.

I couldn't get the effect I wanted, but was able to share the internal data structures (which is where the bulk of the storage was in my application).  This is an example class I constructed (requires php >= 4.0.4) which illustrates the idea:

    class StaticList
    {
        var $list = 0;

        function StaticList($name = '')
        {
            global $master;

            if (!isset($master))
            {
                print "creating new list $name<Br>\n";
                $master = $this;
                $master->list = array();
            }
            else
            {
                print "creating new list $name... using global list<br>\n";
            }
            $this->list =& $master->list;
            return ($this);
        }

        function show()
        {
            if (is_array($this->list))
            {
                if (count($this->list))
                {
                    reset($this->list);
                    while (list($key, $value) = each($this->list))
                    {
                        print "list[$key] => $value<br>\n";
                    }
                }
                else
                {
                    print "list empty.<br>\n";
                }
            }
            else
            {
                print "list is not a list.<br>\n";
            }
        }

        function append($val)
        {
            if (!is_array($this->list))
            {
                print "forced to create list...<br>\n";
                $this->list = array();
            }
            else
            {
                print "appending $val to list<br>\n";
                $this->list[] = $val;
            }
        }
    }

    $a = new StaticList("a");
    $a->append(1);
    $a->append(2);

    $b = new StaticList("b");
    $b->append(3);
    $b->append(4);
    $a->append(5);
    $b->append(6);

    print "Contents of a:<br>\n";
    $a->show();

    print "Contents of a:<br>\n";
    $b->show();
rpav at NOSPAMusers dot sf dot net
14-Aug-2002 12:13
Ah HA! I just found an easy workaround with a bit of playing.  This works:

--- cut ---
$OBJECTS = array();

class Object {
    function &Object() {
        global $OBJECTS;
        $OBJECTS[] = &$this;
    }

    function _init() {

    }
}

class Derived {
    function &Derived() {
        Object::Object();
    }
}

function blah() {
    global $FOO;

    $x =& new Derived;
    $GLOBALS['FOO'] = &$x;
    $x->foo = 42;
    display("x", $x);
}

function display($text, &$o) {
    echo("($text) =>" . serialize($o));
}

blah();
display("GLOBALS[FOO]", $GLOBALS['FOO']);
display("OBJECTS", $OBJECTS);
--- cut ---

As you can see, simply adding & before the constructor to make it return a reference does the trick.  The bit about "returning a copy by default" lead me to try this trick, and it seems to work.  Have fun!

-rjp

Klassen und Objekte (PHP 5)> <Klassen und Objekte
Last updated: Fri, 15 Aug 2008
 
 
show source | credits | sitemap | contact | advertising | mirror sites