Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> Referencje do obiektu
Skie
post
Post #1





Grupa: Zarejestrowani
Postów: 555
Pomógł: 84
Dołączył: 20.02.2008
Skąd: Małopolska

Ostrzeżenie: (0%)
-----


Witam,
mam problem z referencjami , z którymi nie mogę sobie poradzć (PHP 5.4).
Napisałem sobie klasę cache'ującą, która zbiera referencje do pewnych obiektów i trzyma je w odpowiedniej strukturze.
W przypadku, gdy pojawiają się w niej referencje do tego samego obiektu zaczynają się schodki, które wydają się być błędem związanym z PHP niż z moją klasą.

Zobrazuję to w poniższym przykładzie.
  1. $man = new StashManager();
  2.  
  3. $obj = new StdClass;
  4. $obj -> id = 1;
  5. $obj -> x = 5;
  6. $obj -> y = 5;
  7. $man -> InsertInto("key1", $obj); // zapisuje w kontenerze pod kluczem key1 referencję do obiektu1
  8. $man -> InsertInto("key2", $obj); // zapisuje w kontenerze pod kluczem key2 referencje do obiektu1
  9. unset($obj); // czyszczę zmienną
  10.  
  11. $obj = &$man -> Select("key2"); // pobieram referencję do obiektu1


I teraz tak , jeżęli zmienię jakaś składową, np:
  1. $obj -> x = 10;


Po czym wyświetlę stan kontenera - obiekt key1 i key2 mają x ustawione na 10 - w końcu to ten sam obiekt.
Teraz chcę zamiast tego usunąć zawartość tej refrencji, robię więc:
  1. $obj = null;


Odczytuje dane i.... Tylko obietk spod key2 jest nullem. Obiekt spod key1 został niezmieniony.


Problemem okazuje się być funkcja pobierająca miejsce, wk tórym ma być wpisany obiekt.
Gdy wpisuję do kontenera ręcznie wartość:
  1. $this -> stash[...] = &$obj;


Wszystko działa w porządku. Jednak, gdy robię to pobierająć dokładne miejsce w kontenerze za pomocą funkcji:
  1.  
  2. public function &GetPlace($path, $breakOnError = false, $offset = 0) {
  3. $arr = explode('/',$path);
  4. $place = &$this -> stash;
  5. $limit = count($arr);
  6.  
  7. $i = 0;
  8. foreach ($arr as $el) {
  9. if ($i-$offset >= $limit) { break; }
  10.  
  11. if (($child = &$this -> GetChild($place, $el)) === null) {
  12. if ($breakOnError) { $empty = null; return $empty; } else { $obj = array(); $this -> SetChild($place, $el, $obj); unset($obj); }
  13. }
  14. $place = &$this -> GetChild($place, $el);
  15.  
  16. $i++;
  17. }
  18.  
  19. return $place;
  20. }
  21.  
  22. public function &GetChild(&$place, $el) {
  23. if (is_array($place) && isset($place[$el])) {
  24. return $place[$el];
  25. } else if (is_object($place) && isset($place -> $el)) {
  26. return $place -> $el;
  27. } else {
  28. $empty = null;
  29. return $empty;
  30. }
  31. }


Co robię źle?

Co robię źle?

Ten post edytował Skie 21.10.2013, 23:44:24
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi
Skie
post
Post #2





Grupa: Zarejestrowani
Postów: 555
Pomógł: 84
Dołączył: 20.02.2008
Skąd: Małopolska

Ostrzeżenie: (0%)
-----


freemp3

1. W pierwszym przypadku wpisałem:
  1. $place = &$this -> stash;


Dlatego, że jak potem widzisz, we wnętrzu funkcji Place, co iterację, funckja sprawdza, czy dana składowa istnieje w obecnym zagnieżdżeniu , innymi słowy, jak chcę dodać obiekt pod ścieżkę załóżmy /node1/node2/2, to zadaniem funkcji GetPlace() jest dobrać się do:
$this -> stash -> node1 -> node2 -> 2

I oczywiście zwrócić referencję do tego, by można było oryginalną zmienną z tego adresu zmienić / usunąć / zastąpić etc.
Chętnie bym to zrobił inaczej, ale nie znająć ilości zagłębień, nie wiem jak mógłbym to przeiterować bez referencji. Wszelkie sugestie mile widziane.

2. Co do:
  1. public function &GetChild(&$place, $el);
  2. $place = &$this -> GetChild($place, $el);


Musi tak być bo inaczej nie zadziała. Referencje w PHPie są dość dziwne i w pierwszym przypadku zwrócenie referencji mówi, żeby "zwrócić oryginalną zmienną", która jest w return a nie jej kopii.
Ale mimo to, jeżeli dobiorę się do niej w ten sposób:
  1. $place = $this -> GetChild($place, $el); // bez referencji


To mimo, że funkcja zwraca oryginalny obiekt, to operator = bez referencji skopiuje go do $place i powiązanie zostanie utracone.

Np:
  1. function &Test(&$obj) {
  2. return $obj;
  3. }
  4.  
  5. $obj = new StdClass;
  6. $obj -> val = 5;
  7. $tst = &Test($obj);
  8. $tst -> val = 10; // obj -> val == 10
  9. $tst = null; // obj == null
  10. unset($obj);
  11. unset($tst);
  12.  
  13. /* bez ref */
  14. $obj = new StdClass;
  15. $obj -> val = 5;
  16. $tst = Test($obj);
  17. $tst -> val = 10; // obj -> val == 10
  18. $tst = null; // obj == StdClass ( x => 10 )



Crozin:

Dlaczego mam złe wyobrażenia na temat działania kodu? Linkujesz strony manuala dobrze mi znane, więc nie pomaga mi to w zrozumieniu czego nie rozumiem (IMG:style_emoticons/default/smile.gif)
Jeżeli masz na myśli trochę odbiegający sposób tłumaczenia mojego kodu w postach powyżej to wszyskto pisałem w cudzysłowach , właśnie po to, że nie dokładnei to tak działa , ale "mozna tak powiedzieć" dla uproszczenia sprawy.
Chyba, że masz na myśli jeszcze coś innego?

Klasa ma być używana do cache'owania nabieżąco obiektów z możliwością zagnieżdżania struktur. Innymi słowy mam gdzieś w kodzie obiekt, który chcę cache'ować, więc dodaję go do kontenere pod adres /node1/node2/2

  1. $this -> InsertInto("node1/node2/2", $obj);


Załóżmy, że potrzebuję go potem używać w innym miejscu , więc sprawdzam:

  1. if ($this -> Exists("node1/node2/2")) {
  2. $obj = &$this -> Select("node1/node2/2");
  3. } else {
  4. // obiekt nie istnieje w cache, więc wykonaj kosztowne operacje by go pobrać/utworzyć
  5. }


I teraz tak - jeżeli zmienię zawartość $obj - chcę by uległ on zmianie również w kontenerze. Dlatego korzystam z referencji.
Dopuszczam, możliwość, że ten sam obiekt w kontenerze może istnieć pod różnymi ścieżkami (dla szybszego odczytu).

Zależy mi by było to w pełni "flexible", więc np. poniższy kod:
[php]
$obj = &$this -> Select("node1/node2");
[php]

Też powinien zadziałać i zwrócić:
Array (
[2] => Object() // tutaj znajduje się wcześniej wpisany $obj;
)

Nie tworzę w klasie rozbudowanych struktur do trzymania tego, tylko wszystko wykonuję poprzez referencje na samym $this -> stash właśnie po to, by takie operacje były możliwe, bez komplikowania kodu.

W obecnej formie wszystko działa oprócz usuwania obiektu z kontenera (zostaje usunięty tylko z podanej ścieżki, a pozostałe referencje do niego w kontenerze w magiczny sposób się kopiują, jak podałem w pierwszym poście).

Ten post edytował Skie 22.10.2013, 13:13:01
Go to the top of the page
+Quote Post

Posty w temacie


Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 11.10.2025 - 09:44