객체 직렬화 — 세션 속의 객체

객체 직렬화 — 세션 속의 객체

serialize()는 PHP에 저장할 수 있는 모든 값을 바이트 스트림(문자열) 형태로 바꾼 문자열을 돌려줘요. 그리고 unserialize()는 이 문자열을 다시 원래의 변수 값으로 복원해 줍니다. serialize로 객체를 저장하면 그 객체 안의 모든 변수는 그대로 저장되고, 객체의 메서드는 저장되지 않아요. 대신 그 객체가 어떤 클래스인지, 클래스 이름만 함께 저장됩니다.

unserialize()로 객체를 되살리려면 그 객체의 클래스가 정의되어 있어야 해요. 예를 들어 A 클래스의 객체를 serialize하면, 결과 문자열에는 A 클래스를 가리키는 이름과 함께 그 안에 들어 있는 모든 변수 값이 담깁니다. 이 객체를 다른 파일에서 unserialize하려면, 그 파일에 A 클래스의 정의가 먼저 있어야 하죠. 이렇게 하려면 예를 들어 A.php의 클래스 정의를 include 파일로 만들어 두고 그 파일을 포함시키거나, spl_autoload_register() 함수를 이용하면 됩니다.

<?php
// A.php:
  class A {
      public $one = 1;
      public function show_one() {
          echo $this->one;
      }
  }
// page1.php:
  include "A.php";
  $a = new A;
  $s = serialize($a);
  // store $s somewhere where page2.php can find it.
  file_put_contents('store', $s);

// page2.php:
  // this is needed for the unserialize to work properly.
  include "A.php";
  $s = file_get_contents('store');
  $a = unserialize($s);
  // now use the function show_one() of the $a object.
  $a->show_one();
?>

page1.php에서 $a를 직렬화해 파일에 저장하고, page2.php에서 다시 읽어 되살린 뒤 show_one()을 호출하고 있어요. 두 파일 모두 A.php를 먼저 include해서 클래스 정의를 준비해 두는 게 핵심이에요.

애플리케이션이 나중에 쓸 목적으로 객체를 직렬화한다면, 앱 전체에서 그 객체의 클래스 정의를 포함하도록 하는 걸 강력히 권장합니다. 그러지 않으면 클래스 정의 없이 객체가 역직렬화될 수 있는데, 그렇게 되면 PHP는 그 객체에 __PHP_Incomplete_Class_Name이라는 클래스를 부여해요. 이 클래스에는 메서드가 전혀 없어서, 결국 그 객체는 쓸모없게 돼버립니다.

위 예시에서 $a$_SESSION 슈퍼글로벌 배열에 새 키를 추가해 세션의 일부가 된다면, page1.phppage2.php뿐 아니라 모든 페이지에서 A.php를 include해야 합니다.

이 방법 말고도, 객체의 직렬화·역직렬화 시점에 끼어들 수 있는 훅이 있어요. 바로 __sleep()__wakeup() 메서드를 이용하는 거예요. __sleep()을 쓰면 객체의 일부 프로퍼티만 골라서 직렬화할 수도 있습니다.