Skip to content

[FrameworkBundle][Validator] Remove remaining deprecations #51455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion UPGRADE-7.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ FrameworkBundle
* Make the `framework.uid.default_uuid_version` config option default to `7`
* Make the `framework.uid.time_based_uuid_version` config option default to `7`
* Make the `framework.validation.email_validation_mode` config option default to `html5`
* Remove the `framework.validation.enable_annotations` config option, use `framework.validation.enable_attributes` instead
* Remove the `framework.serializer.enable_annotations` config option, use `framework.serializer.enable_attributes` instead

HttpFoundation
--------------
Expand Down Expand Up @@ -472,9 +474,11 @@ Validator
* Remove `VALIDATION_MODE_LOOSE` from `Email` constraint, use `VALIDATION_MODE_HTML5` instead
* Remove constraint `ExpressionLanguageSyntax`, use `ExpressionSyntax` instead
* Remove Doctrine annotations support in favor of native attributes
* Remove the annotation reader parameter from the constructor signature of `AnnotationLoader`
* Remove `ValidatorBuilder::setDoctrineAnnotationReader()`
* Remove `ValidatorBuilder::addDefaultDoctrineAnnotationReader()`
* Remove `ValidatorBuilder::enableAnnotationMapping()`, use `ValidatorBuilder::enableAttributeMapping()` instead
* Remove `ValidatorBuilder::disableAnnotationMapping()`, use `ValidatorBuilder::disableAttributeMapping()` instead
* Remove `AnnotationLoader`, use `AttributeLoader` instead

VarDumper
---------
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ CHANGELOG
* Make the `framework.uid.default_uuid_version` config option default to `7`
* Make the `framework.uid.time_based_uuid_version` config option default to `7`
* Make the `framework.validation.email_validation_mode` config option default to `html5`
* Remove the `framework.validation.enable_annotations` config option, use `framework.validation.enable_attributes` instead
* Remove the `framework.serializer.enable_annotations` config option, use `framework.serializer.enable_attributes` instead

6.4
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,6 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e
->{$enableIfStandalone('symfony/validator', Validation::class)}()
->children()
->scalarNode('cache')->end()
->booleanNode('enable_annotations')->end()
->booleanNode('enable_attributes')->{!class_exists(FullStack::class) ? 'defaultTrue' : 'defaultFalse'}()->end()
->arrayNode('static_method')
->defaultValue(['loadValidatorMetadata'])
Expand Down Expand Up @@ -1090,24 +1089,9 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e
$rootNode
->children()
->arrayNode('serializer')
->validate()
->always(function ($v) {
if (isset($v['enable_annotations'])) {
trigger_deprecation('symfony/framework-bundle', '6.4', 'Option "enable_annotations" at "framework.serializer" is deprecated. Use the "enable_attributes" option instead.');

if (!isset($v['enable_attributes'])) {
$v['enable_attributes'] = $v['enable_annotations'];
} else {
throw new LogicException('The "enable_annotations" and "enable_attributes" options at path "framework.serializer" must not be both set. Only the "enable_attributes" option must be used.');
}
}

return $v;
})->end()
->info('serializer configuration')
->{$enableIfStandalone('symfony/serializer', Serializer::class)}()
->children()
->booleanNode('enable_annotations')->end()
->booleanNode('enable_attributes')->{!class_exists(FullStack::class) ? 'defaultTrue' : 'defaultFalse'}()->end()
->scalarNode('name_converter')->end()
->scalarNode('circular_reference_handler')->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@

<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="cache" type="xsd:string" />
<xsd:attribute name="enable-annotations" type="xsd:boolean" />
<xsd:attribute name="enable-attributes" type="xsd:boolean" />
<xsd:attribute name="static-method" type="xsd:boolean" />
<xsd:attribute name="translation-domain" type="xsd:string" />
Expand Down Expand Up @@ -320,7 +319,6 @@
<xsd:element name="default-context" type="metadata" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="enable-annotations" type="xsd:boolean" />
<xsd:attribute name="enable-attributes" type="xsd:boolean" />
<xsd:attribute name="name-converter" type="xsd:string" />
<xsd:attribute name="circular-reference-handler" type="xsd:string" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public function testDenormalizeWithNestedAttributesWithoutMetadata()

public function testDenormalizeWithSnakeCaseNestedAttributes()
{
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$factory = new ClassMetadataFactory(new AnnotationLoader());
$normalizer = new ObjectNormalizer($factory, new CamelCaseToSnakeCaseNameConverter());
$data = [
'one' => [
Expand All @@ -155,7 +155,7 @@ public function testDenormalizeWithSnakeCaseNestedAttributes()

public function testNormalizeWithSnakeCaseNestedAttributes()
{
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$factory = new ClassMetadataFactory(new AnnotationLoader());
$normalizer = new ObjectNormalizer($factory, new CamelCaseToSnakeCaseNameConverter());
$dummy = new SnakeCaseNestedDummy();
$dummy->fooBar = 'fooBar';
Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ CHANGELOG
* Remove the annotation reader parameter from the constructor signature of `AnnotationLoader`
* Remove `ValidatorBuilder::setDoctrineAnnotationReader()`
* Remove `ValidatorBuilder::addDefaultDoctrineAnnotationReader()`
* Remove `ValidatorBuilder::enableAnnotationMapping()`, use `ValidatorBuilder::enableAttributeMapping()` instead
* Remove `ValidatorBuilder::disableAnnotationMapping()`, use `ValidatorBuilder::disableAttributeMapping()` instead
* Remove `AnnotationLoader`, use `AttributeLoader` instead

6.4
---
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Validator/Constraints/Callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Callback extends Constraint

public function __construct(array|string|callable $callback = null, array $groups = null, mixed $payload = null, array $options = [])
{
// Invocation through annotations with an array parameter only
// Invocation through attributes with an array parameter only
if (\is_array($callback) && 1 === \count($callback) && isset($callback['value'])) {
$callback = $callback['value'];
}
Expand Down
3 changes: 0 additions & 3 deletions src/Symfony/Component/Validator/Constraints/When.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\LogicException;

/**
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
*/
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class When extends Composite
{
Expand Down

This file was deleted.

74 changes: 71 additions & 3 deletions src/Symfony/Component/Validator/Mapping/Loader/AttributeLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,85 @@

namespace Symfony\Component\Validator\Mapping\Loader;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\GroupSequenceProvider;
use Symfony\Component\Validator\Exception\MappingException;
use Symfony\Component\Validator\Mapping\ClassMetadata;

/**
* Loads validation metadata using PHP attributes.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Alexander M. Turek <me@derrabus.de>
* @author Alexandre Daubois <alex.daubois@gmail.com>
*/
class AttributeLoader extends AnnotationLoader
class AttributeLoader implements LoaderInterface
{
public function __construct()
public function loadClassMetadata(ClassMetadata $metadata): bool
{
$reflClass = $metadata->getReflectionClass();
$className = $reflClass->name;
$success = false;

foreach ($this->getAttributes($reflClass) as $constraint) {
if ($constraint instanceof GroupSequence) {
$metadata->setGroupSequence($constraint->groups);
} elseif ($constraint instanceof GroupSequenceProvider) {
$metadata->setGroupSequenceProvider(true);
} elseif ($constraint instanceof Constraint) {
$metadata->addConstraint($constraint);
}

$success = true;
}

foreach ($reflClass->getProperties() as $property) {
if ($property->getDeclaringClass()->name === $className) {
foreach ($this->getAttributes($property) as $constraint) {
if ($constraint instanceof Constraint) {
$metadata->addPropertyConstraint($property->name, $constraint);
}

$success = true;
}
}
}

foreach ($reflClass->getMethods() as $method) {
if ($method->getDeclaringClass()->name === $className) {
foreach ($this->getAttributes($method) as $constraint) {
if ($constraint instanceof Callback) {
$constraint->callback = $method->getName();

$metadata->addConstraint($constraint);
} elseif ($constraint instanceof Constraint) {
if (preg_match('/^(get|is|has)(.+)$/i', $method->name, $matches)) {
$metadata->addGetterMethodConstraint(lcfirst($matches[2]), $matches[0], $constraint);
} else {
throw new MappingException(sprintf('The constraint on "%s::%s()" cannot be added. Constraints can only be added on methods beginning with "get", "is" or "has".', $className, $method->name));
}
}

$success = true;
}
}
}

return $success;
}

private function getAttributes(\ReflectionMethod|\ReflectionClass|\ReflectionProperty $reflection): iterable
{
parent::__construct(null);
foreach ($reflection->getAttributes(GroupSequence::class) as $attribute) {
yield $attribute->newInstance();
}
foreach ($reflection->getAttributes(GroupSequenceProvider::class) as $attribute) {
yield $attribute->newInstance();
}
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
yield $attribute->newInstance();
}
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Component/Validator/Tests/ConstraintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ public function testOptionsWithInvalidInternalPointer()
$this->assertEquals('foo', $constraint->property1);
}

public function testAnnotationSetUndefinedDefaultOption()
public function testAttributeSetUndefinedDefaultOption()
{
$this->expectException(ConstraintDefinitionException::class);
$this->expectExceptionMessage('No default option is configured for constraint "Symfony\Component\Validator\Tests\Fixtures\ConstraintB".');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,22 +205,22 @@ public function testConstraintGetTargets()
$this->assertEquals($targets, $constraint->getTargets());
}

// Should succeed. Needed when defining constraints as annotations.
// Should succeed. Needed when defining constraints as attributes.
public function testNoConstructorArguments()
{
$constraint = new Callback();

$this->assertSame([Constraint::CLASS_CONSTRAINT, Constraint::PROPERTY_CONSTRAINT], $constraint->getTargets());
}

public function testAnnotationInvocationSingleValued()
public function testAttributeInvocationSingleValued()
{
$constraint = new Callback(['value' => 'validateStatic']);

$this->assertEquals(new Callback('validateStatic'), $constraint);
}

public function testAnnotationInvocationMultiValued()
public function testAttributeInvocationMultiValued()
{
$constraint = new Callback(['value' => [__CLASS__.'_Class', 'validateCallback']]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public function testDefaultOption()
$this->assertEquals(5, $constraint->max);
}

public function testConstraintAnnotationDefaultOption()
public function testConstraintAttributeDefaultOption()
{
$constraint = new Count(['value' => 5, 'exactMessage' => 'message']);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function testConstraintDefaultOption()
self::assertEquals(5, $constraint->max);
}

public function testConstraintAnnotationDefaultOption()
public function testConstraintAttributeDefaultOption()
{
$constraint = new Length(['value' => 5, 'exactMessage' => 'message']);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\MissingOptionsException;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Validator\Mapping\Loader\AttributeLoader;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\WhenTestWithAttributes;

Expand Down
Loading