2013-05-20 10 views
19

widzę dlaczegoGrając z odniesieniami

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] = 37; 
echo $a['ID']."\n"; 
echo $b."\n"; 
echo $c['ID']."\n"; 

Wyjścia 37, 42, 37

podczas

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$b = 37; 
echo $a['ID']."\n"; 
echo $b."\n"; 
echo $c['ID']."\n"; 

Wyjścia 37, 37, 37

W obu przypadkach $b jest odniesienie do $a['ID'] podczas gdy $c jest wskaźnikiem do tego samego obiektu co $a.

Kiedy $b zmienia $a['ID'] i $c['ID'] zmiany, ponieważ przypisanie $b zmienia wartość odwołuje $a['ID'].

Po zmianie $c['ID'], nowa int jest przypisana do $a['ID'], $b nie odnosi się już do $a['ID'].

Ale to swędzi mnie

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] &= 0; 
$c['ID'] |= 37; 
echo $a['ID']."\n"; 
echo $b."\n"; 
echo $c['ID']."\n"; 

(Wyjścia 37, 37, 37)

Czy to zdefiniowane zachowanie? nie widzę nic na ten temat w dokumentacji ...

+3

Tak, nie sądzę, że jest to udokumentowane. Instrukcja mówi tylko, że ++ i - wykonuje pośrednie modyfikacje i tym samym wyzwala 'ArrayAccess :: offsetGet()', a nie 'ArrayAccess :: offsetSet()'. Ze względu na spójność, myślę, że + =, - = i kuzyni zostali zaimplementowani w ten sam sposób. – cleong

+0

@cleong Nie mogę znaleźć tego w dokumentach. Czy możesz wskazać gdzie? – webmaster777

Odpowiedz

3

Weźmy ten kod jako podstawa: (refcounting documentation)

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 

xdebug_debug_zval('a'); 
xdebug_debug_zval('b'); 
xdebug_debug_zval('c'); 

Daje:

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 42 
b: 
(refcount=2, is_ref=1),int 42 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 42 

jak mówisz : $a to obiekt, $b jest numerem referencyjnym: $a['ID'] ($a['ID'] i $b: refcount=2, is_ref=1) i $ c is copy as a reference (since PHP5), więc $ c jest odniesienie $ a (to już ten sam obiekt: refcount=2, is_ref=0)


Jeśli mamy: $c['ID'] = 37;

Dostajemy:

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 37 
b: 
(refcount=1, is_ref=0),int 42 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 37 

$c['ID'] jest przypisany nowy int, więc =>

$b staje się niezależny (refcount=1 i is_ref=0), a także $a['ID'] i $c['ID']

ale jako $c i $a są zależne od $a['ID'] i $c['ID'] mieć taką samą wartość 37.


Teraz rzućmy kod bazowy i robimy: $c['ID'] &= 0;

UPDATE: Niespodziewanie, otrzymujemy:

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 0 
b: 
(refcount=2, is_ref=1),int 0 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 0 

zamiast: (jeżeli: $c['ID'] = $c['ID'] & 0;)

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 0 
b: 
(refcount=1, is_ref=0),int 42 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 0 

ArrayObject realizuje ArrayAccess sposób:

Jak wspomniano w komentarzu i documented here:

bezpośrednia zmiana jest, że zastępuje całkowicie wartość wymiar tablicy, jak w $ obj [6] = 7. Z drugiej strony, modyfikacja pośrednia zmienia jedynie część wymiaru lub próbuje przypisać wymiar przez odniesienie do innej zmiennej, jak w $ obj [6] [7] = 7 lub $ var = & $ obj [ 6]. Przyrosty ++ i dekrety z - są również implementowane w sposób, który wymaga pośredniej modyfikacji.

Ewentualna odpowiedź:

"Combined Operator (+ =, - =, & =, | =) może być pracował jako ten sam sposób (modyfikacja pośredni.)":

refcount i is_ref nie mają wpływu, dlatego (w naszym przypadku) wartości wszystkich zmiennych związanych z są modyfikowane. ($c['ID'] =>$a['ID'] =>$b)

+1

A nie widzisz, jak to jest nieoczekiwane w porównaniu do przykładu 1? – webmaster777

+1

Naprawdę, powinieneś spróbować tego kodu. ('$ c ['ID'] = $ c ['ID'] & 0') – webmaster777

+0

@ webmaster777, rzeczywiście ... myliłem się – antoox

2

To mniej lub bardziej określone (ale czasami nieudokumentowane) zachowanie; głównie dlatego $a nie jest array ale ArrayObject

Rzućmy okiem na swoim trzecim fragmentem kodu. pierwsza:

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] &= 0; 

ostatniej zmiany przypisania przekłada się na:

$tmp = &$c->offsetGet('ID'); 
$tmp &= 0; // or: $tmp = $tmp & 0; 

Punktem wynos tutaj jest tylkooffsetGet() nazywa i zwraca odniesienie do $c['ID'], jak zauważono w this comment. Ponieważ offsetSet() nie jest wywoływany, zmienia się również wartość $b.

Btw, operator inkrementacji (++) i dekrementacji (-) działa w podobny sposób, nie jest wywoływany offsetSet().

Różnice

ta różni się od pierwszego przykładu:

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] = 37; 

Ostatnia wypowiedź ma następującą równoważne:

$c->offsetSet('ID', 37); 

Zanim nowa wartość jest przypisana do $c['ID'] poprzednia wartość jest skutecznie unset(); dlatego właśnie $b jest jedyną zmienną wciąż trzymającą się 42.

Dowód tego zachowania można zaobserwować podczas korzystania z obiektów zamiast liczb:

class MyLoggerObj 
{ 
     public function __destruct() 
     { 
       echo "Destruct of " . __CLASS__ . "\n"; 
     } 
} 

$a = new ArrayObject(); 
$a['ID'] = new MyLoggerObj(); 
$a['ID'] = 37; 

echo $a['ID']."\n"; 

Output:

Destruct of MyLoggerObj 
37 

Jak widać, destruktor wywoływana jest MyLoggerObj; to dlatego, że w tym przypadku nie ma już zmiennej trzymającej się tej wartości.

Bonus

Jeśli próbujesz dowiedzieć się, kiedy offsetGet() i offsetSet() są nazywane przez rozszerzenie ArrayObject będzie rozczarowany, aby dowiedzieć się, że nie można prawidłowo naśladować mixed &offsetGet($key);. W rzeczywistości zmiana zachowania ArrayObject jest taka, że ​​niemożliwe jest udowodnienie zachowania tej klasy.

+1

Nadal nie jestem przekonany: P, jak to się stało, że b nie zostanie zaktualizowany w przykładzie 1 ? W zależności od twojego <_expected_ zachowanie offsetSet, zmienia odniesienie (pośrednia modyfikacja), ale wtedy b powinno być 37 aswell. – webmaster777

+0

Mogę tylko zgadywać, że inne semantyki działają tutaj, ale kiedy dojdę do komputera, poświęcę temu aspektowi trochę uwagi. –

+1

To spowodowałoby, że wynik w Ex.3 byłby 37.42.37 (ponieważ referencja $ c ['id'] jest rozbrajana przez twoje standardy, a potem ustawiona na 0). – webmaster777

Powiązane problemy