| 
        php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login | 
  [2006-11-04 20:59 UTC] cw264701 at ohiou dot edu
 Description: ------------ PHP assumes that I will not use an object after serializing it. This shouldn't cause problems if my object's class does not define a __sleep() function, but if it does, and that __sleep() function modifies the object, then I can't reliably use that object until it is recreated using unserialize(). There is no mention of this in the documentation for the serialize() function, or anywhere else that I saw. More importantly, if PHP expects me to *not* use an object after calling serialize() on it, then PHP should produce an error message if I *do* try to use that object before unserialization. This is one of several problems (not all necessarily "bugs", but shaky designs), that I've come across recently, which greatly reduces the ability for PHP applications to take advantage of *transparency*. I.e., I should not have to care how a class is implemented (for instance, whether or not it uses the magic __sleep() function) to make use of it. I recently adopted the ezpdo (http://ezpdo.net/) ORM tool. It has probably hurt my productivity more than it has helped because it makes use of such leaky abstractions. Some of these may be the fault of that tool, but many flaws like this seem to be more general PHP problems. (Sorry for the rant, but I think issues like this are pretty important, and the reason I very often become frustrated with PHP.) Reproduce code: --------------- <?php class MultiplicationTable { public $size; public $table; public function MultiplicationTable( $size ) { $this->size = $size; for( $a = 1; $a <= $size; ++$a ) { for( $b = 1; $b <= $size; ++$b ) { $this->table[$a][$b] = $a * $b; } } } public function __sleep() { $this->table = null; return( array("size") ); } public function __wakeup() { $this->MultiplicationTable($this->size); } } $mt = new MultiplicationTable(4); echo $mt->size . ", " . $mt->table[4][4] . "\n"; $serialized_mt = serialize($mt); echo $mt->size . ", " . $mt->table[4][4] . "\n"; $unserialized_mt = unserialize($serialized_mt); echo $unserialized_mt->size . ", " . $unserialized_mt->table[4][4] . "\n"; ?> Expected result: ---------------- Well, ideally the object would still "work" after creating a serialize()'d version of it, but I think making that work would require significant changes to PHP's whole serialization model (or perhaps you could just have __wakeup() be called right after serialization; perhaps only if the object is accessed again). But, the more realistic solution would probably result in some kind of error message when I try to access my $mt object after calling serialize() on it. Actual result: -------------- 4, 16 4, 4, 16 PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits             
             | 
    |||||||||||||||||||||||||||
            
                 
                Copyright © 2001-2025 The PHP GroupAll rights reserved.  | 
        Last updated: Tue Nov 04 04:00:01 2025 UTC | 
Yes, I understand that I need not set the "table" attribute to null. This was a bad example; sorry. My complaint isn't about a specific case of using serialize() and the magic __sleep() function. I am complaining that the whole concept is flawed. The PHP documentation is encouraging users to define this __sleep() function and, thus, modify their objects before they are serialized. This is kind of silly, because, when we serialize() an object, that exact object is not being serialized, *itself*, but a *serialized representation* of that object is being formed (and stored as a string). The intent of the __sleep() function is good: to allow some control over what is actually stored for a serialized version of an object. The problem is, it can have *side effects*. This really becomes a problem when you are working with a class that you didn't write. For example, I am using ezpdo. I carelessly (which should be okay in this case) serialize()'d one of my ezpdo-mapped objects before I was finished using it; things blew up. It shouldn't be "wrong" for me to do something like this: $_SESSION['purchase'] = serialize($myPurchaseObject); $smarty->assign('purchase', $myPurchaseObject); There, I attempt to store away my object in the session, just before I pass it off to my template engine for view rendering. Perhaps this technique would be considered bad practice (to a PHP guru/developer), but that shouldn't leave me in the dark with some broken code. When I use a class, I want to program to its *interface*, not an implementation; I shouldn't have to care whether or not it happens to define a __sleep() function, and therefore cannot be used after it has been passed to a call to serialize(). I understand the value of __sleep(), but I think the whole serialization library/interface needs re-thinking. I suggest one of three solutions: - When an object, O, is serialize()'d, PHP could create an exact copy of that object (a "shallow copy", I believe), let's say C. PHP would then call __sleep() for the new object, C, and serialize that instance of the class. This technique seems slightly risky, though, because we have to depend on the class' __sleep() function to not directly modify any of its referenced objects, but rather rely on those references to use their own __sleep() functions to do any cleaning up; the class shouldn't directly modify its references, but who knows... - Call an object's __wakeup() method after a serialized representation has been formed but before returning from the serialize() function. This method might be wasteful in many cases (where the object was never again used after serialization), but better safe than sorry. Perhaps the __wakeup() function could be called on the object only if an attempt is made to "use it" after the call to serialize(). - The quick-n-dirty solution would be to, simply, cough up an error message if an object is referenced *after* a call to serialize() has been made on it - *regardless of whether the object has an associated __sleep() method*.