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

search for in the

オブジェクトのシリアライズ> <遅延静的束縛 (Late Static Bindings)
Last updated: Fri, 03 Sep 2010

view this page in

オブジェクトと参照

PHP5 でのオブジェクト指向プログラミングのポイントとしてよく言われるのは 「オブジェクトはデフォルトでは参照渡しとなります」ということです。 しかし、正確には少し異なります。 この節では、いくつかの例を用いてその誤解をといていきます。

PHP の参照は一種のエイリアスで、ふたつの異なる変数に同じ値を書き込めるものです。 PHP5 以降、オブジェクト変数の値にオブジェクト自身は含まれなくなりました。 含まれるのはオブジェクトの ID のみで、 これを用いて実際のオブジェクトにアクセスできるようになっています。 オブジェクトが引数として渡されたり返り値となったり あるいは別の変数に代入されたりした場合、 それはエイリアスではありません。ID のコピーを保持し、 同じオブジェクトを指すようになるのです。

例1 参照とオブジェクト

<?php
class {
    public 
$foo 1;
}  

$a = new A;
$b $a;     // $a と $b は同じ ID を持つコピーです
             // ($a) = ($b) = <id>
$b->foo 2;
echo 
$a->foo."\n";


$c = new A;
$d = &$c;    // $c と $d は参照です
             // ($c,$d) = <id>

$d->foo 2;
echo 
$c->foo."\n";


$e = new A;

function 
foo($obj) {
    
// ($obj) = ($e) = <id>
    
$obj->foo 2;
}

foo($e);
echo 
$e->foo."\n";

?>

上の例の出力は以下となります。

2
2
2


add a note add a note User Contributed Notes
オブジェクトと参照
Hayley Watson
30-Mar-2010 03:21
Using &$this can result in some weird and counter-intuitive behaviour - it starts lying to you.

<?php

class Bar
{
    public
$prop = 42;
}

class
Foo
{
    public
$prop = 17;
    function
boom()
    {
       
$bar = &$this;
        echo
"\$bar is an alias of \$this, a Foo.\n";
        echo
'$this is a ', get_class($this), '; $bar is a ', get_class($bar), "\n";

        echo
"Are they the same object? ", ($bar === $this ? "Yes\n" : "No\n");
        echo
"Are they equal? ", ($bar === $this ? "Yes\n" : "No\n");
        echo
'$this says its prop value is ';
        echo
$this->prop;
        echo
' and $bar says it is ';
        echo
$bar->prop;
        echo
"\n";

        echo
"\n";

       
$bar = new Bar;
        echo
"\$bar has been made into a new Bar.\n";
        echo
'$this is a ', get_class($this), '; $bar is a ', get_class($bar), "\n";

        echo
"Are they the same object? ", ($bar === $this ? "Yes\n" : "No\n");
        echo
"Are they equal? ", ($bar === $this ? "Yes\n" : "No\n");
        echo
'$this says its prop value is ';
        echo
$this->prop;
        echo
' and $bar says it is ';
        echo
$bar->prop;
        echo
"\n";

    }
}

$t = new Foo;
$t->boom();
?>
In the above $this claims to be a Bar (in fact it claims to be the very same object that $bar is), while still having all the properties and methods of a Foo.

Fortunately it doesn't persist beyond the method where you committed the faux pas.
<?php
echo get_class($t), "\t", $t->prop;
?>
miklcct at gmail dot com
07-Jan-2010 09:34
Notes on reference:
A reference is not a pointer. However, an object handle IS a pointer. Example:
<?php
class Foo {
  private static
$used;
  private
$id;
  public function
__construct() {
   
$id = $used++;
  }
  public function
__clone() {
   
$id = $used++;
  }
}

$a = new Foo; // $a is a pointer pointing to Foo object 0
$b = $a; // $b is a pointer pointing to Foo object 0, however, $b is a copy of $a
$c = &$a; // $c and $a are now references of a pointer pointing to Foo object 0
$a = new Foo; // $a and $c are now references of a pointer pointing to Foo object 1, $b is still a pointer pointing to Foo object 0
unset($a); // A reference with reference count 1 is automatically converted back to a value. Now $c is a pointer to Foo object 1
$a = &$b; // $a and $b are now references of a pointer pointing to Foo object 0
$a = NULL; // $a and $b now become a reference to NULL. Foo object 0 can be garbage collected now
unset($b); // $b no longer exists and $a is now NULL
$a = clone $c; // $a is now a pointer to Foo object 2, $c remains a pointer to Foo object 1
unset($c); // Foo object 1 can be garbage collected now.
$c = $a; // $c and $a are pointers pointing to Foo object 2
unset($a); // Foo object 2 is still pointed by $c
$a = &$c; // Foo object 2 has 1 pointers pointing to it only, that pointer has 2 references: $a and $c;
const ABC = TRUE;
if(
ABC) {
 
$a = NULL; // Foo object 2 can be garbage collected now because $a and $c are now a reference to the same NULL value
} else {
  unset(
$a); // Foo object 2 is still pointed to $c
}
mjung at poczta dot onet dot pl
16-Aug-2009 05:25
Ultimate explanation to object references:
NOTE: wording 'points to' could be easily replaced with 'refers ' and is used loosly.
<?php
$a1
= new A(1);  // $a1 == handle1-1 to A(1)
$a2 = $a1;     // $a2 == handle1-2 to A(1) - assigned by value (copy)
$a3 = &$a1// $a3 points to $a1 (handle1-1)
$a3 = null;      // makes $a1==null, $a3 (still) points to $a1, $a2 == handle1-2 (same object instance A(1))
$a2 = null;      // makes $a2 == null
$a1 = new A(2); //makes $a1 == handle2-1 to new object and $a3 (still) points to $a1 => handle2-1 (new object), so value of $a1 and $a3 is the new object and $a2 == null
//By reference:
$a4 = &new A(4);  //$a4 points to handle4-1 to A(4)
$a5 = $a4;   // $a5 == handle4-2 to A(4) (copy)
$a6 = &$a4//$a6 points to (handle4-1), not to $a4 (reference to reference references the referenced object handle4-1 not the reference itself)

$a4 = &new A(40); // $a4 points to handle40-1, $a5 == handle4-2 and $a6 still points to handle4-1 to A(4)
$a6 = null// sets handle4-1 to null; $a5 == handle4-2 = A(4); $a4 points to handle40-1; $a6 points to null
$a6 =&$a4; // $a6 points to handle40-1
$a7 = &$a6; //$a7 points to handle40-1
$a8 = &$a7; //$a8 points to handle40-1
$a5 = $a7//$a5 == handle40-2 (copy)
$a6 = null; //makes handle40-1 null, all variables pointing to (hanlde40-1 ==null) are null, except ($a5 == handle40-2 = A(40))
?>
Hope this helps.
Aaron Bond
20-Jun-2009 01:08
I've bumped into a behavior that helped clarify the difference between objects and identifiers for me.

When we hand off an object variable, we get an identifier to that object's value.  This means that if I were to mutate the object from a passed variable, ALL variables originating from that instance of the object will change. 

HOWEVER, if I set that object variable to new instance, it replaces the identifier itself with a new identifier and leaves the old instance in tact.

Take the following example:

<?php
class A {
    public
$foo = 1;


class
B {
    public function
foo(A $bar)
    {
       
$bar->foo = 42;
    }
   
    public function
bar(A $bar)
    {
       
$bar = new A;
    }
}

$f = new A;
$g = new B;
echo
$f->foo . "\n";

$g->foo($f);
echo
$f->foo . "\n";

$g->bar($f);
echo
$f->foo . "\n";

?>

If object variables were always references, we'd expect the following output:
1
42
1

However, we get:
1
42
42

The reason for this is simple.  In the bar function of the B class, we replace the identifier you passed in, which identified the same instance of the A class as your $f variable, with a brand new A class identifier.  Creating a new instance of A doesn't mutate $f because $f wasn't passed as a reference.

To get the reference behavior, one would have to enter the following for class B:

<?php
class B {
    public function
foo(A $bar)
    {
       
$bar->foo = 42;
    }
   
    public function
bar(A &$bar)
    {
       
$bar = new A;
    }
}
?>

The foo function doesn't require a reference, because it is MUTATING an object instance that $bar identifies.  But bar will be REPLACING the object instance.  If only an identifier is passed, the variable identifier will be overwritten but the object instance will be left in place.
Ivan Bertona
15-Oct-2008 03:45
A point that in my opinion is not stressed enough in the manual page is that in PHP5, passing an object as an argument of a function call with no use of the & operator means passing BY VALUE an unique identifier for that object (intended as instance of a class), which will be stored in another variable that has function scope.

This behaviour is the same used in Java, where indeed there is no notion of passing arguments by reference. On the other hand, in PHP you can pass a value by reference (in PHP we refer to references as "aliases"), and this poses a threat if you are not aware of what you are really doing. Please consider these two classes:

<?php
class A
{
    function
__toString() {
        return
"Class A";
    }
}
   
class
B
{
    function
__toString() {
        return
"Class B";
    }
}
?>

In the first test case we make two objects out of the classes A and B, then swap the variables using a temp one and the normal assignment operator (=).

<?php
$a
= new A();
$b = new B();
   
$temp = $a;
$a = $b;
$b = $temp;
   
print(
'$a: ' . $a . "\n");
print(
'$b: ' . $b . "\n");
?>

As expected the script will output:

$a: Class B
$b: Class A

Now consider the following snippet. It is similar to the former but the assignment $a = &$b makes $a an ALIAS of $b.

<?php
$a
= new A();
$b = new B();
   
$temp = $a;
$a = &$b;
$b = $temp;
   
print(
'$a: ' . $a . "\n");
print(
'$b: ' . $b . "\n");
?>

This script will output:

$a: Class A
$b: Class A

That is, modifying $b reflects the same assignment on $a... The two variables end pointing to the same object, and the other one is lost. To sum up is a good practice NOT using aliasing when handling PHP5 objects, unless your are really really sure of what you are doing.
lazybones_senior
01-Oct-2008 01:57
WHOA... KEEP IT SIMPLE!

In regards to secure_admin's note: You've used OOP to simplify PHP's ability to create and use object references. Now use PHP's static keyword to simplify your OOP.

<?php

class DataModelControl {
  protected static
$data = 256; // default value;
 
protected $name;

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

  public static function
setData($dmcData) {
    if(
is_numeric($dmcData)) {
     
self::$data = $dmcData;
    }
  }

  public function
__toString() {
    return
"DataModelControl [name=$this->name, data=" . self::$data . "]";
  }  
}

# create several instances of DataModelControl...
$dmc1 = new DataModelControl('dmc1');
$dmc2 = new DataModelControl('dmc2');
$dmc3 = new DataModelControl('dmc3');
echo
$dmc1 . '<br>';
echo
$dmc2 . '<br>';
echo
$dmc3 . '<br><br>';

# To change data, use any DataModelControl object...
$dmc2->setData(512);
# Or, call setData() directly from the class...
DataModelControl::setData(1024);
echo
$dmc1 . '<br>';
echo
$dmc2 . '<br>';
echo
$dmc3 . '<br><br>';
?>

 DataModelControl [name=dmc1, data=256]
 DataModelControl [name=dmc2, data=256]
 DataModelControl [name=dmc3, data=256]

 DataModelControl [name=dmc1, data=1024]
 DataModelControl [name=dmc2, data=1024]
 DataModelControl [name=dmc3, data=1024]

... even better! Now, PHP creates one copy of $data, that is shared amongst all DataModelControl objects.
secure_admin
30-Sep-2008 11:38
USE OOP to ACCESS OBJECT REFERENCES

The PHP language itself offers a slew of nifty operators that can copy, clone, and alias objects and references in many ways. But that kind of syntax looks rather fearsome. Here, I use OOP to get the same results, but with cleaner and more practical code. Below, one DataModel object is instantiated so that many instances of DataControl can use and alter it. Regardless of how PHP works, the OOP styled setup keeps all DataControl instances "on the same page" because they are all looking at the "same model" - which this code clearly shows.

<?php

class DataModel {
  protected
$name, $data;

  public function
__construct($dmName, $dmData) {
   
$this->name = $dmName;
   
$this->setData($dmData);
  }

  public function
setData($dmData) {
    if(
is_numeric($dmData)) {
     
$this->data = $dmData;
    }
  }

  public function
__toString() {
    return
"DataModel [name=$this->name, data=$this->data]";
  }  
}

class
DataControl {
  protected
$name, $model;

  public function
__construct($dcName, $dcModel) {
   
$this->name = $dcName;
   
$this->model = $dcModel;
  }

  public function
setData($dmData) {
   
$this->model->setData($dmData);
  }

  public function
__toString() {
    return
"DataController [name=$this->name, model=" . $this->model->__toString() . "]";
  }
}

# create one instance of DataModel...
$model = new DataModel('dm1', 128);
echo
$model . '<br><br>';

# create several instances of DataControl, passing $model to each one...
$dc1 = new DataControl('dc1', $model);
$dc2 = new DataControl('dc2', $model);
$dc3 = new DataControl('dc3', $model);
echo
$dc1 . '<br>';
echo
$dc2 . '<br>';
echo
$dc3 . '<br><br>';

# To change data, use any $dataControl->setData()...
$dc3->setData(512);

echo
$dc1 . '<br>';
echo
$dc2 . '<br>';
echo
$dc3 . '<br><br>';

?>
* * * output * * *
DataModel [name=dm1, data=128]

DataController [name=dc1, model=DataModel [name=dm1, data=128]]
DataController [name=dc2, model=DataModel [name=dm1, data=128]]
DataController [name=dc3, model=DataModel [name=dm1, data=128]]

DataController [name=dc1, model=DataModel [name=dm1, data=512]]
DataController [name=dc2, model=DataModel [name=dm1, data=512]]
DataController [name=dc3, model=DataModel [name=dm1, data=512]]
wbcarts at juno dot com
30-Sep-2008 08:23
A BIT DILUTED... but it's alright!

In the PHP example above, the function foo($obj), will actually create a $foo property to "any object" passed to it - which brings some confusion to me:
  $obj = new stdClass();
  foo($obj);    // tags on a $foo property to the object
                // why is this method here?
Furthermore, in OOP, it is not a good idea for "global functions" to operate on an object's properties... and it is not a good idea for your class objects to let them. To illustrate the point, the example should be:

<?php

class A {
  protected
$foo = 1;

  public function
getFoo() {
    return
$this->foo;
  }

  public function
setFoo($val) {
    if(
$val > 0 && $val < 10) {
     
$this->foo = $val;
    }
  }

  public function
__toString() {
    return
"A [foo=$this->foo]";
  }
}

$a = new A();
$b = $a;                        // $a and $b are copies of the same identifier
                                // ($a) = ($b) = <id>
$b->setFoo(2);
echo
$a->getFoo() . '<br>';

$c = new A();
$d = &$c;                       // $c and $d are references
                                // ($c,$d) = <id>
$d->setFoo(2);
echo
$c . '<br>';

$e = new A();
$e->setFoo(16);                 // will be ignored
echo $e;

?>
- - -
 2
 A [foo=2]
 A [foo=1]
 - - -
Because the global function foo() has been deleted, class A is more defined, robust and will handle all foo operations... and only for objects of type A. I can now take it for granted and see clearly that your are talking about "A" objects and their references. But it still reminds me too much of cloning and object comparisons, which to me borders on machine-like programming and not object-oriented programming, which is a totally different way to think.

 
show source | credits | sitemap | contact | advertising | mirror sites