Skip to content

[Serializer] Cache error normalizing anonymous class #36589

@ndench

Description

@ndench

Symfony version(s) affected: 4.4.7

Description
During phpunit tests with APP_DEBUG=1, an anonymous class can be normalized by the NormalizerInterface, however, with APP_DEBUG=0 the following error is produced:

Symfony\Component\Cache\Exception\InvalidArgumentException: Cache key "class@anonymous/app/tests/AnonymousClassTest.php:16$9c" contains reserved characters "{}()/\@:".
Full stack trace
Symfony\Component\Cache\Exception\InvalidArgumentException: Cache key "class@anonymous/app/tests/AnonymousClassTest.php:16$9c" contains reserved characters "{}()/\@:".

/app/vendor/symfony/cache/CacheItem.php:177
/app/vendor/symfony/cache/Traits/AbstractTrait.php:281
/app/vendor/symfony/cache/Traits/AbstractAdapterTrait.php:44
/app/vendor/symfony/cache/Adapter/PhpArrayAdapter.php:122
/app/vendor/symfony/serializer/Mapping/Factory/CacheClassMetadataFactory.php:50
/app/vendor/symfony/serializer/Mapping/ClassDiscriminatorFromClassMetadata.php:50
/app/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:269
/app/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:164
/app/vendor/symfony/serializer/Serializer.php:152
/app/tests/AnonymousClassTest.php:19

How to reproduce

The error can be reproduced with the following test case:

<?php

namespace App\Tests;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

final class AnonymousClassTest extends KernelTestCase
{
    public function testSerializeAnonymousClass(): void
    {
        static::bootKernel();

        $normalizer = static::$container->get(NormalizerInterface::class);

        $class = new class() {
        };

        $normalizer->normalize($class);

        $this->addToAssertionCount(1);
    }
}
Successful test run with APP_DEBUG=1
$ APP_DEBUG=1 vendor/bin/phpunit tests/AnonymousClassTest.php 
PHPUnit 8.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 466 ms, Memory: 20.00 MB

OK (1 test, 1 assertion)
Failed test run with APP_DEBUG=0
$ APP_DEBUG=0 vendor/bin/phpunit tests/AnonymousClassTest.php 
PHPUnit 8.5.4 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 216 ms, Memory: 18.00 MB

There was 1 error:

1) App\Tests\AnonymousClassTest::testSerializeAnonymousClass
Symfony\Component\Cache\Exception\InvalidArgumentException: Cache key "class@anonymous/app/tests/AnonymousClassTest.php:16$9c" contains reserved characters "{}()/\@:".

/app/vendor/symfony/cache/CacheItem.php:177
/app/vendor/symfony/cache/Traits/AbstractTrait.php:281
/app/vendor/symfony/cache/Traits/AbstractAdapterTrait.php:44
/app/vendor/symfony/cache/Adapter/PhpArrayAdapter.php:122
/app/vendor/symfony/serializer/Mapping/Factory/CacheClassMetadataFactory.php:50
/app/vendor/symfony/serializer/Mapping/ClassDiscriminatorFromClassMetadata.php:50
/app/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:269
/app/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:164
/app/vendor/symfony/serializer/Serializer.php:152
/app/tests/AnonymousClassTest.php:19

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

Possible Solution

It appears that an issue similar to this has appeared a few times in the past (linked issued below) and it was fixed with something like this in PropertyAccessor::getReadAccessInfo and PropertyAccessor::getWriteAccessInfo:

$key = false !== strpbrk($key = $class.'..'.$property, '{}()/@:') ? rawurlencode($key) : $key;

But this issue seems to be unrelated to the PropertyAccessor, so I adding the change to CacheClassMetadataFactory::getMetadataFor and that makes the test pass successfully with APP_DEBUG=0:

public function getMetadataFor($value)
{
    $class = $this->getClass($value);
    // Key cannot contain backslashes according to PSR-6
    $key = strtr($class, '\\', '_');
+    $key = false !== strpbrk($key, '{}()/@:') ? rawurlencode($key) : $key;

    $item = $this->cacheItemPool->getItem($key);

Not sure if this is the correct place to fix the issue, or if it has knock on affects elsewhere.

Additional context

I found a few older issues which may be related:

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