Balduran, here is the solution to your problem:
http://us.php.net/manual/en/language.oop5.magic.php#81492
and here is the working code:
class A {
public $a1 = 'a1';
protected $a2 = 'a2';
private $a3 = 'a3';
public function __sleep() {
return array("a1", "\0*\0a2", "\0A\0a3");
}
}
class B extends A {
public $b1 = 'b1';
public $b2 = 'b2';
public $b3 = 'b3';
public function __sleep() {
return array_merge(array("b1", "b2", "b3"), parent::__sleep());
}
}
print_r(unserialize(serialize(new B())));
객체 일렬화 - 세션 내의 객체
Note: PHP 3에서 객체는 일렬화(serialization)와 비일렬화(unserialization)처리 과정에서 클래스의 구성요소를 잃게 된다. 처리후 이 변수는 더이상 객체 타입 이 아니다. 그래서 클래스와 메소드를 갖지 않는다. 결국은 거의 쓸모 없게 되는것이다 (웃기는 문법을 갖는 배열처럼 바뀌게 된다)
다음 설명은 PHP 4로만 한정되는 내용이다.
serialize()함수는 PHP에 저장할수 있는 값은 뭐든지 바이트-스트림(byte-stream) 대표성을 내포하는 문자열로 돌려준다. unserialize()함수는 이런 문자열을 원래 변수값으로 되돌려준다. serialize를 사용하여 객체를 저장하면 객체 내의 모든 변수 를 저장할 수 있다. 객체 내의 함수들을 저장할 수는 없고, 오직 클래스의 이름만 저장될것이다.
객체를 unserialize()하기 위해서는, 그 객체의 클래스가 선언되어 있어야 한다. 즉, page1.php에서 클래스 A의 객체 $a 가 있고 이것을 일렬화하면, 클래스 A를 참조하는 문자열을 얻게 되고, 이 문자열은 $a에 속해있는 모든 변수값을 포함하게 될것이다. Page2.php에서 이 문자열을 비일렬화하려면, 클래스 A의 $a를 재생성하고, 클래스 A의 선언이 page2.php에 존재해야 한다. 예를 들면 인클루드 파일 안에 클래스 A의 클래스 선언부를 저장하고 page1.php와 page2.php 에서 이 파일을 인클루드 하면 된다.
<?php
// classa.inc:
class A
{
var $one = 1;
function show_one()
{
echo $this->one;
}
}
// page1.php:
include("classa.inc");
$a = new A;
$s = serialize($a);
// store $s somewhere where page2.php can find it.
$fp = fopen("store", "w");
fputs($fp, $s);
fclose($fp);
// page2.php:
// this is needed for the unserialize to work properly.
include("classa.inc");
$s = implode("", @file("store"));
$a = unserialize($s);
// now use the function show_one() of the $a object.
$a->show_one();
?>
세션을 사용해서 객체를 등록하기 위해 session_register()함수를 사용한다면, 이 객체들은 각 PHP 페이지의 끝에서 자동으로 일렬화되고, 다음 페이지들 각각에서 자동으로 비일렬화된다. 이 객체들이 일단 세션의 일부분이 되면 페이지의 어느 부분에서도 이런 객체가 나타날 수 있다는것을 의미한다.
모든 페이지에서 실제로 이런 클래스를 사용하지 않더라도, 모든 페이지에 등록된 모든 객체의 클래스 선언을 인클루드하도록 한다. 이렇게 하지 않고, 존재하는 클래스 선언부없이 객체가 일렬화되면, 그 객체는 클래스 구성원을 잃게 될것이고 어떤 함수도 갖지 않는 stdClass클래스의 객체가 될것이다. 즉, 그 객체는 쓸모없게 될것이다.
그래서 위 예제코드에서 $a가 session_register("a") 수행하여 세션의 일부분이 되면, page1.php와 page2.php뿐만 아니라 사용중인 모든 페이지에 classa.inc파일을 인클루드해야 한다.
객체 일렬화 - 세션 내의 객체
07-Mar-2008 08:39
02-Jan-2008 01:36
This is an interesting thing with classes when B extends A, then private variable of class A will not be serialized when using magic method __sleep(). Without __sleep() it works fine.
class A {
public $a1 = 'a1';
protected $a2 = 'a2';
private $a3 = 'a3';
public function __sleep() {
// option 1: wont work, says:
// Notice: serialize() [function.serialize]: "a3" returned as member variable from __sleep() but does not exist in **/index.php on line **
return $this;
// option 2: wont work, says:
// Notice: serialize() [function.serialize]: "a3" returned as member variable from __sleep() but does not exist in **/index.php on line **
$keys = array();
foreach( $this as $key => $value)
$keys[] = $key;
return $vals;
}
}
class B extends A {
public $b1 = 'b1';
public $b2 = 'b2';
public $b3 = 'b3';
}
print_r(unserialize(serialize(new B())));
Not sure if this is bug or feature.
18-Dec-2007 02:02
It's worth noting that when you serialize an object (after creating it normally), its __sleep method will be called, and then when the object naturally goes out of scope, it's __destruct method will also be called.
However, when you unserialize an object, its __wakeup method will be called, but it's __construct method will not be called.
I'm not quite sure if there's a way to force an object completely out of scope without having __destruct automatically be called. If there is though (and it's neat - not like invoking 'die' at the end of every page), I'd like to hear about it!
03-Sep-2007 06:25
If you use __autoload(), this is also used when unserialize() hits a serialized class. So no need to include() all files everywhere...
19-Dec-2005 04:35
If you have register_globals = on, then be very careful about naming conventions when it comes to sessions and objects.
Example:
<?php
/* Setting the session variable, which becomes "$order" if register_globals is on */
session_start()
$_SESSION["order"] = "SomeValue";
/* Instantiating an object with the same name as the session variable */
$order = new Classname;
?>
This will activate the __PHP_Incomplete_class object within your session variable! This, all references and methods to $order will not work.
Took me hours to figure this out, so I hope this is helpful.
13-Aug-2005 09:06
never serialize an object, if it contains references to itself. Just create the object in an array, or make a reference of this object in an array before calling serialize() and serialize the array instead of the object.
print_r is fine to validate a reference: The reference will be serialized in a right way, if print_r returns a *recursive* on right place (look carefully).
Just have a look on this small example:
<?
class foo {
var $bar;
function addBar() {
$this->bar = new bar(&$this);
}
}
class bar {
var $parentFoo;
function bar(&$partentBean) {
$this->parentFoo =& $partentBean;
}
}
$foo = new foo();
$foo->addBar();
$fooArray = array(&$foo);
echo "<pre>";
print_r($foo);
echo serialize($foo);
echo "</pre>";
echo "<pre>";
print_r($fooArray);
echo serialize($fooArray);
echo "</pre>";
?>
OutPut:
foo Object
(
[bar] => bar Object
(
[parentFoo] => foo Object
(
[bar] => *RECURSION*
)
)
)
O:3:"foo":1:{s:3:"bar";O:3:"bar":1:{s:9:"parentFoo";O:3:"foo":1:
{s:3:"bar";O:3:"bar":1:{s:9:"parentFoo";R:3;}}}} //sorry for linebreak
Array
(
[0] => foo Object
(
[bar] => bar Object
(
[parentFoo] => *RECURSION*
)
)
)
a:1:{i:0;O:3:"foo":1:{s:3:"bar";O:3:"bar":1:{s:9:"parentFoo";R:2;}}}
if you serialize the object, the the refernce becomes a copy an first the reference inside the copy is a real reference, (but just only to the copy, for sure :).
25-May-2005 09:31
I can't store an instance of MySQLPagedResultSet (I grabbed this code from Kevin Yank at Sitepoint) in my session without losing all the data. This seems related to the previous post since the result set member variable is from calling mysql_query(). It doesn't get serialized properly and when I retrieve it from the session the result set is empty.
My workaround is store the query (ie. select * from mydata where id in (1,2,3...)) instead of the result set. In this particular case, it works beautifully and yields a big performance boost, since the id's are generated from several other queries that can now be avoided. With query caching on in mysql, I would not be surprised if this is just as fast or faster than retrieving the result set from the session. There are fewer potential data synchronization problems, too, since the info from mydata is fresh.
21-Apr-2005 05:51
Was wondering, if References are also lost after serializing and deserializing in php5.
Wrote a testfile and my result is:
All references are restored corretly. Even if the classes where recursively referenced.
Here is the code of the file:
<?
//check refereneces
class A
{
public $a_var;
/**
* @var B
*/
public $parent;
public function __construct($parent)
{
$this->parent=$parent;
}
}
class B
{
/**
* @var A
*/
public $Ref_auf_A;
/**
* @var A
*/
public $aObj;
public $name="";
public function __construct($InhaltA)
{
$this->aObj=new A($this);
$this->aObj->a_var=$InhaltA;
$this->Ref_auf_A=$this->aObj;
}
}
$B_obj = new B("Inhalt von A");
$B_obj->name="My is <defaultName>";
echo "Object B before serializing:<pre>";
print_r($B_obj);
echo "</pre><br><br>";
$serB=serialize($B_obj);
echo "Object B serialized: ".$serB."<br><br>";
$B_obj = unserialize($serB);
echo "Object B deserializedt:<pre>";
print_r($B_obj);
echo "</pre><br><br>";
$B_obj->Ref_auf_A->a_var="Test";
echo "Object A reference content changed, should have changed everywhere:<pre>";
print_r($B_obj);
echo "</pre><br><br>";
//Change name of object where A parent references to.
$B_obj->Ref_auf_A->parent->name="My name changed";
echo $B_obj->name;
?>
19-Mar-2005 05:43
It seems that serializing an object dereferences all its attributes that were references - even if the object they referenced was contained within the serialized one.
Consider this example:
<?
class foo {
var $a=new array();
}
$example=new foo;
$obj=new whatever();
$example->a[]=&$obj;
$example->a[]=$obj;
//now $example contains a list with
//both: an object and a reference to that object
//let us serialize and immediately unserialize $example:
$string=serialize(example);
$example=unserialize(string);
//now the situation is completely different!
//$example contains just two identical objects,
//the reference is lost
?>
03-Jun-2004 03:45
A special gotcha for unserializing stored objects. If you alter the class (adding new methods), they will appear and be available when the object is unserialized. If, however, you alter the parent class that the class extends, the methods are *not* available, and any renamed ones silently vanish (the method disappears and does not reappear under the new name).
A way to get around this is to create a new dummy method in the child class and rename it each time you alter the parent class. This is very much a Bad Solution when you have to deal with a base class that is extended by dozens of other classes. Especially when you have a database full of serialized objects, each of which is a child of the base class. There appears to be no runtime fix.
Applies to PHP 4. Not sure about 5.
05-May-2004 04:32
When working with a installation where Register Globals if off. You must remeber to use $_SESSION['objectName'] to store objects that will be used on multiple pages.
To re-use a saved object, you must define your object (i.e. include it's class file) before you call session_start(), or the object becomes unusable.
21-Nov-2003 01:00
Good news when serializing a class and then reading it back into the similar or modified class.
PHP behaves very well when a class was serialized and then it is unserialized to initialize similar class or when the class was modified meanwhile.
Try this sample:
First version of the class A contains members $one and $three. An instance of this class is stored into the file.
The second version of the class A contains members $one as in prevoius but it also contains $two and and it doesn't contain $three member. The file is read back.
Resulting class is still working !
<?php
/////////////////////////////
// classa.inc:
class A
{
var $one = 1;
var $three = 3;
function show_one(){ echo $this->one."<br>";}
}
/////////////////////////////
// classa_modified.inc:
class A
{
var $two = 2;
var $one = 1;
function show_one(){ echo $this->one."<br>"; }
function show_two(){ echo $this->two."<br>"; }
}
/////////////////////////////
// teststore
include("classa.inc");
$a = new A;
$s = serialize($a);
// store $s somewhere where page2.php can find it.
$fp = fopen("savedclass.tmp", "w");
fputs($fp, $s);
fclose($fp);
$a->show_one();
var_dump($a);
/////////////////////////////
// testreadmodified.php:
include("classa_modified.inc");
$s = implode("", @file("savedclass.tmp"));
$a = unserialize($s);
// now use the function show_one() of the $a object.
$a->show_one();
$a->show_two();
var_dump($a);
?>
Resulting Output:
1
2
object(a)(3) { ["two"]=> int(2) ["one"]=> int(1) ["three"]=> int(3) } ?>
Note:
- $one member is unserialized normally
- $two member exist after unserialisation and it's well initialised with value 2
- show_two() function is working after unserialization
- $thee member exist was loaded too even if it isn't declared in modified version of the class !
04-Mar-2003 05:21
I had a problem using session_register() to globally store a class that I was working with. I hope my solution will help beginners understand this concept of sessions in PHP
When you use session_register() to store any object, yes you don't need to call session_start() unless you want to. However in other pages where you want to use that object, you must call session_start() after you have included the class definition of the file. Failure to do this will unserlize your object without its functions and it will be pretty useless.
Hope it helps someone.
Cheers.
15-Nov-2002 10:10
Serialization is very useful for the storing of objects in databases.
The following example stores an obbect in a mySQL db:
# Seperate Class.
include 'thisObjectClass.php';
# Create instance of a class.
$object = new thisObjectClass;
# Create link to the database
$link = mysql_connect('host', 'user', 'password');
mysql_select_db('dbname', $link);
# Formulate query and submit.
$sql_query = mysql_query("INSERT INTO objecttable(object) VALUES (serialize($object)") or print (mysql_error());
# Retrieve blob object.
$sql_query = mysql_query("SELECT object FROM objecttable WHERE 1) or print (mysql_error());
$row = mysql_fetch_row($sql_query);
# This should show string / text data.
print $row[0];
# This should show 'object'.
print unserialize($row[0])."<br>";
# Close db link.
mysql_close($link);
14-Nov-2002 09:07
Don't forget to lock your storage file using flock() before opening the file you wish to use to store your serialized object(s)! PHP will make no attempt to prevent two threads or processes from writing to your file at the same time and you can easily end up with a corrupted data store unless you serialize access to your storage. (Note that using NFS for your storage may not be lock-safe.)
24-Aug-2002 11:26
A quick note about objects containing another object as a variable. Say you have something like this:
class A {
var $A_Var;
}
class B {
var $A
var $B_Var;
function B() {
$this->A = new A();
}
}
$B = new B();
$B->A->A_Var = "A_Var value";
---
When you try to serialize() an instance of B() $B->A->A_Var doesn't seem to keep it's value.
After much fighting with this the solution I found was to append to my __sleep and __wakeup functions and serialize $this->A in __sleep and then unserialize it in __wakeup.
class A {
var $A_Var;
}
class B {
var $A
var $A_serialized;
var $B_Var;
function B() {
$this->A = new A();
}
function __sleep() {
$this->A_serialized = serialize($this->A);
return array('A_serialized', 'B_Var');
}
function __wakeup() {
$this->A = unserialize($this->A_serialized);
}
}
08-Jul-2002 12:31
Got around the session_auto_start problem by using:
session_write_close();
class defination
session_start();
works for me
08-Jul-2002 12:57
The deserialize callback function is pretty useless if you name your classes and files using title case (ie ClassName.php), because PHP changes all classnames to lower case!!!
26-May-2002 05:13
In case this isn't clear:
In 4.2 and below: session.auto_start and session objects are mutually exclusive.
17-Apr-2002 01:52
The problem with objects during session management arise because the object definition must exist before starting the session. i.e. if you have object x and you want to use it in a session, you have to perform the following.
<?
class x
{
...definition here
}
// php now knows what x is... start the session
session_start();
session_register("x_as_variable");
?>
if object x is defined in another file, make sure that the file is included or required BEFORE you start the session.
<?
require("x.inc");
session_start();
session_register("x_as_variable");
?>
This stops the INCOMPLETE_OBJECT response eliminating the need to create an array representation for an object.
17-Mar-2002 07:00
PHP.INI FILE AND SESSION.AUTO_START
Set
session.auto_start = 0
in php.ini file if you want that all works well during serialization/unserialization.
If session.auto_start is set to 1 all class definition is loaded after unserialization process, infact you obtain a messagge error like this "The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition className of the object you are trying to operate on was loaded _before_ the session was started in scriptFile on line n"
20-Feb-2002 01:15
A quick note about automatic serialisation in sessions.
If you have an object
$myCart = new ShoppingCart();
and this object contains variables which are objects
function ShoppingCart()
{
$mySingleItem = new BasketItem();
}
this will work fine and you can access the contained object $mySingleItem and its methods etc ONLY WHILE YOU ARE ON THE PAGE WHERE YOU CREATED IT.
if you are using sessions and register the cart
session_register('myCart')
the ShoppingCart object is serialised and unserialised on the next page ok but the inner BasketItem object is just garbled.
The only solution I have found is to use an array in the ShoppingCart instead of an object. What a pain!
22-Sep-2001 05:03
When storing an object that was generated with:
$row = mysql_fetch_object($result)
there is no apearent way to define the class of that object in the next page, without querying the database, and fetching a row as an object. This needs to be done so any such objects can be restored (deserialized) from a PHP4 Session.
It needs to be done before session_start() or session_register().
