final public function unserialize(
        $serialized = null)
    {
        $objectData = null;

        try {
            // Attempt to unpack the serialized data. Do not @suppress any
            // syntax errors, since the user needs to know if they've provided
            // a bad serialized string (or even a non-string value).
            // NOTE: If the original object only contained perfect JSON data,
            // then there are no sub-objects. But if any sub-objects existed
            // within the data, this will recursively unserialize those too.
            $objectData = unserialize($serialized); // Throws.
        } catch (\Exception $e) { // IMPORTANT: Catch ANY exception!
            // This can literally ONLY happen if the user had given us (and then
            // serialized) non-JSON data containing other objects that attempt
            // (and fail) unserialization now and throw an exception instead.
            throw new LazySerializationException(sprintf(
                'Unexpected exception encountered while unserializing a sub-object. Error: %s',
                $e->getMessage()
            ));
        }

        if (!is_array($objectData)) {
            // Anything other than an array means that $serialized was invalid.
            throw new LazySerializationException(
                'The serialized object data that you provided could not be unserialized.'
            );
        }

        // The data is fine. Now just construct this new object instance.
        // NOTE: Important since ctor builds/links its necessary property map.
        // NOTE: The unserialized object (or its data) has no links to the
        // originally serialized object anymore, apart from the fact that (just
        // like ANY two identical classes) they would both be linked to the
        // same compiled class property map ("_compiledPropertyMapLink").
        $this->__construct($objectData); // Throws.
    }