Skip to content

[Serializer] TraceableNormalizer - SerializerDataCollector does not fallback gracefully if expected properties to be collected are missing #46522

@silverbackdan

Description

@silverbackdan

Symfony version(s) affected

6.1.0

Description

The lateCollect method looks to have been introduced. I'm not sure why, but currently some of the data is not collected. Perhaps the issue could be there, or perhaps we can fallback gracefully if some data is missing.

How to reproduce

I am using API Platform and it is in a Behat test where some invalid data has been submitted where a property submitted is not the correct variable type (a null value instead of a boolean).

So the serialization is probably started and then a validation process is triggered which stops the process. Therefore some data is not collected in the new TraceableNormalizer.

While I can raise an issue there - I'm first checking whether for BC breaking purposes, it should be addressed at this level.

API Platform will throw Symfony\Component\ErrorHandler\Exception\FlattenException from Serializer/AbstractItemNormalizer line 554 : https://github.com/api-platform/core/blob/main/src/Serializer/AbstractItemNormalizer.php#L554

Possible Solution

These changes to allow fallbacks would resolve the issue for me.

    /**
     * {@inheritDoc}
     */
    public function lateCollect(): void
    {
        $this->data = [
            'serialize' => [],
            'deserialize' => [],
            'normalize' => [],
            'denormalize' => [],
            'encode' => [],
            'decode' => [],
        ];

        foreach ($this->collected as $collected) {
            $collectedData = $collected['data'] ?? null;
            $data = [
                'data' => $this->cloneVar($collectedData),
                'dataType' => get_debug_type($collectedData),
                'type' => $collected['type'] ?? null,
                'format' => $collected['format'] ?? null,
                'time' => $collected['time'] ?? null,
                'context' => $this->cloneVar($collected['context'] ?? null),
                'normalization' => [],
                'encoding' => [],
            ];

            if (isset($collected['normalization'])) {
                $mainNormalization = array_pop($collected['normalization']);

                $data['normalizer'] = ['time' => $mainNormalization['time']] + $this->getMethodLocation($mainNormalization['normalizer'], $mainNormalization['method']);

                foreach ($collected['normalization'] as $normalization) {
                    if (!isset($data['normalization'][$normalization['normalizer']])) {
                        $data['normalization'][$normalization['normalizer']] = ['time' => 0, 'calls' => 0] + $this->getMethodLocation($normalization['normalizer'], $normalization['method']);
                    }

                    ++$data['normalization'][$normalization['normalizer']]['calls'];
                    $data['normalization'][$normalization['normalizer']]['time'] += $normalization['time'];
                }
            }

            if (isset($collected['encoding'])) {
                $mainEncoding = array_pop($collected['encoding']);

                $data['encoder'] = ['time' => $mainEncoding['time']] + $this->getMethodLocation($mainEncoding['encoder'], $mainEncoding['method']);

                foreach ($collected['encoding'] as $encoding) {
                    if (!isset($data['encoding'][$encoding['encoder']])) {
                        $data['encoding'][$encoding['encoder']] = ['time' => 0, 'calls' => 0] + $this->getMethodLocation($encoding['encoder'], $encoding['method']);
                    }

                    ++$data['encoding'][$encoding['encoder']]['calls'];
                    $data['encoding'][$encoding['encoder']]['time'] += $encoding['time'];
                }
            }

            $this->data[$collected['method'] ?? 'unknown'][] = $data;
        }
    }

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions