diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cd863c7a55..5fa2fe227d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -220,10 +220,17 @@ jobs: tools: pecl, composer extensions: intl, bcmath, curl, openssl, mbstring, pdo_sqlite, mongodb ini-values: memory_limit=-1 - - name: Run ${{ matrix.component }} tests + - name: Run ${{ matrix.component }} install working-directory: src/${{ matrix.component }} run: | composer update + - name: PHP version tweaks + if: matrix.component == 'api-platform/metadata' && matrix.php != '8.1' + run: composer require symfony/type-info + working-directory: 'src/Metadata' + - name: Run ${{ matrix.component }} tests + working-directory: src/${{ matrix.component }} + run: | mkdir -p build/logs/phpunit vendor/bin/phpunit --log-junit "build/logs/phpunit/junit.xml" ${{ matrix.coverage && '--coverage-clover build/logs/phpunit/clover.xml' || '' }} - name: Upload test artifacts diff --git a/composer.json b/composer.json index f7476083143..eeb1e79342b 100644 --- a/composer.json +++ b/composer.json @@ -150,7 +150,8 @@ "allow-plugins": { "composer/package-versions-deprecated": true, "phpstan/extension-installer": true, - "php-http/discovery": true + "php-http/discovery": true, + "soyuka/pmu": true } }, "extra": { diff --git a/src/Doctrine/Odm/PropertyInfo/DoctrineExtractor.php b/src/Doctrine/Odm/PropertyInfo/DoctrineExtractor.php index 93ce4a4d5d0..7967a6f2192 100644 --- a/src/Doctrine/Odm/PropertyInfo/DoctrineExtractor.php +++ b/src/Doctrine/Odm/PropertyInfo/DoctrineExtractor.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Doctrine\Odm\PropertyInfo; +use ApiPlatform\Metadata\Util\PropertyInfoToTypeInfoHelper; use Doctrine\Common\Collections\Collection; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as MongoDbClassMetadata; use Doctrine\ODM\MongoDB\Types\Type as MongoDbType; @@ -22,7 +23,8 @@ use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; -use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\PropertyInfo\Type as LegacyType; +use Symfony\Component\TypeInfo\Type; /** * Extracts data using Doctrine MongoDB ODM metadata. @@ -53,9 +55,9 @@ public function getProperties($class, array $context = []): ?array /** * {@inheritdoc} * - * @return Type[]|null + * @return LegacyType[]|null */ - public function getTypes($class, $property, array $context = []): ?array + public function getTypes(string $class, string $property, array $context = []): ?array { if (null === $metadata = $this->getMetadata($class)) { return null; @@ -72,19 +74,19 @@ public function getTypes($class, $property, array $context = []): ?array if ($metadata->isSingleValuedAssociation($property)) { $nullable = $metadata instanceof MongoDbClassMetadata && $metadata->isNullable($property); - return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $class)]; + return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, $class)]; } - $collectionKeyType = Type::BUILTIN_TYPE_INT; + $collectionKeyType = LegacyType::BUILTIN_TYPE_INT; return [ - new Type( - Type::BUILTIN_TYPE_OBJECT, + new LegacyType( + LegacyType::BUILTIN_TYPE_OBJECT, false, Collection::class, true, - new Type($collectionKeyType), - new Type(Type::BUILTIN_TYPE_OBJECT, false, $class) + new LegacyType($collectionKeyType), + new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, $class) ), ]; } @@ -94,18 +96,18 @@ public function getTypes($class, $property, array $context = []): ?array $nullable = $metadata instanceof MongoDbClassMetadata && $metadata->isNullable($property); $enumType = null; if (null !== $enumClass = $metadata instanceof MongoDbClassMetadata ? $metadata->getFieldMapping($property)['enumType'] ?? null : null) { - $enumType = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $enumClass); + $enumType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, $enumClass); } switch ($typeOfField) { case MongoDbType::DATE: - return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, \DateTime::class)]; + return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, \DateTime::class)]; case MongoDbType::DATE_IMMUTABLE: - return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, \DateTimeImmutable::class)]; + return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, \DateTimeImmutable::class)]; case MongoDbType::HASH: - return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; + return [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; case MongoDbType::COLLECTION: - return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT))]; + return [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT))]; case MongoDbType::INT: case MongoDbType::STRING: if ($enumType) { @@ -115,7 +117,7 @@ public function getTypes($class, $property, array $context = []): ?array $builtinType = $this->getPhpType($typeOfField); - return $builtinType ? [new Type($builtinType, $nullable)] : null; + return $builtinType ? [new LegacyType($builtinType, $nullable)] : null; } return null; @@ -154,16 +156,21 @@ private function getMetadata(string $class): ?ClassMetadata } } + public function getType(string $class, string $property, array $context = []): ?Type + { + return PropertyInfoToTypeInfoHelper::convertLegacyTypesToType($this->getTypes($class, $property, $context)); + } + /** * Gets the corresponding built-in PHP type. */ private function getPhpType(string $doctrineType): ?string { return match ($doctrineType) { - MongoDbType::INTEGER, MongoDbType::INT, MongoDbType::INTID, MongoDbType::KEY => Type::BUILTIN_TYPE_INT, - MongoDbType::FLOAT => Type::BUILTIN_TYPE_FLOAT, - MongoDbType::STRING, MongoDbType::ID, MongoDbType::OBJECTID, MongoDbType::TIMESTAMP, MongoDbType::BINDATA, MongoDbType::BINDATABYTEARRAY, MongoDbType::BINDATACUSTOM, MongoDbType::BINDATAFUNC, MongoDbType::BINDATAMD5, MongoDbType::BINDATAUUID, MongoDbType::BINDATAUUIDRFC4122 => Type::BUILTIN_TYPE_STRING, - MongoDbType::BOOLEAN, MongoDbType::BOOL => Type::BUILTIN_TYPE_BOOL, + MongoDbType::INTEGER, MongoDbType::INT, MongoDbType::INTID, MongoDbType::KEY => LegacyType::BUILTIN_TYPE_INT, + MongoDbType::FLOAT => LegacyType::BUILTIN_TYPE_FLOAT, + MongoDbType::STRING, MongoDbType::ID, MongoDbType::OBJECTID, MongoDbType::TIMESTAMP, MongoDbType::BINDATA, MongoDbType::BINDATABYTEARRAY, MongoDbType::BINDATACUSTOM, MongoDbType::BINDATAFUNC, MongoDbType::BINDATAMD5, MongoDbType::BINDATAUUID, MongoDbType::BINDATAUUIDRFC4122 => LegacyType::BUILTIN_TYPE_STRING, + MongoDbType::BOOLEAN, MongoDbType::BOOL => LegacyType::BUILTIN_TYPE_BOOL, default => null, }; } diff --git a/src/GraphQl/Serializer/SerializerContextBuilder.php b/src/GraphQl/Serializer/SerializerContextBuilder.php index 7726167102c..4dad4812b34 100644 --- a/src/GraphQl/Serializer/SerializerContextBuilder.php +++ b/src/GraphQl/Serializer/SerializerContextBuilder.php @@ -18,6 +18,7 @@ use ApiPlatform\Metadata\GraphQl\Subscription; use GraphQL\Type\Definition\ResolveInfo; use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; +use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; /** @@ -101,7 +102,7 @@ private function denormalizePropertyName(string $property, ?string $resourceClas if (null === $this->nameConverter) { return $property; } - if ($this->nameConverter instanceof AdvancedNameConverterInterface) { + if ($this->nameConverter instanceof AdvancedNameConverterInterface || $this->nameConverter instanceof MetadataAwareNameConverter) { return $this->nameConverter->denormalize($property, $resourceClass, null, $context); } diff --git a/src/GraphQl/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php b/src/GraphQl/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php index 3c4efde93d9..74ce9c165ae 100644 --- a/src/GraphQl/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php +++ b/src/GraphQl/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php @@ -13,6 +13,7 @@ namespace ApiPlatform\GraphQl\Tests\Fixtures\Serializer\NameConverter; +use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -20,7 +21,7 @@ * Custom converter that will only convert a property named "nameConverted" * with the same logic as Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter. */ -class CustomConverter implements NameConverterInterface +class CustomConverter implements AdvancedNameConverterInterface { private NameConverterInterface $nameConverter; @@ -29,12 +30,12 @@ public function __construct() $this->nameConverter = new CamelCaseToSnakeCaseNameConverter(); } - public function normalize(string $propertyName): string + public function normalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { return 'nameConverted' === $propertyName ? $this->nameConverter->normalize($propertyName) : $propertyName; } - public function denormalize(string $propertyName): string + public function denormalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { return 'name_converted' === $propertyName ? $this->nameConverter->denormalize($propertyName) : $propertyName; } diff --git a/src/GraphQl/Type/FieldsBuilder.php b/src/GraphQl/Type/FieldsBuilder.php index 5aba2bde5b1..445cfd8d628 100644 --- a/src/GraphQl/Type/FieldsBuilder.php +++ b/src/GraphQl/Type/FieldsBuilder.php @@ -38,6 +38,7 @@ use Symfony\Component\Config\Definition\Exception\InvalidTypeException; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; +use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; /** @@ -572,7 +573,7 @@ private function normalizePropertyName(string $property, string $resourceClass): if (null === $this->nameConverter) { return $property; } - if ($this->nameConverter instanceof AdvancedNameConverterInterface) { + if ($this->nameConverter instanceof AdvancedNameConverterInterface || $this->nameConverter instanceof MetadataAwareNameConverter) { return $this->nameConverter->normalize($property, $resourceClass); } diff --git a/src/Metadata/Tests/Util/PropertyInfoToTypeInfoHelperTest.php b/src/Metadata/Tests/Util/PropertyInfoToTypeInfoHelperTest.php new file mode 100644 index 00000000000..a39d6046526 --- /dev/null +++ b/src/Metadata/Tests/Util/PropertyInfoToTypeInfoHelperTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ApiPlatform\Metadata\Tests\Util; + +use ApiPlatform\Metadata\Util\PropertyInfoToTypeInfoHelper; +use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyInfo\Type as LegacyType; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\TypeIdentifier; + +/** + * @group legacy + */ +class PropertyInfoToTypeInfoHelperTest extends TestCase +{ + /** + * @dataProvider convertLegacyTypesToTypeDataProvider + * + * @param list|null $legacyTypes + */ + public function testConvertLegacyTypesToType(?Type $type, ?array $legacyTypes): void + { + if (!class_exists(Type::class)) { + $this->markTestSkipped('symfony/type-info requires PHP > 8.2'); + } + + $this->assertEquals($type, PropertyInfoToTypeInfoHelper::convertLegacyTypesToType($legacyTypes)); + } + + /** + * @return iterable|null}> + */ + public function convertLegacyTypesToTypeDataProvider(): iterable + { + if (!class_exists(Type::class)) { + return; + } + + yield [null, null]; + yield [Type::null(), [new LegacyType('null')]]; + // yield [Type::void(), [new LegacyType('void')]]; + yield [Type::int(), [new LegacyType('int')]]; + yield [Type::object(\stdClass::class), [new LegacyType('object', false, \stdClass::class)]]; + yield [ + Type::generic(Type::object('Foo'), Type::string(), Type::int()), // @phpstan-ignore-line + [new LegacyType('object', false, 'Foo', false, [new LegacyType('string')], new LegacyType('int'))], + ]; + yield [Type::nullable(Type::int()), [new LegacyType('int', true)]]; // @phpstan-ignore-line + yield [Type::union(Type::int(), Type::string()), [new LegacyType('int'), new LegacyType('string')]]; + yield [ + Type::union(Type::int(), Type::string(), Type::null()), + [new LegacyType('int', true), new LegacyType('string', true)], + ]; + + $type = Type::collection(Type::builtin(TypeIdentifier::ARRAY), Type::int(), Type::string()); // @phpstan-ignore-line + yield [$type, [new LegacyType('array', false, null, true, [new LegacyType('string')], new LegacyType('int'))]]; + } +} diff --git a/src/Metadata/Util/PropertyInfoToTypeInfoHelper.php b/src/Metadata/Util/PropertyInfoToTypeInfoHelper.php new file mode 100644 index 00000000000..ba3f0867df4 --- /dev/null +++ b/src/Metadata/Util/PropertyInfoToTypeInfoHelper.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Metadata\Util; + +use Symfony\Component\PropertyInfo\Type as LegacyType; +use Symfony\Component\TypeInfo\Exception\InvalidArgumentException; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\Type\BuiltinType; +use Symfony\Component\TypeInfo\Type\UnionType; +use Symfony\Component\TypeInfo\TypeIdentifier; + +/** + * A helper about PropertyInfo Type conversion. + * + * @see https://github.com/mtarld/symfony/commits/backup/chore/deprecate-property-info-type/ + * + * @author Mathias Arlaud + * + * @internal + */ +final class PropertyInfoToTypeInfoHelper +{ + /** + * Converts a {@see LegacyType} to what is should have been in the "symfony/type-info" component. + * + * @param list|null $legacyTypes + */ + public static function convertLegacyTypesToType(?array $legacyTypes): ?Type + { + if (!$legacyTypes) { + return null; + } + + $types = []; + $nullable = false; + + foreach (array_map(self::convertLegacyTypeToType(...), $legacyTypes) as $type) { + if ($type->isNullable()) { + $nullable = true; + + if ($type instanceof BuiltinType && TypeIdentifier::NULL === $type->getTypeIdentifier()) { + continue; + } + + $type = self::unwrapNullableType($type); + } + + if ($type instanceof UnionType) { + $types = [$types, ...$type->getTypes()]; + + continue; + } + + $types[] = $type; + } + + if ($nullable && [] === $types) { + return Type::null(); + } + + $type = \count($types) > 1 ? Type::union(...$types) : $types[0]; + if ($nullable) { + $type = Type::nullable($type); + } + + return $type; + } + + /** + * @param list $collectionKeyTypes + * @param list $collectionValueTypes + */ + public static function createTypeFromLegacyValues(string $builtinType, bool $nullable, ?string $class, bool $collection, array $collectionKeyTypes, array $collectionValueTypes): Type + { + $variableTypes = []; + + if ($collectionKeyTypes) { + $collectionKeyTypes = array_unique(array_map(self::convertLegacyTypeToType(...), $collectionKeyTypes)); + $variableTypes[] = \count($collectionKeyTypes) > 1 ? Type::union(...$collectionKeyTypes) : $collectionKeyTypes[0]; + } + + if ($collectionValueTypes) { + if (!$collectionKeyTypes) { + $variableTypes[] = \is_array($collectionKeyTypes) ? Type::mixed() : Type::union(Type::int(), Type::string()); // @phpstan-ignore-line + } + + $collectionValueTypes = array_unique(array_map(self::convertLegacyTypeToType(...), $collectionValueTypes)); + $variableTypes[] = \count($collectionValueTypes) > 1 ? Type::union(...$collectionValueTypes) : $collectionValueTypes[0]; + } + + if ($collectionKeyTypes && !$collectionValueTypes) { + $variableTypes[] = Type::mixed(); + } + + try { + $type = null !== $class ? Type::object($class) : Type::builtin(TypeIdentifier::from($builtinType)); + } catch (\ValueError) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid PHP type.', $builtinType)); + } + + if (\count($variableTypes)) { + $type = Type::generic($type, ...$variableTypes); + } + + if ($collection) { + $type = Type::collection($type); + } + + if ($nullable && !$type->isNullable()) { + $type = Type::nullable($type); + } + + return $type; + } + + public static function unwrapNullableType(Type $type): Type + { + if (!$type instanceof UnionType) { + return $type; + } + + return $type->asNonNullable(); + } + + /** + * Recursive method that converts {@see LegacyType} to its related {@see Type}. + */ + private static function convertLegacyTypeToType(LegacyType $legacyType): Type + { + return self::createTypeFromLegacyValues( + $legacyType->getBuiltinType(), + $legacyType->isNullable(), + $legacyType->getClassName(), + $legacyType->isCollection(), + $legacyType->getCollectionKeyTypes(), + $legacyType->getCollectionValueTypes(), + ); + } +} diff --git a/src/Serializer/AbstractConstraintViolationListNormalizer.php b/src/Serializer/AbstractConstraintViolationListNormalizer.php index af255895579..9e182fa1a0a 100644 --- a/src/Serializer/AbstractConstraintViolationListNormalizer.php +++ b/src/Serializer/AbstractConstraintViolationListNormalizer.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Serializer; use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; +use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Serializer; @@ -76,7 +77,7 @@ protected function getMessagesAndViolations(ConstraintViolationListInterface $co foreach ($constraintViolationList as $violation) { $class = \is_object($root = $violation->getRoot()) ? $root::class : null; - if ($this->nameConverter instanceof AdvancedNameConverterInterface) { + if ($this->nameConverter instanceof AdvancedNameConverterInterface || $this->nameConverter instanceof MetadataAwareNameConverter) { $propertyPath = $this->nameConverter->normalize($violation->getPropertyPath(), $class, static::FORMAT); } elseif ($this->nameConverter instanceof NameConverterInterface) { $propertyPath = $this->nameConverter->normalize($violation->getPropertyPath()); diff --git a/src/Serializer/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php b/src/Serializer/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php index 4e940ffa6dd..c2baa0be9b9 100644 --- a/src/Serializer/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php +++ b/src/Serializer/Tests/Fixtures/Serializer/NameConverter/CustomConverter.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Serializer\Tests\Fixtures\Serializer\NameConverter; +use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -20,7 +21,7 @@ * Custom converter that will only convert a property named "nameConverted" * with the same logic as Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter. */ -class CustomConverter implements NameConverterInterface +class CustomConverter implements AdvancedNameConverterInterface { private NameConverterInterface $nameConverter; @@ -29,12 +30,12 @@ public function __construct() $this->nameConverter = new CamelCaseToSnakeCaseNameConverter(); } - public function normalize(string $propertyName): string + public function normalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { return 'nameConverted' === $propertyName ? $this->nameConverter->normalize($propertyName) : $propertyName; } - public function denormalize(string $propertyName): string + public function denormalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { return 'name_converted' === $propertyName ? $this->nameConverter->denormalize($propertyName) : $propertyName; } diff --git a/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php index cd8ee370fc4..4fc3fb44b54 100644 --- a/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php +++ b/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php @@ -52,12 +52,12 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Finder\Finder; use Symfony\Component\HttpClient\ScopingHttpClient; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Validator\Validator\ValidatorInterface; diff --git a/src/Symfony/Validator/Serializer/ValidationExceptionNormalizer.php b/src/Symfony/Validator/Serializer/ValidationExceptionNormalizer.php index d45d32a37fa..07adeae4bff 100644 --- a/src/Symfony/Validator/Serializer/ValidationExceptionNormalizer.php +++ b/src/Symfony/Validator/Serializer/ValidationExceptionNormalizer.php @@ -16,6 +16,7 @@ use ApiPlatform\Serializer\CacheableSupportsMethodInterface; use ApiPlatform\Symfony\Validator\Exception\ValidationException; use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; +use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Serializer; @@ -32,7 +33,7 @@ public function normalize(mixed $object, ?string $format = null, array $context foreach ($object->getConstraintViolationList() as $violation) { $class = \is_object($root = $violation->getRoot()) ? $root::class : null; - if ($this->nameConverter instanceof AdvancedNameConverterInterface) { + if ($this->nameConverter instanceof AdvancedNameConverterInterface || $this->nameConverter instanceof MetadataAwareNameConverter) { $propertyPath = $this->nameConverter->normalize($violation->getPropertyPath(), $class, $format); } elseif ($this->nameConverter instanceof NameConverterInterface) { $propertyPath = $this->nameConverter->normalize($violation->getPropertyPath()); diff --git a/tests/.ignored-deprecations b/tests/.ignored-deprecations index c920d522474..0b259f3a524 100644 --- a/tests/.ignored-deprecations +++ b/tests/.ignored-deprecations @@ -9,3 +9,7 @@ %Do the same.*Doctrine\\Bundle\\MongoDBBundle% %The "Symfony\\Bundle\\MakerBundle\\Maker\\MakeAuthenticator" class is deprecated, use any of the Security\\Make\* commands instead% +# We don't care as we use this in our tests and the default value is just fine +%Since symfony/validator 7.1: Not passing a value for the "requireTld" option to the Url constraint is deprecated. Its default value will change to "true".% +# https://github.com/FriendsOfBehat/SymfonyExtension/pull/212 +%The "Symfony\\Component\\HttpKernel\\DependencyInjection\\Extension" class is considered internal since Symfony 7.1, to be deprecated in 8.1; use Symfony\\Component\\DependencyInjection\\Extension\\Extension instead. It may change without further notice. You should not use it from "FriendsOfBehat\\SymfonyExtension\\Bundle\\DependencyInjection\\FriendsOfBehatSymfonyExtensionExtension".% diff --git a/tests/Fixtures/TestBundle/Serializer/NameConverter/CustomConverter.php b/tests/Fixtures/TestBundle/Serializer/NameConverter/CustomConverter.php index e2837b7228c..6421819b03c 100644 --- a/tests/Fixtures/TestBundle/Serializer/NameConverter/CustomConverter.php +++ b/tests/Fixtures/TestBundle/Serializer/NameConverter/CustomConverter.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Tests\Fixtures\TestBundle\Serializer\NameConverter; +use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -20,7 +21,7 @@ * Custom converter that will only convert a property named "nameConverted" * with the same logic as Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter. */ -class CustomConverter implements NameConverterInterface +class CustomConverter implements AdvancedNameConverterInterface { private NameConverterInterface $nameConverter; @@ -29,12 +30,12 @@ public function __construct() $this->nameConverter = new CamelCaseToSnakeCaseNameConverter(); } - public function normalize(string $propertyName): string + public function normalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { return 'nameConverted' === $propertyName ? $this->nameConverter->normalize($propertyName) : $propertyName; } - public function denormalize(string $propertyName): string + public function denormalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { return 'name_converted' === $propertyName ? $this->nameConverter->denormalize($propertyName) : $propertyName; }