PHP serialization optimization

I recently had to use the serialize() function to store objects in Memcache.

However, I realized that a lot of these objects (Propel objects precisely) were unnecessarily huge when stored: they had a lot of properties with quite long names having their default class value.

That’s when I realized I could use the __sleep() function to store only what I wanted. I gave up on using this function before as it would have been to hard to maintain it …unless you use some meta-programming features of PHP to store only the variables not having their default value! That’s what I did, and the results are a faster unserialize(), less memory usage, and no maintenance whatsoever.

Here it is:

class Util
{
  public static function cmp($a, $b)
  {
 
    return $a === $b ? 0 : 1;
  }
}
 
class Penguin
{
  function __sleep()
  {
    // Get the name of the changed properties
    $vars = array_keys(
              array_udiff_assoc(
                get_object_vars($this),
                get_class_vars(get_class($this)),
                array('Util', 'cmp')
              )
            );
 
    // Optional part: call the parent's __sleep() function if there is one
    if (method_exists(get_parent_class($this), '__sleep'))
    {
      $vars = array_merge($vars, parent::__sleep());
    }
 
    return $vars;
  }
}

If you use unset() on a default variable, my function will not detect it. Doing this would be a very bad idea anyway.

If your final object extends an object that has private properties, you will need to add my __sleep() function to it too. The good news is that Propel objects only have protected properties.

Update: Unfortunately the $_new attribute of Propel’s BaseObject is private, and set to true by default, which should not be the case for already saved objects (the ones you are likely to cache). You could either add it manually in your __sleep() function or use this __wakeup() function:

class Penguin extends BaseObject
{
  function __wakeup()
  {
     $this->setNew(false);
  }
}

Though the proper way would be to add this to the __sleep() function:

  $vars = array_merge(array("\0BaseObject\0_new", "\0BaseObject\0_deleted"), $vars);

I wish there was a better way to do all this in PHP!

This entry was posted in PHP, Symfony and tagged , , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*
*