0

My object array is not deserializing when loading the entity from the database.

I have a non-entity object named NetworkAddress. Then in my Foo entity, I have an array of these network addresses.

The network addresses are being saved as serialized PHP in the database, but when I load the object, the data for the network addresses aren't being added.

/**
 * @ORM\Entity(repositoryClass="App\Repository\FooRepository")
 */
class Foo implements \Serializable
{
    ...

    /**
     * @var NetworkAddress[]
     * @Groups({"main", "initialized"})
     * @ORM\Column(type="array")
     */
    private array $addresses;
    ...

So when I load my $foo object, $foo->getAddresses() returns an array of empty NetworkAddresses, even though the raw database values is this:

a:1:{i:0;C:22:"App\DTO\NetworkAddress":41:{a:2:{i:0;s:9:"127.0.0.1";i:1;s:4:"5000";}}}

I'm using a basic ->findOneBy() command on my repo to retrieve the object.

Serializing seems to work, but is there something special for deserializing an object stored as an array?

Here's my NetworkAddress class, w/o the getters/setters

class NetworkAddress implements \Serializable
{
    /**
     * @Groups({"main", "initialized"})
     * @Assert\Regex(
     *     pattern="/^[0-9A-z\.-]+$/",
     *     match=true,
     *     message="Invalid host format."
     * )
     */
    private string $host = 'localhost';

    /**
     * Intentionally left untyped so we can accept int or string for port number.
     *
     * @Groups({"main", "initialized"})
     * @Assert\NotBlank(message = "Port should not be empty.")
     * @Assert\Range(min="1", max="65535", invalidMessage="Invalid port.")
     */
    private $port;
4
  • It seems that db value is not in json notation. {a:{1:{C:[{22:"App\DTO\NetworkAddress"}, {41:""}]},2:{s:[{9:"127.0.0.1"},{4:"5000"}]}}} Commented Apr 11, 2020 at 0:52
  • Correct, it's serialized PHP. This is the format used with array column types by Doctrine. For PHP, it's more powerful because it can reconstruct the NetworkAddress object from that without any additional logic. Commented Apr 11, 2020 at 7:01
  • Take a look at Embeddables. I found rosstuck.com/persisting-value-objects-in-doctrine a good read for Value Objects in Doctrine. Commented Apr 11, 2020 at 7:49
  • 1
    @Coder1: That's right, but php serialization has vulnerability issue caused by self recursive proccess possibility, which would make proccess overflow indefinitely, and cause server crash. It is different with json serialization that have solid inner deep loops limitation checking. Commented Apr 12, 2020 at 12:04

1 Answer 1

1

You can check the following, associate to deserialization problem on array of objects, and "safe php serialization" to fix up the vulnerability issue on php serialization.

class Foo implements \Serializable
{
    //...

    protected $addresses;
    //...

    public function __construct($addresses = null) {
        if (!is_null($addresses)) {
        {
            $this->addresses = $addresses;
        }
    }

    /* serialize network addresses */
    public function serialize(): string {

        /* sanitize for any recursion */
        if ($class = $this->checkRecursion()) {
            throw new \Exception("Recursion detected: '$class'");
        }
        return serialize($this->addresses);
    }

    /* deserialize network addresses */
    public function unserialize($serialized): void {
        $this->addresses = unserialize($serialized);

        /* sanitize for any recursion */
        if ($class = $this->checkRecursion()) {
            throw new \Exception("Recursion detected: '$class'");
        }
    }

    /* check recursion function */
    protected function checkRecursion() {
        $class = print_r($this, true);
        if (preg_match('/[^\=\>]+(?=\*RECURSION\*)/i', $class, $matches)) {
            return trim($matches[0]);
        }
        return false;
    }
}

Class NetworkAddress:

class NetworkAddress implements \Serializable
{
    protected $netAddress ;

    public function __construct($netAddress = ['host' => '127.0.0.7', 'port' => 7007]) {
        if (!is_null($netAddress)) {
            $this->netAddress = $netAddress;
        }
    }

    /* get class property by name
        eg. $net->__get('netAddress');
     */
    public function __get($name) {
        return $this->$name;
    }

    /* set class property from name
        eg. $net->__set('netAddress', ['host' => 'localhost', 'port' => '8080']);
     */
    public function __set($name, $value) {
        $this->$name = $value;
    }

    /* safe serialize */
    public function serialize(): string {

        /* sanitize for any recursion */
        if ($class = $this->checkRecursion()) {
            throw new \Exception("Recursion detected: '$class'");
        }
        return serialize($this->netAddresses);
    }

    /* safe unserialize */
    public function unserialize($serialized): void {
        $this->netAddresses = unserialize($serialized);

        /* sanitize for any recursion */
        if ($class = $this->checkRecursion()) {
            throw new \Exception("Recursion detected: '$class'");
        }
    }

    /* check recursion function */
    protected function checkRecursion() {
        $class = print_r($this, true);
        if (preg_match('/[^\=\>]+(?=\*RECURSION\*)/i', $class, $matches)) {
            return trim($matches[0]);
        }
        return false;
    }
}

Calls example:

/* create new network adresses */
$addresses = [new NetworkAddress(['host' => 'localhost', 'port' => 8080]), new NetworkAddress(['host' => 'example.com', 'port' => 80])];

/* create new foo */
$foo1 = new Foo($addresses);

/* serialize foo object */
$serialized = serialize($foo1);

/* unserialized foo object */
$foo2 = unserialize($serialized);

Hope this helps.

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

2 Comments

Thanks @OO7, this strategy worked for me! For anyone looking at this answer in the future, I did have to modify the serialization methods in the answer to accommodate host and port properties as defined in the NetworkAddress model in the original post.
You are welcome. I have edit my answer, so please to look at checkRecursion() method and implementation on serialize and unserialize method. Thanks.

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.