Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Problem z serializacją obiektu
Forum PHP.pl > Forum > PHP > Object-oriented programming
vegelus
Witam

Mam problem z serializacją obiektu posiadającego inne obiekty. Może przykład coś wyjaśni:
  1. class Map{
  2. private $wys;
  3. private $sze;
  4. private $map = array();
  5.  
  6. function __construct($wys,$sze){
  7. $this->wys = $wys;
  8. $this->sze = $sze;
  9. //przypisanie w map do każdego pola klasy obiektu z klasy field
  10. for ($x = 1; $x <= $this->sze; $x++) {
  11. for ($y = $this->wys; $y >= 1; $y--) {
  12. $losowa = rand(1, 4);
  13. $picPath = 'flor' . $losowa . '.png';
  14. $this->map[$y][$x] = new field($picPath);//obiekt nic nie robi poza przechowywaniem patha do obrazka i contentu pola
  15. }
  16. }
  17. }
  18.  
  19. function __sleep(){
  20. for ($x = 1; $x <= $this->sze; $x++) {
  21. for ($y = $this->wys; $y >= 1; $y--) {
  22. $_SESSION['maps'][$y][$x] = serialize($this->map[$y][$x]);
  23. }
  24. }
  25. return array('sze', 'wys');//chcę serializować tylko te dwa atrybuty
  26. }
  27. }


Efekt serializacji poszczególnych pól tablicy map jest prawidłowy ale w przypadku serializacji obiektu map :
  1. if (!isset($_SESSION['map'])) {
  2. $map = new map(4, 4);
  3. $_SESSION['map'] = serialize($map);
  4. } else {
  5. $map = unserialize($_SESSION['map']);
  6. }



Serializacji usilnie ładuje do sesji wszystko z obiektu łącznie atrybutem map, który już wcześniej sobie do sesji zapisałem i w __wakeup pobrałem z powrotem.

Jak zrobić aby serializując obiekt zapisać sobie proste zmienne a pozostałe serializować ręcznie.
A może jest jakiś inny lepszy sposób radzenia sobie z obiektami zawierającymi inne obiekty?


ps. poprawiłem smile.gif pisałem z ręki gdyż klasa zainteresowana serializacją ma już ponad 1000 linii i nie widziałem sensu jej tu całej pchać. Jest to wycinek tej klasy generujący moją frustrację oraz błąd.
ziqzaq
Aleś sobie namieszał smile.gif
  1. // Najpierw
  2. $this->wys = $wys;
  3. $this->sze = $sze;
  4. // a potem
  5. for ($x = 1; $x <= $this->width; $x++) {
  6. for ($y = $this->height; $y >= 1; $y--) {
  7. // i jeszcze chcesz serializowac
  8. return array('width', 'height');

no i jeszcze:
  1. //...
  2. funtion __sleep(){
  3. //...
zend
Cytat(vegelus @ 24.03.2010, 12:51:58 ) *
1.
if (!isset($_SESSION['map'])) {
2.
$map = new map(4, 4);
3.
$_SESSION['map'] = serialize($map);
4.
} else {
5.
$map = unserialize($_SESSION['map']);
6.
}


Takiego mixu to ja już dawno nie widziałem smile.gif Tworzysz obiekt a potem odrazu go serializujesz, nie wykonując na nim żadnych operacji smile.gif
To może ci się wydawać dobre i działać, ale świadczy tylko o złym zaprojektowaniu klasy. Zastanów się czy tych 1000 lini kodu nie da się rozłożyć do kilku klas, bo takie giganty strasznie zalatują "god's class", a z tąd już tylko krok do programowania strukturalnego.

Edit:
A poza tym staraj się utrzymywać jakąć stałą konwencję co do nazewnictwa, a nie mieszaj polskiego z angielskim, albo rybki albo akwarium smile.gif

A teraz będzie troche na temat smile.gif A że poźno jest, a ja zmęczony to będzie to metoda brutal force smile.gif
Po serializacji map chamsko wyczyść to pole unset'em, to dane nie będą się dublować, zakładająć oczywiście że nie masz jakoś dziwnie zaimplementowanych destruktorów w fildzie

A teraz dobranoc, koniec imprezy smile.gif Ide spać
vegelus
Dzięki za odpowiedz.

Jeszcze są dwie linijki kodu w index.php
  1. session_start();
  2.  
  3. if (!isset($_SESSION['map'])) {
  4. $map = new map(4, 4);
  5. $map->setHero(new Hero(2,1));
  6. } else {
  7. echo 'unserialize MAP<br>';
  8. $map = unserialize($_SESSION['map']);
  9. }
  10.  
  11. if (!empty($_GET['move'])) {
  12. $map->moveHero($_GET['move']);
  13. }
  14.  
  15. ?>
  16.  
  17. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  18. <html>
  19. <head>
  20. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  21. <link rel="Stylesheet" type="text/css" href="style.css" />
  22. <title>Plansza gry</title>
  23. </head>
  24. <body>
  25. <div id="kontener">
  26. <div id="map"><?=$map->paint(); ?></div>
  27. <div id="control"><?=control::paint(); ?></div>
  28. </div>
  29. <div><?php Komunikaty::getKomunikaty();?></div>
  30.  
  31.  
  32. </body>
  33. </html>
  34. <?php
  35. if (!isset($_SESSION['map'])) {
  36. $_SESSION['map'] = serialize($map);
  37. }


Na twoje rozwiązanie także wpadłem i zrobiłem sobie taką funkcję __sleep
  1. public function __sleep() {
  2. $_SESSION['hero'] = serialize($this->hero);
  3. for ($x = 1; $x <= $this->width; $x++) {
  4. for ($y = $this->height; $y >= 1; $y--) {
  5. $_SESSION['maps'][$y][$x] = serialize($this->map[$y][$x]);
  6. }
  7. }
  8. unset($this->hero);
  9. unset($this->map);
  10. return array('width', 'height', 'map', 'hero');
  11. }


Myślałem jednak, że istnieje jakiś ładniejszy sposób serializacji obiektów kontenerowych.
Dla potomnych może jeszcze podam jak budzę obiekt do życia pozostawiając oryginalne nazewnictwo:
  1. public function __wakeup() {
  2. $this->hero = unserialize($_SESSION['hero']);
  3. for ($x = 1; $x <= $this->width; $x++) {
  4. for ($y = $this->height; $y >= 1; $y--) {
  5. $this->map[$y][$x] = unserialize($_SESSION['maps'][$y][$x]);
  6. }
  7. }
  8. }


Udało mi się w końcu upchnąć obiekt w sesji smile.gif
Najwięcej czasu zajęło mi osiągnięcie tego efektu:
  1. [map] => O:3:"map":4:{s:10:"mapwidth";i:4;s:11:"mapheight";i:4;s:8:"mapmap";N;s:9:"maphero";N;}


Serializacjia przy pierwszym odpaleniu strony jest idealna, wszystkie obiekty wewnętrzne są w sesji i po deserializacji się odtwarzają, więc można powiedzieć, że osiągnąłem jakiś sukces. Ale po wykonaniu działań i przeładowaniu strony pojawia się problem, którym już nie wiem jak sobie poradzić. Mianowicie przy drugiej serializacji do zmiennej $_SESSION ładuje mi się coś takiego:

  1. [map] => map Object
  2. (
  3. [hero:private] => Hero Object
  4. (
  5. [actionPoint:private] => 29
  6. [posX:private] => 2
  7. [posY:private] => 2
  8. )
  9.  
  10. [width:private] => 4
  11. [height:private] => 4
  12. [map:private] => Array
  13. (
  14. [4] => Array
  15. (
  16. [1] => field Object
  17. (
  18. [picPath:private] => flor2.png
  19. [content:private] =>
  20. )
  21.  
  22. ...
  23.  


czyli obiekty hero i map nie zostały zniszczone i przekazane do serializacji jako obiekty anie NULL, co powoduje błąd przy deserializacji
Warning: unserialize() expects parameter 1 to be string, object given in D:\WebServ\httpd-users\pole_gry\index.php on line 8
zend
Pokręć się koło tematu session_register, bo wygląda na to że obiekt w sesji jest automatycznie deserializowany, a funkcja unserialize przyjmuje stringi a nie już odserializowane obiekty smile.gif Wszystko działa ok, wygląda na to że brakowało Ci tego klocka o automatycznej deserializacji smile.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.