2

I have an object tree like the following, which I need to serialize and store on the filesystem. I need the full hierarchy with all class properties and later I will unserialize and restore the class hierarchy.

class X implements \Serializable {

  private $x1;

    public function serialize() {
            return serialize(get_class_vars(get_class($this)));
    }

    public function unserialize($data) {
            $values = unserialize($data);
            foreach ($values as $key => $value) {
                    $this->$key = $value;
            }
    }
}

class A implements \Serializable {

  private $a1;
  private $a2;
  // type of a3 is class X!
  protected $a3;

    public function serialize() {
            return serialize(get_class_vars(get_class($this)));
    }

    public function unserialize($data) {
            $values = unserialize($data);
            foreach ($values as $key => $value) {
                    $this->$key = $value;
            }
    }
}

class B extends A implements \Serializable {

  private $b1;
  private $b2;

    public function serialize() {
    //  $base = parent::serialize();
            return serialize(get_class_vars(get_class($this)));
    }

    public function unserialize($data) {
            $values = unserialize($data);
            foreach ($values as $key => $value) {
                    $this->$key = $value;
            }
    }
}


class C extends A implements \Serializable {

  private $c1;
  private $c2;

    public function serialize() {
    //  $base = parent::serialize();
            return serialize(get_class_vars(get_class($this)));
    }

    public function unserialize($data) {
            $values = unserialize($data);
            foreach ($values as $key => $value) {
                    $this->$key = $value;
            }
    }
}

The subclasses can serialize itself, but for the base class I don't know, how I can combine the serialized data. Furthermore I get serialized data from the filesystem, but I don't know, which subclass I will get. Does PHP's unserialize() create the right class instance? It should also initialize the base class A.

How can I solve that?

Maybe I can use the var_dump() output, but how I can store it into a variable?

2 Answers 2

1

This is how I would recommend serializing objects:

class Color implements \Serializable
{
    private $Name;
    private $Type;

    public function __construct(string $Name, int $Type)
    {
        $this->Name = $Name;
        $this->Type = $Type;
    }

    public function serialize()
    {
        $Props['Name'] = $this->Name;
        $Props['Type'] = $this->Type;
        return serialize($Props);
    }

    public function unserialize($Data)
    {
        list($this->Name, $this->Type) = unserialize($Data);
    }
}

class Blue extends Color
{
    private $Intensity;

    public function __construct()
    {
        parent::__construct('Blue', 10);
        $this->Intensity = 90;
    }

    public function serialize()
    {
        $Props['parent'] = parent::serialize();
        $Props['Intensity'] = $this->Intensity;
        return serialize($Props);
    }

    public function unserialize($Data)
    {
        $Obj = unserialize($Data);
        parent::unserialize($Obj['parent']);
        $this->Intensity = $Obj['Intensity'];
    }
}

Whichever object you pass in to the serialize() function is the object you will get back (as a string) to unserialize(). If you go your route, then you can implement the serialize()/unserialize() functions inside a trait and get_object_vars() will work properly for private variables.

Sign up to request clarification or add additional context in comments.

Comments

0

I have implemented serialize() and unserialize() in every affected class like this:

        public function serialize() {

            $res = array();

            $reflect = new \ReflectionClass(__CLASS__);
            $propList = $reflect->getProperties();

            foreach($propList as $prop) {
                    if ($prop->class != __CLASS__) {
                            continue; // visible properties of base clases
                    }

                    $name = $prop->name;
                    $res[$name . ":" . __CLASS__] = serialize($this->$name);
            }

            if (method_exists(get_parent_class(__CLASS__), "serialize")) {
                    $base = unserialize(parent::serialize());
                    $res = array_merge($res, $base);
            }

            return serialize($res);
    }

    public function unserialize($data) {

            $values = unserialize($data);
            foreach ($values as $key => $value) {

                    // key contains propertyName:className
                    $prop = explode(":", $key);
                    if ($prop[1] != __CLASS__) {
                            continue;
                    }

                    $this->$prop[0] = unserialize($value);
            }

            // call base class
            if (method_exists(get_parent_class(__CLASS__), "unserialize")) {
                    parent::unserialize($data);
            }
    }

Maybe there is a solution to add this functionality to a base class to prevent code copies. It should work with simple properties, arrays and objects also for large object trees with multiple levels of parent classes.

1 Comment

This code seems to have a problem with class references. Sometimes the result contains r:147, which is a reference to an object, stored also in another variable. The unserialize() cannot handle that. Maybe you can copy the object content into a fresh object to prevent references.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.