Hello,
AbstractObjectNormalizer
has a long list of ongoing issues and pull request, we got a talk at Paris Symfony Live with @dunglas @fbourigault and @soyuka on how we want to move forward, here is a resume of our talk and what we would like to achieve for the future:
At the origin this normalizer was done for API Platform and many other third party libraries / project, in order to have a normalizer that is able to normalize and denormalize any data object (specifically doctrine entities). ObjectNormalizer
, PropertyNormalizer
and GetSetMethodNormalizer
were already existing, but were having slightly different beahvior, that's why two abstract class were created AbstractNormalizer
and AbstractObjectNormalizer
Over time, lot of missing features in the ObjectNormalizer were added on both the AbstractObjectNormalizer and the AbstractNormalizer as we needed the same features there.
The main problem with the inheritance model is that it's very hard to customize some behavior without having to write a lot of code. For example, the current API Platform Normalizer has a lot of duplicated code as he extends the AbstractObjectNormalizer
and must rewrite the logic around setting and getting value.
If we look at the main differences between those 3 normalizers they just have a different way of setting and getting value for a given object:
- PropertyNormalizer use Reflection to access and set public / protected / private properties
- GetSetMethodNormalizer use hard coded prefix to get and all a getter and setter on properties
- ObjectNormalizer use PropertyAccessor to get / set value on properties
-
creating a rock solid test suite that handles every features of those 3 normalizers implementing the
NormalizerInterface
. Thanks to this, the refactor can be handled without breaking the working behavior. Also this test suite would be used by other people that want to provide different ways of dealing with object normalization like: -
A generated normalizer (like it can be done with the Automapper proposal)
-
A bridge for the JMS serializer
-
...
Current normalizers already handle lot of features, but some of them, that are widely used in the JMS/Serializer
library are missing. Goal is to provide some of this features or at least extension points.
We strongly believe that those 3 normalizers can be merged into a single normalizer, each feature that they currently support should be provided by a call to another object:
-
Serializer should have an hard dependency on
symfony/property-access
component, a bridge interface may be provided to handle how value can be setted or gettted: also should allow a new extension point that will allow virtual property -
A new interface will be provided
AttributeListExtractorInterface
(name can change), that allow getting attributes based on an object. A default implementation will be provided that use thePropertyListInfoExtractorInterface
and a class resolver, as the latter one only rely on class name, it may be important to have this distinction (this will allow features that rely on a specific value of the object to get the current property list, like exclusion policy) -
Extracting list of attributes can be delegated to implementations that implements the
PropertyListExtractorInterface
orAttributeListExtractorInterface
-
Groups
,Max Depth
,Attributes
,IgnoredAttributes
,Ignore
and many other features that influence the way an attribute is allowed or disallowed on a normalizer can be also delegated to the extraction mechanism by implementing thePropertyListExtractorInterface
orAttributeListExtractorInterface
-
Instancing an object can be delegated to a new interface with implementations that will handle this part (something like
InstantiatorInterface
), which will provide a new extension point -
Name converter will certainly stay the same
-
Circular Reference could be delegated to a new Normalizer that decore another normalizer if possible (if not it will stay in this normalizer)
-
Discriminator Handling could be delegated to a new denormalizer that decore another denormalizer: as this is mainly to find the correct class when denormalizing
- Finish the work on symfony/symfony#30704 to add a new extraction extension point for accessor and mutators.
- Add a hard dependency on property-info component and use the ReadAccessor / WriteMutator to handle accessing property on a object or an array
All of those interface / extensions point will work with a $context
variable. Like the http-client component, this $context
will be an array.
-
Each implementation will have a
$defaultContext
attribute in the constructor to handle global configuration. -
Each different context will have a
ContextBuilder
class that allow a API that play nicely with IDE and discovery for setting the configuration (like theHttpOptions
class).
Provide cache mechanism for each of those extension point:
- Attributes extraction cache for the
PropertyListExtractorInterface
only,AttributeListExtractorInterface
cannot have a cache by its volatile nature - Instantiator Cache: Construction without calling constructor can be cached
- Accessor / Mutator cache: calling accessor / mutator can be cached by using code generation or other mechanism (like closure binding) to speed up this process
Cache will vary depending on the context, so a context hash mecanism should be provided.
Some of the Serializer interfaces may be moved to the contracts
component
We want to tackle a lot of this work during the upcoming Hackthon, and i would like to thanks all people that contribute issues and pull requests on the serializer component.
We try to respect a lot of your work and problems by making this plan, if you think that there is some features missing or some wrong approach, feel free to respond here.