Skip to content

[Validator] Decouple type validation from constraint validators #12312

Closed
@webmozart

Description

@webmozart

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:

  1. what type an element should have (or any)
  2. 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:

  1. Get the expected type for the current node
  2. Get the actual type (gettype()) of the node's value
  3. Check that the actual type matches one of the expected types. Otherwise, raise a constraint violation.
  4. Validate all "normal" constraints. Only validate a constraint if the node's value is not null or if null is in the expected types of that constraint.

Consequences:

  • While the Type constraint currently accepts any type for which either is_<type>() or ctype_<type>() is defined or which is a class name, that would be restricted to the results of gettype() and get_class(). A BC layer needs to be provided in some way.

Related issues:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions