Skip to content

[Bug]: call_user_func() fails when resolving Assert\Choice callback on abstract classes #2715

@philiprabbett

Description

@philiprabbett

Version

5.9.2

Description

When using Assert\Choice with a callback parameter on a property in an abstract class, Nelmio API Doc Bundle throws a fatal error during documentation generation.

Error Message:

call_user_func(): Argument #1 ($callback) must be a valid callback, cannot call abstract method App\Entity\AbstractAuthor::getGenres()

Reproduction Steps:

  1. Create an abstract entity class with an Assert\Choice constraint using a callback:
namespace App\Entity;

use Symfony\Component\Validator\Constraints as Assert;

abstract class AbstractAuthor
{
    #[Assert\Choice(callback: 'getGenres')]
    protected string $genre;

    public static function getGenres(): array
    {
        return ['fiction', 'non-fiction'];
    }
}
  1. Run Nelmio API Doc Bundle's documentation generation (e.g., php bin/console api:openapi:export)
  2. The error occurs in the callback resolution logic

Root Cause:

The issue is in the callback resolution code that attempts to invoke call_user_func() on an abstract class:

\call_user_func(\is_array($choice->callback) ? $choice->callback : [$reflection->class, $choice->callback]);

Since $reflection->class is an abstract class, call_user_func() cannot instantiate it to invoke the method.

Expected Behavior:

Nelmio should gracefully handle abstract classes by either:

  • Skipping callback resolution for abstract classes
  • Attempting to resolve the callback on concrete subclasses
  • Falling back to a sensible default or warning

Actual Behavior:

A fatal error is thrown, preventing documentation generation.

Workaround:

Currently, the only workaround is to use hardcoded choices instead of a callback or move the constraint to concrete subclasses:

#[Assert\Choice(choices: ['fiction', 'non-fiction'])]
protected string $genre;

or

abstract class AbstractAuthor
{
    protected string $genre;
}

class ConcreteAuthor extends AbstractAuthor
{
    #[Assert\Choice(callback: 'getGenres')]
    protected string $genre;

    public static function getGenres(): array
    {
        return ['fiction', 'non-fiction'];
    }
}

JSON OpenApi

N/A

Additional context

This issue affects any abstract entity class using Assert\Choice with callbacks, which is a common pattern when sharing validation constraints across multiple concrete subclasses.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions