Re: [PHP4BETA] serialize() musings...
| From: | Kristian Köhntopp | Date: | Mon, 12 Apr 1999 08:12:29 +0000 |
| Subject: | Re: [PHP4BETA] serialize() musings... | ||
| References: | 1 | Groups: | php.version4 |
| Request: | Send a blank email to php-version4+get-81@lists.php.net to get a copy of this message | ||
Zeev Suraski schrieb:
> That said, I think you have a fundemental mistake in
> your entire approach to serialzation. Objects *never*
> serialize their methods. In PHP4, objects don't even
> really have methods, they belong to a class. When you
> want to serialize an object, you serialize its stateful
> data only, not its methods.
We both agree on this. I do not serialize an objects methods, but
only the stateful data of an object. I ask that you see for
yourself by following me through the example below, because it
makes further discussion much easier.
Please have a look at the actual saved state of a session,
because I will use it as an example.
kk@poe ~ $ ./sesdump.pl
name=M_Session
sid=488f19acdf4867a623c947ed10a09353
changed=19990322111358
val = '$this->in = "";
$this->pt = array();
$this->pt["lang"] = "1";
$this->pt["beenthere"] = "1";
$this->pt["cart"] = "1";
$GLOBALS["lang"] = "en";
$GLOBALS["beenthere"] = array();
$GLOBALS["beenthere"]["VLB"] = "yes";
$GLOBALS["beenthere"]["KNO"] = "yes";
$GLOBALS["beenthere"]["VLZ"] = "yes";
$GLOBALS["cart"] = new M_Cart;
$GLOBALS["cart"]->item = array();
$GLOBALS["cart"]->item["1"] = array();
$GLOBALS["cart"]->item["1"]["art"] = "163-1468";
$GLOBALS["cart"]->item["1"]["num"] = "1";
$GLOBALS["cart"]->currentItem = "2";
This is a dump of a session named "M_Session" with a Session id
of "488...353". It has been updated at 22-Mar-1999 for the last
time (this is a development server which does not expire old
sessions). The val-string is the actual saved state of the
session and it is saved in the form of an executeable PHP
program. This is only because I am a lazy programmer and did not
want to write a parser for my session state when I already have a
perfectly working parser at hand (PHP's eval() statement).
The session contains a saved scalar $lang, a saved array
$beenthere[] and a saved object $cart of the class M_Cart. The
saved object is complex: It contains a multidimensional array
that is saved with it. I chose the example, because it contains
most of the cases you can encounter besides references (which
weren't available until now).
The session state saves the following things ($this in the
context of the session state means the session object itself.
$this-variables in a saved session refer to internal variable of
the session):
- $this->in
A flag which indicates if the session has one-shot startup code
to execute and if that code already has run. If a session has
startup code, we include() that code, run it and set $this->in to
"1" so that we remember that we are already initialized and do
not run that code again.
- $this->pt[]
A hash that contains the names of all global symbols that are to
be serialized. If you register a variable with the session
management, its name is stored in $this->[] and it is serialized
and written away each time a page closes. We also serialize
$this->pt[], to that the registered names are persistent
themselves.
- $GLOBALS[]
This are the actual saved variables. Note that we take care to
reconstruct the proper type for each variable. We generate
$GLOBALS["beenthere"] = array(); even for an empty array
$beenthere[], because otherwise we get problems with restoring
such cases.
That's all that is saved in one of PHPLIBs sessions.
So much for the general background, now to your comments:
We do serialize object stateful data, but no methods, just like
you said we should. Saving methods would be quite pointless,
because we already have the objects methods in a serialized form
that can be easily loaded: That is the include file in which the
class was defined in the first place (we have the convention of
defining each class and only that class in a seperate include
file, but we do not depend on that convention - see below).
We do not serialize all stateful data in an object in some cases,
because that would be not only pointless, but dangerous as well.
An example is the session object itself, as shown in the example
above: The session object has many variables, many of which are
constants. These are being set by PHPLIB users to configure the
library. There is no point in saving these, because the values
never change.
Also, the session object contains a database connection object,
which in turn contains a variable $Link_ID, which is the result
of a mysql_pconnect() call. We must not save that value, because
it is only a handle for an opaque data structure outside the
scope of PHP (the socket to the database server) and that
structure cannot be serialized - since the connection is lost at
the end of the page, the $Link_ID must be lost, too, so that we
can discover that we have no database link, yet, and must
recreate it. Thus the need for NOT serialzing certain state in
some cases.
As for serializing objects: The current implementation of the
serialize() builtin in PHP3 does save all object state, but it
does not retain the class-object connection. An unserialized()
object is of no class at all - I cannot invoke any methods on
such an object.
A proper implementation of serialize() and unserialize() in PHP4
or Zend must retain the class of an object, i.e. it must do the
semantic equivalent of the "$GLOBALS["cart"] = new M_Cart;"
statement in the example above. As of now, the
serialize()/unserialize() implementation in PHP3 is useless for
PHPLIB because (in order of severity):
- unserialized() objects do not retain their class
- serialize() always saves the complete object state
That is why PHPLIB comes with an independent serialize() method
instead of using the PHP3 builtin.
A final note on "serializing methods of classes":
Currently, PHPLIB requires that you always include all class
definitions of objects that might be serialized at any time by
your application. PHPLIB needs to be able to execute statements
like '$GLOBALS["cart"] = new M_Cart;' when the need arises.
There is no way to include only the needed files (i.e. to NOT
include 'cart.inc' unless there is a $cart object within the
current session state), because the unserializing function
(Session::thaw() in PHPLIB) is just that: a function. You cannot
define a global class (by executing 'include("cart.inc")') from
within a function context currently.
This is not a problem, but it would be a nice optimization for
PHPLIB. PHPLIB would then optionally generate an appropriate
include() statement in the session state for each saved object.
A final note on the format of the saved session:
Currently, we generate arrays and object by executing a string of
statements creating the individual slots of the arrays and
objects one at a time. We experimented with a syntax like
$GLOBALS["beenthere"] = array(
"VLB" => "yes,
"KNO" => "yes",
"VLZ" => "yes"
);
but decided against it, because PHP3 does have a syntax for array
constants, but no syntax for object constants, i.e. there is no
construct like
$GLOBALS["cart"] = object("M_Cart",
array(
1 => array(
"art" => "163-1468",
"num" => "1"
)
),
currentItem = 2
);
This is a kind of asymmetry in the language: We have constant
notations for all scalar types and for arrays, but not for
objects.
Again, this only an observation: The syntax we currently use is
in fact easier to generate and maintain that the nested syntax
above.
Kristian
--
Kristian Köhntopp, NetUSE Kommunikationstechnologie GmbH
Siemenswall, D-24107 Kiel, Germany, +49 431 386 436 00
Using PHP3? See our web development library at
http://phplib.shonline.de/ (GPL)
--
PHP 4 Beta