Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: PHP aktualizuje zserializowany w sesji obiekt
Forum PHP.pl > Forum > PHP
macq
Witam,

wykorzystywałem ostatnio sesje do przechowywania obiektów i spostrzegłem bardzo ciekawe zachowanie PHP, tj. jeżeli zapiszę obiekt do sesji to przy jego dalszej modyfikacji niejawnie aktualizowany jest on w sesji. Czy ktoś jest w stanie wytłumaczyć jak działa ten mechanizm 'pod spodem'?

Przygotowałem paczkę która prezentuje to zagadnienie:

SessionRegistry.php
  1. <?php
  2. class SessionRegistry{
  3. private static $instance;
  4.  
  5. private function __construct(){
  6.  
  7. }
  8.  
  9. static function instance(){
  10. if(!isset(static::$instance)){
  11. static::$instance = new self();
  12. }
  13.  
  14. return static::$instance;
  15. }
  16.  
  17. protected function get($name){
  18. if(!isset($_SESSION[__CLASS__][$name])){
  19. return NULL;
  20. }
  21.  
  22. return $_SESSION[__CLASS__][$name];
  23. }
  24.  
  25. protected function set($name, $val){
  26. $_SESSION[__CLASS__][$name] = $val;
  27. }
  28.  
  29. protected function unregister($name){
  30. unset($_SESSION[__CLASS__][$name]);
  31. }
  32.  
  33.  
  34. function SetManager(Manager $upload){
  35. $this->set('manager', $upload);
  36. }
  37.  
  38. function GetManager(){
  39. return $this->get('manager');
  40. }
  41.  
  42. function UnregisterManager(){
  43. $this->unregister('manager');
  44. }
  45.  
  46. function ManagerScreen(){
  47. return print_r($_SESSION[__CLASS__]['manager']);
  48. }
  49.  
  50. }
  51.  




Manager.php
  1. <?php
  2. class Manager{
  3. private $new = array();
  4. private $good = array();
  5. private $bad = array();
  6.  
  7.  
  8. function __construct(){
  9.  
  10. }
  11.  
  12. function hireWorkers($num=15){
  13. $num = (int)$num;
  14. for($i=0; $i<$num; $i++){
  15. $this->new[] = new Worker();
  16. }
  17. }
  18.  
  19.  
  20. function SaveToSession(){
  21. $Sess_reg = SessionRegistry::instance();
  22. $Sess_reg->SetManager($this);
  23. }
  24.  
  25. static function LoadFromSession(){
  26. $Sess_reg = SessionRegistry::instance();
  27. return $Sess_reg->GetManager();
  28. }
  29.  
  30.  
  31. static function SessionScreen(){
  32. $Sess_reg = SessionRegistry::instance();
  33. $Sess_reg->ManagerScreen();
  34. }
  35.  
  36.  
  37. function filterWorkers(){
  38. foreach($this->new as $worker){
  39. if(rand(0, 1) == 1){
  40. $this->good[] = $worker;
  41. }else{
  42. $this->bad[] = $worker;
  43. }
  44. }
  45.  
  46. $this->new = array();
  47. }
  48.  
  49. function undoFilter(){
  50. $this->new = array_merge($this->good, $this->bad);
  51. $this->good = array();
  52. $this->bad = array();
  53. }
  54.  
  55. function reset(){
  56. $this->new = array();
  57. $this->good = array();
  58. $this->bad = array();
  59. }
  60.  
  61. }
  62.  
  63.  
  64. class Worker{
  65. private $id;
  66.  
  67. function __construct(){
  68. $this->id = uniqid();
  69. }
  70. }




set.php
  1. <?php
  2. require_once 'loader.php';
  3.  
  4.  
  5. $Manager = new Manager();
  6.  
  7. $Manager->hireWorkers();
  8. $Manager->SaveToSession();
  9.  
  10. ?>




change.php
  1. <?php
  2. require_once 'loader.php';
  3.  
  4.  
  5.  
  6. $Manager = Manager::LoadFromSession();
  7.  
  8. $Manager->filterWorkers();
  9. //$Manager->undoFilter();
  10. ?>




show.php
  1. <pre>
  2. <?php
  3. require_once 'loader.php';
  4.  
  5.  
  6. print_r(Manager::SessionScreen());
  7.  
  8.  
  9. ?>
  10. </pre>




loader.php
  1. <?php
  2. function __autoload($class){
  3. require_once "{$class}.php";
  4. }
  5.  
  6. ?>






Przepływ zdarzeń:
1. Uruchamiam set.php
2. Uruchamiam show.php
3. Wynik print_r:
Kod
Manager Object
(
    [new:Manager:private] => Array
        (
            [0] => Worker Object
                (
                    [id:Worker:private] => 509b99e26056c
                )

            [1] => Worker Object
                (
                    [id:Worker:private] => 509b99e26094d
                )

            [2] => Worker Object
                (
                    [id:Worker:private] => 509b99e260d33
                )

            [3] => Worker Object
                (
                    [id:Worker:private] => 509b99e261148
                )

            [4] => Worker Object
                (
                    [id:Worker:private] => 509b99e261518
                )

        )

    [good:Manager:private] => Array
        (
        )

    [bad:Manager:private] => Array
        (
        )

)

Jak widać jest wszystko co powinno być. Manager utworzył instancje Workerów, i zapisał się do sesji - wynikiem jest uzupełniona tablica new.

4. Uruchamiam change.php (zwróćcie uwagę, że nie ma tam wywołania SaveToSession)
5. Ponownie uruchamiam show.php i tym razem na ekranie pojawia się coś takiego:
Kod
Manager Object
(
    [new:Manager:private] => Array
        (
        )

    [good:Manager:private] => Array
        (
            [0] => Worker Object
                (
                    [id:Worker:private] => 509b99e26094d
                )

            [1] => Worker Object
                (
                    [id:Worker:private] => 509b99e260d33
                )

            [2] => Worker Object
                (
                    [id:Worker:private] => 509b99e261d0e
                )

        )

    [bad:Manager:private] => Array
        (
            [0] => Worker Object
                (
                    [id:Worker:private] => 509b99e26056c
                )

            [1] => Worker Object
                (
                    [id:Worker:private] => 509b99e261148
                )

        )

)


Plik change.php zmienił obiekt, 'nie mówiąc nic o tym sesji', ale sesja 'sama zaktualizowała' jego stan... Czy nie powinno być przypadkiem tak, że sesja powinna utrzymywać 'pierwotny' stan obiektu? Bardzo proszę, w miarę możliwości, wytłumaczyć co się dzieje 'pod maską' tego przykładu.


Z góry serdecznie dziękuję,
Maciek
nospor
Obiekty przekazywane są przez referencje. Czyli jak ty z sesji pobierasz obiekt to tak naprawdę pobierasz jego referencję i wszelkie zmiany jakie robisz na tym obiekcie dotyczą zmian u źródła, czyli w tym przypadku w obiekcie zapisanym w sesji smile.gif

Jeśli chcesz pobrać klon obiektu, to musisz go poprostu sklonować przy użyciu CLONE

ps: a na przyszłość nie generuj aż 15 próbek by nam coś pokazać. 3 wystarczą - nie trzeba wówczas pół godziny scrollować posta... wink.gif
macq
Dzięki nospor za info!

Właśnie rzuciłem okiem do pliku sesji po stronie serwera i w sumie po jego zawartości można było wydedukować sposób zachowania tego mechanizmu... ale to już wpadło mi do głowy dopiero po Twoim poście wink.gif


Jeszcze raz wielkie dzięki wink.gif

PS. Co do próbek - sorry, już tego błędu nie powtórzę wink.gif Troszkę się już nawet zreflektowałem!

Pozdrawiam serdecznie,
Maciek

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.