Serialization

In PHP world

by m1r1k

Serialization?

Serialization is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer, or transmitted across a network connection link) and reconstructed later in the same or another computer environment.

What could be easier?

$obj = (object) [
  'id' => 15,
  'label' => 'Serialize me!',
  'tags' => [
    'Tag1', 'Tag2', 'Tag3'
  ],
];

// Let's represent it as a string.

serialize()/deserialize()

echo serialize($obj);

// 'O:8:"stdClass":3:{
//    s:2:"id";i:15;
//    s:5:"label";s:13:"Serialize me!";
//    s:4:"tags";a:2:{
//       i:0;s:4:"Tag1";
//       i:1;s:4:"Tag2";
//    }
// }'

json_encode()/json_decode()

echo json_encode($obj);

// {
//  "id": 15,
//  "label": "Serialize me!",
//  "tags": [
//    "Tag1",
//    "Tag2"
//  ]
// }

SimpleXML

$xml = simplexml_load_string("<?xml version='1.0'><document />");
foreach ((array) $obj as $key => $value) {
  $xml->addChild($key, $value);
}
echo $xml->asXML();

// <?xml version="1.0"?>
//  <object>
//      <id>15</id>
//      <label>Serialize me!</label>
//      <empty/>
//  </object>

But we have other formats

  • YAML
  • HAL
  • Xliff
  • Custom formated XML
  • ect

But usually we have a bit more complex data structures

Have you seen externals of ContentEntity instance?

echo strlen(serialize(node_load(1)));

// 9846

echo json_encode(node_load(1));

// {"in_preview":null}

echo \Drupal::service('serializer')->serialize(node_load(1), 'json');

// { "nid": [ { "value": "1" } ], "uuid": [ { "value": "3bc88030-1ebb-457b-9e00-db67c90efd17" } ],
// "vid": [ { "value": "1" } ], "type": [ { "target_id": "page" } ], "langcode": [ { "value": "en" } ],
// "title": [ { "value": "Abluo Capto Olim" } ], "uid": [ { "target_id": "0", "url": "\/user\/0" } ],
// "status": [ { "value": "1" } ], "created": [ { "value": "1447807663" } ], "changed": [ { "value":
//  "1448367583" } ], "promote": [ { "value": "0" } ], "sticky": [ { "value": "0" } ], 
// "revision_timestamp": [ { "value": "1448367583" } ], "revision_uid": [ { "target_id": "0",
// "url": "\/user\/0" } ], "revision_log": [], "revision_translation_affected": [ { "value": "1" } ],
// "default_langcode": [ { "value": "1" } ], "content_translation_source": [],
// "content_translation_outdated": [], "path": [], "body": [ { "value": "VALUE",
// "format": "plain_text", "summary": "VALUE" } ] }

What is serializer?

Serializer public API

class Serializer {

    final public function serialize($data, $format, array $context = array())

    final public function deserialize($data, $type, $format, array $context = array())

    public function normalize($data, $format = null, array $context = array())

    public function denormalize($data, $type, $format = null, array $context = array())

    public function supportsNormalization($data, $format = null)

    public function supportsDenormalization($data, $type, $format = null)

}

Basic usage

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new ObjectNormalizer());

$serializer = new Serializer($normalizers, $encoders);

$serializer->serialize($obj, 'json');

Serialization in Drupal 8

  • Symfony components
    • Main Serializer API
    • Basic Encoders
  • Serialization module
    • Normalizers
    • Services resolver
  • Hal module
    • Normalizers for HAL
    • Encoder for HAL

Basic usage

$serializer = \Drupal::service('serializer');

$json_output = $serializer->serialize($node, 'json');

$deserialized_node = $serializer->deserialize($json_output, 
    'Drupal\Core\Entity\ContentEntityInterface', 'json', ['entity_type' => 'node']);

Drupal Normalizers

Normalizer Goal
ComplexDataNormalizer Top level normalizer for all objects in drupal
ConfigEntityNormalizer Normalizer to deal with config entities only
ContentEntityNormalizer Normalizer to deal with content entities
ListNormalizer Normalizer for iterable objects
TypedDataNormalizer Normalizer for low level pieces of data

Metadata definition

services:
  serializer.normalizer.config_entity:
    class: Drupal\serialization\Normalizer\ConfigEntityNormalizer
    tags:
      - { name: normalizer }
    arguments: ['@entity.manager']
  serializer.normalizer.content_entity:
      class: Drupal\serialization\Normalizer\ContentEntityNormalizer
      tags:
        - { name: normalizer }
      arguments: ['@entity.manager']
  serializer.normalizer.password_field_item:
      class: Drupal\serialization\Normalizer\NullNormalizer
      arguments: ['Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem']
      tags:
        - { name: normalizer, priority: 20 }
  serializer.encoder.hal:
    class: Drupal\hal\Encoder\JsonEncoder
    tags:
      - { name: encoder, priority: 10, format: hal_json }

Let's create our own format

Technical description:

  • Custom formated XML
  • XML comments
  • Show only translatable data

 

http://cgit.drupalcode.org/smartling/tree/?h=8.x-1.x

Symfony and  JMS Serializer

The same serializer with the same public API but...

With some adjustments :)

Flexible configuration

$serializer =
    JMS\Serializer\SerializerBuilder::create()
    // Set directory to store cached and compiled
    // version of classes with metadata.
    ->setCacheDir($someWritableDir)
    // Additional check if cached data is still valid.
    ->setDebug($trueOrFalse)
    // Custom logic injection into the serialization process
    ->configureHandlers(function(JMS\Serializer\Handler\HandlerRegistry $registry) {
            $registry->registerHandler('serialization', 'MyObject', 'json',
                function($visitor, MyObject $obj, array $type) {
                    return $obj->getName();
                }
            );
        })
    ->build();

Events

use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;

class MyEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            array('event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize'),
        );
    }

    public function onPreSerialize(PreSerializeEvent $event)
    {
        // do something
    }
}

Events

use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;

class MyEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            array('event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize'),
        );
    }

    public function onPreSerialize(PreSerializeEvent $event)
    {
        // do something
    }
}

And the most delicious 

ANNOTATIONS!

http://jmsyst.com/libs/serializer/master/reference/annotations

Thank you

Questions?

Serialization in Symfony and Drupal 8

By Artyom Miroshnik

Serialization in Symfony and Drupal 8

  • 2,154