Description
Several issues have been raised in the past because of the validator's inconcistent type validation. Some constraints through UnexpectedTypeExceptions, others add constraint violations, others ignore unexpected types. This should be fixed.
An indepth preliminary discussion of this topic can be found here:
#10221 (comment)
The way I envision this to happen is by changing the Type
constraint to a meta-constraint (like Valid
or Traverse
), which is handled by the validation engine directly. Currently, this constraint specifies:
- what type an element should have (or any)
- what violation message should be added if the type does not match
/**
* @Type("int", message="This field expects an integer.")
* @GreaterThan(0)
*/
private $price;
While the $type
option of the Type
constraint is mandatory at the moment, it will become optional. Instead, each constraint will have a new method returning the accepted types:
class GreaterThan extends Constraint
{
// ...
public function getAcceptedTypes()
{
return array(self::INT, self::FLOAT);
}
}
Now the validation engine will take care that a given value matches the types expected by each constraint. If not, a constraint violation will be added. The Type
constraint can be used to customize the violation message:
/**
* @Type(message="The price should be a number.")
* @GreaterThan(0)
*/
private $price;
We need to change GenericMetadata::addConstraint()
to validate the compatibility of the defined constraints:
- When adding a normal constraint, the expected type should be set to the intersection of all "accepted types" (dependent on the validation group). If the expected type is empty, the constraints conflict and an exception should be thrown.
- When adding a
Type
constraint, the expected type is set manually. Again, check that there are no conflicts with the other constraints on that property.
The constraint validation process can then be adapted in the following way:
- Get the expected type for the current node
- Get the actual type (
gettype()
) of the node's value - Check that the actual type matches one of the expected types. Otherwise, raise a constraint violation.
- Validate all "normal" constraints. Only validate a constraint if the node's value is not
null
or ifnull
is in the expected types of that constraint.
Consequences:
- While the
Type
constraint currently accepts any type for which eitheris_<type>()
orctype_<type>()
is defined or which is a class name, that would be restricted to the results ofgettype()
andget_class()
. A BC layer needs to be provided in some way.
Related issues:
- [Validatior] Constraint value type check inconsistency #6799
- Symfony\Component\Validator\ConstraintsAll trow php error if just empty string provided #9502
- [Validator] Add an integer constraint and validator #11076
- [Form][Validator] LengthValidator #11083
- [Validator] fixed: Expressions always valid for null values #11590
- [3.0] Constraints should not ignore empty strings #11956
- [Validator][ChoiceValidator] null value cause SQL crash #12163