Repository: symfony/symfony
File: src/Symfony/Component/ObjectMapper/ObjectMapper.php (PHP)

Code before:
                continue;
            }

            $mapTo = $mapAttribute;
        }

        return $mapTo;
    }

    private function applyTransforms(Mapping $map, mixed $value, object $source, ?object $target): mixed
    {
        if (!$transforms = $map->transform) {
            return $value;
        }

        if (\is_callable($transforms)) {
            $transforms = [$transforms];
        } elseif (!\is_array($transforms)) {
            $transforms = [$transforms];
        }

        foreach ($transforms as $transform) {
            if ($fn = $this->getCallable($transform, $this->transformCallableLocator)) {
                $value = $this->call($fn, $value, $source, $target);
            } else {
                throw new MappingTransformException(\sprintf('Transform "%s" is not a valid callable.', \is_string($transform) ? $transform : get_debug_type($transform)));
            }
        }

        return $value;
    }

    /**
     * @param (string|callable(mixed $value, object $object): mixed) $fn
     */
    private function getCallable(string|callable $fn, ?ContainerInterface $locator = null): ?callable
    {
        if (\is_callable($fn)) {
            return $fn;
        }

        if ($locator?->has($fn)) {
            return $locator->get($fn);
        }

        return null;
    }

    /**
     * @return ?\ReflectionClass<object|T>
     */

Review comment (suggestion):
lets mention in the exception, that this class is invalid because it does not implement `TransformCallableInterface`? this is more actionable for the user

Code after:
    private function getMapTarget(array $metadata, mixed $value, object $source, ?object $target): ?Mapping
    {
        $mapTo = null;
        foreach ($metadata as $mapAttribute) {
            if (($if = $mapAttribute->if) && ($fn = $this->getCallable($if, $this->conditionCallableLocator, ConditionCallableInterface::class)) && !$this->call($fn, $value, $source, $target)) {
                continue;
            }

            $mapTo = $mapAttribute;
        }

        return $mapTo;
    }

    private function applyTransforms(Mapping $map, mixed $value, object $source, ?object $target): mixed
    {
        if (!$transforms = $map->transform) {
            return $value;
        }

        if (\is_callable($transforms)) {
            $transforms = [$transforms];
        } elseif (!\is_array($transforms)) {
            $transforms = [$transforms];
        }

        foreach ($transforms as $transform) {
            $fn = $this->getCallable($transform, $this->transformCallableLocator, TransformCallableInterface::class);
            $value = $this->call($fn, $value, $source, $target);
        }

        return $value;
    }

    /**
     * @param (string|callable(mixed $value, object $object): mixed) $fn
     * @param class-string|null                                      $expectedInterface
     */
    private function getCallable(string|callable $fn, ?ContainerInterface $locator = null, ?string $expectedInterface = null): callable
    {
        if (\is_callable($fn)) {
            if ($expectedInterface && \is_object($fn) && !$fn instanceof $expectedInterface) {
                throw new NoSuchCallableException(\sprintf('"%s" is not a valid callable. Make sure it implements "%s".', get_debug_type($fn), $expectedInterface));
            }

            return $fn;
        }

        if ($locator?->has($fn)) {
            $callable = $locator->get($fn);