Problem/Motivation

When using drupal/menu_normalizer 2.1.0 with Drupal 11 and Symfony 7, a PHP Fatal error occurs on cache rebuild (drush cr) because the normalize() method signature in MenuLinkNormalizer and MenuLinkTreeNormalizer is incompatible with the updated NormalizerInterface in Symfony 7.

PHP Fatal error: Declaration of Drupal\menu_normalizer\Normalizer\MenuLinkNormalizer::normalize($object, $format = null, array $context = []) must be compatible with Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize(mixed $data, ?string $format = null, array $context = []): ArrayObject|array|string|int|float|bool|null in .../menu_normalizer/src/Normalizer/MenuLinkNormalizer.php on line 23

Steps to reproduce

  1. Install Drupal 11 with Symfony 7
  2. Enable menu_normalizer 2.1.0
  3. Run drush cr
  4. PHP Fatal error occurs

Proposed resolution

Update the normalize() method signature in both normalizer classes to match Symfony 7's NormalizerInterface:

public function normalize(mixed $object, ?string $format = NULL, array $context = []): array|\ArrayObject|string|int|float|bool|null {

Files to update:

  • src/Normalizer/MenuLinkNormalizer.php
  • src/Normalizer/MenuLinkTreeNormalizer.php

Remaining tasks

Apply fix and release a new version compatible with Drupal 11 / Symfony 7.

User interface changes

None.

API changes

None.

Data model changes

None.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

ctribouillard created an issue. See original summary.

brian.seek made their first commit to this issue’s fork.

brian.seek’s picture

Status: Active » Needs review

Added a MR to update the normalize method signature as suggested.

brian.seek’s picture

Status: Needs review » Needs work
brian.seek’s picture

Status: Needs work » Needs review

batigolix made their first commit to this issue’s fork.

batigolix’s picture

Issue tags: +finalist-sprint

I opened MR !9 as an alternative to MR !7 by @brian.seek.

It contains the same Symfony 7 fix (normalize() signature + getSupportedTypes()), minus the meta_datametadata rename — that belongs in a separate issue. Also updated tests to use MenuLinkMock::createMock() (renamed in D11).

fabianderijk made their first commit to this issue’s fork.

fabianderijk’s picture

Status: Needs review » Reviewed & tested by the community

Confirmed the fatal on 2.1.0 and confirmed MR !9 resolves it. One note on the reproduction steps: drush cr on its own does not trigger the fatal, because RegisterSerializationClassesCompilerPass only collects tagged service IDs and never loads the classes. The fatal appears as soon as the serializer service is actually instantiated — so it will surface during a cache rebuild only if a module such as jsonapi or rest instantiates it. A minimal reproduction is drush php:eval '\Drupal::service("serializer");' with serialization and menu_normalizer enabled.

I pushed two follow-up commits to the MR branch:

  1. Remove the obsolete $supportedInterfaceOrClass property. It was read by NormalizerBase up to Drupal 10.1; the base class no longer references it (grep -rn supportedInterfaceOrClass core/ returns nothing on 11.4), so it is dead code now that getSupportedTypes() is implemented.
  2. Narrow the core requirement to ^10 || ^11 in both menu_normalizer.info.yml and composer.json. The new signature types $format as ?string, but Drupal 9 ships Symfony 4.4, whose NormalizerInterface::normalize($object, $format = null, array $context = []) leaves every parameter untyped — narrowing a parameter type there is a fatal error. Drupal 10.0 ships Symfony 6.2, which already declares normalize(mixed $object, string $format = null, array $context = []), so the MR is compatible from Drupal 10 onwards. Keeping ^9 in the metadata would advertise support that the code no longer has.

Results after those two commits: PHPUnit 4 tests / 23 assertions pass, phpcs --standard=Drupal,DrupalPractice reports zero issues, drush cr succeeds on a cold cache (cache tables truncated), the serializer instantiates, and normalizing a real MenuLinkTreeElement from the admin menu produces the expected nested structure. No new entries in the PHP watchdog log.

Two pre-existing problems I noticed while testing, both out of scope for this issue — I can open separate issues if wanted:

- delete_route and edit_route are not passed through $this->serializer->normalize(), so raw Url objects end up in the returned array and serialize to "edit_route":{} in JSON.
- menu_normalizer.info.yml does not declare dependencies: - drupal:serialization, even though both normalizers extend a class from that module. drush pmu serialization succeeds while menu_normalizer stays enabled.

With the two follow-up commits included, this is RTBC from my side.

batigolix’s picture

Status: Reviewed & tested by the community » Fixed

Thanks. I will merge this

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • batigolix committed e904a006 on 2.x
    fix: #3584180 [Drupal 11 / Symfony 7] Fatal error: MenuLinkNormalizer...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.