hgraca/serializer

A highly customizable serializer

v0.1.0 2023-08-16 19:59 UTC

This package is auto-updated.

Last update: 2024-04-16 21:17:35 UTC


README

pipeline status PHP Coverage

This is a highly customizable PHP serializer, who by default can serialize/deserialize any data structure to/from json.

How to use

For a full example of how to bootstrap it in pure PHP, see this.

For a full example of how to bootstrap it in Symfony PHP config, see this.

You can find a working example in tests/dumper/dumper.php. You can run it with ./tests/dumper/dumper.php and experiment with it.

You can find an example of a serialization result, further down this document.

For more examples, look at the tests in tests/TestCase.

Changing the default behavior

Unknown source types

The normalizers store the source type for a data structure, which in case of objects it's the FQCN. If the class changes location or name, or is replaced by a new version, or is a foreign class (from another application) that has a correspondent class in the current codebase, we need to tell the de/serializer what is the target type. This is achieved by using a mapper (serializer.type_mapper.php), mapping a hardcoded type to the target type.

Take a look at the documentation bootstrap examples to see the anatomy of a serializer.type_mapper.php file.

Custom de/normalizers

By default, it uses reflection to normalize/denormalize objects.

You can add custom de/normalizers to de/normalize specific types of objects, avoiding reflection and potentially making de/serialization more performant. Take a look at the documentation bootstrap examples on how the composite de/normalizer is constructed.

Custom codec

By default, it de/serializes to json, directly from the de/normalizer output.

This can be changed by replacing the default json codec. Take a look at the documentation bootstrap examples on how the codec is injected in the de/serializer.

Serialization result

The following is an example of the default serialization result, with the SerializationDateTimeMetadataCollector metadata collector configured.

Worth noting:

  • The metadata collected can be seen at the bottom, and is only collected for the root element because that is how the medatada collector is defined to work, we can create metadata collectors that collect metadata at all levels;
  • The _type_id is filled for the root element because that class has an ID defined in the serializer.type_mapper.php;
  • Although the source class doesn't exist anymore, it would have been possible to deserialize this data structure because the _type_id in the serializer.type_mapper.php identifies to what class the data structure should be hydrated to.
{
  "_source_type": "Some\\Class\\That\\Doesnt\\Exist",
  "_type_id": "01G7YFVFRQBA0B2JD02D7MHYQT",
  "_data": {
    "someId": {
      "_source_type": "Hgraca\\Serializer\\Test\\TestSubject\\SomeId",
      "_type_id": null,
      "_data": {
        "protectedId": {
          "_source_type": "string",
          "_type_id": null,
          "_data": "protected-1"
        },
        "id": {
          "_source_type": "string",
          "_type_id": null,
          "_data": "1"
        }
      }
    },
    "someIdSubclass": {
      "_source_type": "Hgraca\\Serializer\\Test\\TestSubject\\SomeIdSubclass",
      "_type_id": null,
      "_data": {
        "protectedId": {
          "_source_type": "string",
          "_type_id": null,
          "_data": "protected-bar"
        },
        "id": {
          "_source_type": "string",
          "_type_id": null,
          "_data": "bar"
        },
        "bar": {
          "_source_type": "string",
          "_type_id": null,
          "_data": "foo"
        }
      }
    },
    "array": {
      "_source_type": "array",
      "_type_id": null,
      "_data": [
        {
          "_source_type": "int",
          "_type_id": null,
          "_data": 5
        },
        {
          "_source_type": "float",
          "_type_id": null,
          "_data": 6.1
        },
        {
          "_source_type": "string",
          "_type_id": null,
          "_data": "another string"
        }
      ]
    },
    "dateTime": {
      "_source_type": "DateTime",
      "_type_id": null,
      "_data": {
        "date_time": "2023-06-18T19:27:07.449002",
        "timezone": "UTC"
      }
    },
    "dateTimeImmutable": {
      "_source_type": "DateTimeImmutable",
      "_type_id": null,
      "_data": {
        "date_time": "2023-06-18T19:27:07.449003",
        "timezone": "UTC"
      }
    },
    "anArray": {
      "_source_type": "array",
      "_type_id": null,
      "_data": [
        {
          "_source_type": "int",
          "_type_id": null,
          "_data": 7
        },
        {
          "_source_type": "float",
          "_type_id": null,
          "_data": 1.7
        },
        {
          "_source_type": "string",
          "_type_id": null,
          "_data": "yet another string"
        }
      ]
    }
  },
  "_metadata": {
    "_serialization_date_time": "2023-06-18T19:27:07+00:00"
  }
}

How to run the test suite and other commands

Using local PHP (8.2):

  • Change PHP and xdebug configs in ./build;
  • Install the dependencies with composer install;
  • The tests can be run with composer test
  • If in development you can run composer dev to run all tests and fix code style;
  • To list all custom scripts run composer run-script --list.