Skip to content

Doctrine second-level cache unserializes Media with uninitialized typed properties #87

@BriceFab

Description

@BriceFab

Bug report

When Doctrine ORM second-level cache is enabled, entities containing JoliCode\MediaBundle\Model\Media can be read from cache with a broken Media object state.
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html

I consistently get:

  • deprecations about dynamic properties Media::$0 and Media::$1 during unserialization
  • then a fatal error when accessing Media::getPath():
    • Typed property JoliCode\MediaBundle\Model\Media::$path must not be accessed before initialization

This looks related to Media::__serialize() returning a positional array while Media has no matching __unserialize() to restore typed/readonly constructor properties.

Environment

  • PHP: 8.2.16
  • Symfony Framework Bundle: 7.4.5
  • Doctrine ORM: 3.6.2
  • DoctrineBundle: 2.18.2
  • symfony/cache: 7.4.5
  • jolicode/media-bundle: v0.2.1 (2b3ef65638d5de23c94bdd811d308ef5d5539056)

Doctrine second-level cache setup

config/packages/doctrine.yaml:

doctrine:
  orm:
    second_level_cache:
      enabled: true
      log_enabled: true
      region_cache_driver:
        type: pool
        pool: doctrine.second_level_cache_pool
      region_lifetime: 3600

Entities using SLC:

  • Service (#[ORM\Cache(..., region: 'entity.service')])
  • related collections (children, sections, faqs, galleryImages)

Service and ServiceGalleryImage both contain Media properties mapped with MediaTypes::MEDIA_LONG.

Reproduction

  1. Enable Doctrine second-level cache.
  2. Have an entity with a Media field (JoliCode\MediaBundle\Model\Media) and cache it with #[ORM\Cache].
  3. Hit a page once (cache put).
  4. Hit the same page again (cache hit).
  5. Access media.path in Twig (or call getPath() in PHP).

Expected result

Cached entities should hydrate with valid Media objects, and getPath() should work.

Actual result

Deprecations from cache unmarshalling

var/log/prod.deprecations-2026-02-12.log:

Deprecated: Creation of dynamic property JoliCode\MediaBundle\Model\Media::$0 is deprecated
... file: /var/www/vendor/symfony/cache/Marshaller/DefaultMarshaller.php:72

Deprecated: Creation of dynamic property JoliCode\MediaBundle\Model\Media::$1 is deprecated
... file: /var/www/vendor/symfony/cache/Marshaller/DefaultMarshaller.php:72

Fatal error later when reading media path

var/log/prod-2026-02-12.log:

Uncaught PHP Exception Twig\Error\RuntimeError:
"An exception has been thrown during the rendering of a template
("Typed property JoliCode\MediaBundle\Model\Media::$path must not be accessed before initialization")
in "components/ServiceCard.html.twig" at line 95."
previous:
Error: Typed property JoliCode\MediaBundle\Model\Media::$path must not be accessed before initialization
at /var/www/vendor/jolicode/media-bundle/src/Model/Media.php:151

SLC statistics confirm cache hits

var/log/slc_prod-2026-02-12.log:

"delta":{"total":{"hit":4,"miss":0,"put":0}, ... "entity.service":{"hit":3,"miss":0,"put":0}, ...}

So the issue appears on reads from second-level cache.

Additional observation

In vendor/jolicode/media-bundle/src/Model/Media.php, I see:

public function __serialize(): array
{
    return [$this->path, $this->storage->getLibrary()->getName()];
}

But I do not see a corresponding __unserialize() implementation to restore object state ($path, $storage, etc.).

Could this be the root cause for cache-unserialized Media objects ending up partially initialized?

Thank you in advance

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions