Jump to content

Serializable Class with Sub Classes


rwhite35
Go to solution Solved by requinix,

Recommended Posts

So I have a registry class that holds sub class instances.  When the application loads a module, the registry class needs to make sub class functionality available to the module.  There's an autoloader in place, I just need the class instances.  A service manager should load the registry class with sub classes.  Service manager RegistryClass has this prototype at the end of the process:

// hash table of registry properties and classes
// SiteNavigation example is one sub class
RegistryClass Object( 
	[data:RegistryClass:private] => 
	[regList:RegistryClass:private] => Array ( 
		[SiteNavigation] => SiteNavigation Object ( 
			[siteConfig:protected] => 
			[route:protected] => 
		
                ) 
	)
)

There are several MVC scripts in the module that require different things.  However, when the script (ie module controller) picks up the Registry object, it's hash table (regList) is empty.

// empty table, empty belly
RegistryClass Object
(
    [data:RegistryClass:private] => 
    [regList:RegistryClass:private] => Array
        (
        )

)

I've tried using the built in PHP interface Serializable however, it's producing incomplete PHP objects when I call unserialize() method. Called from a module script - that looks like:

$newObj = unserialize($serialized);

// Registry appears fine, however, SiteNavigation is incomplete
RegistryClass Object ( 
    [data:RegistryClass:private] => 
    [regList:RegistryClass:private] => Array ( 
      [SiteNavigation] => __PHP_Incomplete_Class Object ( 
          [__PHP_Incomplete_Class_Name] => SiteNavigation 
          [siteConfig:protected] => 
          [route:protected] => ) ) )

Here is a snip from the service manager which 1. instantiates a singleton registry class 2. instantiates the sub classes.

$regObj = RegistryClass::getInstance(); // Singleton class holds instances of sub classes in hash table
/*
* set() method simply assigns sub class names as key and object instance as value to regList
*/
if(class_exists($className)) {
	$regObj->set($className, new $className);
}
/*
* now serialize and assign to session, RegistryClass implements Serializable
*/
$_SESSION['RegistryClass'] = $regObj->serialize($regObj);
 

It seems like the sub classes (ie SiteNavigation) doesn't serialize. Error log output only C:13:"RegistryClass":2:{N;}).

 

Any thoughts are appreciated.

Link to comment
Share on other sites

  • Solution

What is the code for RegistryClass::serialize and why aren't you using just serialize()?

 

"__PHP_Incomplete_Class" means PHP couldn't load the class named. Which is SiteNavigation. Are you sure the autoloader is set up properly and gets installed before the call to unserialize()?

Link to comment
Share on other sites

What is the code for RegistryClass::serialize and why aren't you using just serialize()?

 

"__PHP_Incomplete_Class" means PHP couldn't load the class named. Which is SiteNavigation. Are you sure the autoloader is set up properly and gets installed before the call to unserialize()?

 

Thanks for the reply requinix!

 

The way I called serialize() was incorrect - fixed that now - thanks!  

The service manager runs after all classes have been autoloaded and I've confirmed that I have valid objects at this point in the service manager process.

 

Im expecting serialize() to recursively "serialize" each sub class.  But when I pass $regObj through serialize(), only the parent RegistryClass is being serialized.  

error_log("RegistryClass Serialized: " . $RegClassSerial); // outputs C:13:”RegistryClass”:2:{N;}

Here is the RegistryClass relevant code:

class RegistryClass implements Serializable {
  private $data;
  private $regList = array();

/* instantiates the singleton, add sub classes to regList with set(), etc */

public function serialize() {
  return serialize($this->data); // where recursion is expected
}

public function unserialize($data) {
  $this->data = unserialize($data);
}

public function getData() {
  return $this->regList;
}

To test the theory that serialize() isn't recursively serializing each sub class, I've hacked a work around that does this manually.  And here is that code:

$regObj = RegistryClass::getInstance();
/* adds sub classes, see code in initial post */

$serial = serialize($regObj);
/*
* here's the hack to manually force serializing each sub class
* by cloning the serialized string and appended the sub classes
*/
foreach($regObj->getData() as $obj) {
  if(is_a($obj, $className)) {
    $serial .= serialize($obj);
  }
}
$_SESSION['RegistryClass'] = $serial;

The hack code produces a string that looks like:

error_log("RegistryClass Sub Class Serialized: " . $serial);
// RegistryClass Serialized: C:13:"RegistryClass":2:{N;}O:14:"SiteNavigation":2:{s:13:"...

And finally, when the serial string from $_SESSION['RegistryClass'] is assigned and unserialized(), the parent class is picked up but not the subs. Again, no recursion.

// module/view/Index.phtml

$serialized = $_SESSION['RegistryClass']; 
// echo $serialized
//C:13:”RegistryClass”:2:{N;}O:14:"SiteNavigation":2:{s:13:"*siteConfig";N;s:8:"*route";N;}

$newObj = unserialize($serialized);
print_r($newObj);
/*
* outputs
* RegistryClass(
*    [data:RegistryClass:private] => 
*    [regList:RegistryClass:private] => Array( )
* )
*/

As you see from above, regList is empty.  Where I'm expecting SiteNavigation.

 

Thanks again for the feedback, I appreciate your insight.

Edited by rwhite35
Link to comment
Share on other sites

Your serialize methods aren't doing anything with $regList, so that information will get lost during serialization.

 

Drop the Serializable interface and its two methods and let PHP do what it normally would do. Serializable is only really if you want to change how serialization works.

Link to comment
Share on other sites

Here is how object references can be saved during serialization.

<?php

error_reporting(E_ALL);
ini_set('display_errors',1);
define('br','<br />');

class RegistryClass
{

    private $data;
    private $regList = array();

    public function set($name,$value)
    {
        $this->regList[$name] = $value;
    }

    public function getData()
    {
      return $this->regList;
    }
}

class RegList {

    private $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

$reg = new RegistryClass();
$a = new RegList("fred");
$reg->set('a',$a);
$b = new RegList("jane");
$reg->set('b',$b);
$c = serialize($reg); // doesn't work as references to other objects are lost

//we can save the Registry object and the referenced objects in an array
$c = array();
$c[0] = $reg;

foreach($reg->getData() as $key => $value)
{
    $c[1][$key] = $value;
}

// now serialize the array
$d = serialize($c); // everything is saved

// un-serialize the array
$e = unserialize($d);

// retrieve our Registry object
$reg = $e[0];

// now add the instances back into the Registry object
foreach($e[1] as $key => $value)
{
    $reg->set($key,$value);
}

// test it out
$instance = $reg->getData();

foreach($instance as $key => $value)
{
    echo $key . ': ' . $value->getName() . br;
}
  • Like 1
Link to comment
Share on other sites

What is the code for RegistryClass::serialize and why aren't you using just serialize()?

 

"__PHP_Incomplete_Class" means PHP couldn't load the class named. Which is SiteNavigation. Are you sure the autoloader is set up properly and gets installed before the call to unserialize()?

 

Yep this ultimately was the problem.  A sub class (not SiteNavigation) was not properly getting instantiated due to a typo in an extended data class... VERY FRUSTRATING trying to track down bugs when they are three objects deep.  What bubbles up isn't typically the correct problem.  I was getting a path errors.

 

Eventually (after a lot of trial and error) I created a test script to load each class individually.  It was through that test script the typo was revealed...

 

Thanks again.

Edited by rwhite35
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.