vendor/symfony/validator/Constraints/AbstractComparisonValidator.php line 32

  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Validator\Constraints;
  11. use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
  12. use Symfony\Component\PropertyAccess\PropertyAccess;
  13. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  14. use Symfony\Component\Validator\Constraint;
  15. use Symfony\Component\Validator\ConstraintValidator;
  16. use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
  17. use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  18. /**
  19.  * Provides a base class for the validation of property comparisons.
  20.  *
  21.  * @author Daniel Holmes <daniel@danielholmes.org>
  22.  * @author Bernhard Schussek <bschussek@gmail.com>
  23.  */
  24. abstract class AbstractComparisonValidator extends ConstraintValidator
  25. {
  26.     private ?PropertyAccessorInterface $propertyAccessor;
  27.     public function __construct(PropertyAccessorInterface $propertyAccessor null)
  28.     {
  29.         $this->propertyAccessor $propertyAccessor;
  30.     }
  31.     public function validate(mixed $valueConstraint $constraint)
  32.     {
  33.         if (!$constraint instanceof AbstractComparison) {
  34.             throw new UnexpectedTypeException($constraintAbstractComparison::class);
  35.         }
  36.         if (null === $value) {
  37.             return;
  38.         }
  39.         if ($path $constraint->propertyPath) {
  40.             if (null === $object $this->context->getObject()) {
  41.                 return;
  42.             }
  43.             try {
  44.                 $comparedValue $this->getPropertyAccessor()->getValue($object$path);
  45.             } catch (NoSuchPropertyException $e) {
  46.                 throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: '$pathget_debug_type($constraint)).$e->getMessage(), 0$e);
  47.             }
  48.         } else {
  49.             $comparedValue $constraint->value;
  50.         }
  51.         // Convert strings to DateTimes if comparing another DateTime
  52.         // This allows to compare with any date/time value supported by
  53.         // the DateTime constructor:
  54.         // https://php.net/datetime.formats
  55.         if (\is_string($comparedValue) && $value instanceof \DateTimeInterface) {
  56.             // If $value is immutable, convert the compared value to a DateTimeImmutable too, otherwise use DateTime
  57.             $dateTimeClass $value instanceof \DateTimeImmutable \DateTimeImmutable::class : \DateTime::class;
  58.             try {
  59.                 $comparedValue = new $dateTimeClass($comparedValue);
  60.             } catch (\Exception) {
  61.                 throw new ConstraintDefinitionException(sprintf('The compared value "%s" could not be converted to a "%s" instance in the "%s" constraint.'$comparedValue$dateTimeClassget_debug_type($constraint)));
  62.             }
  63.         }
  64.         if (!$this->compareValues($value$comparedValue)) {
  65.             $violationBuilder $this->context->buildViolation($constraint->message)
  66.                 ->setParameter('{{ value }}'$this->formatValue($valueself::OBJECT_TO_STRING self::PRETTY_DATE))
  67.                 ->setParameter('{{ compared_value }}'$this->formatValue($comparedValueself::OBJECT_TO_STRING self::PRETTY_DATE))
  68.                 ->setParameter('{{ compared_value_type }}'$this->formatTypeOf($comparedValue))
  69.                 ->setCode($this->getErrorCode());
  70.             if (null !== $path) {
  71.                 $violationBuilder->setParameter('{{ compared_value_path }}'$path);
  72.             }
  73.             $violationBuilder->addViolation();
  74.         }
  75.     }
  76.     private function getPropertyAccessor(): PropertyAccessorInterface
  77.     {
  78.         return $this->propertyAccessor ??= PropertyAccess::createPropertyAccessor();
  79.     }
  80.     /**
  81.      * Compares the two given values to find if their relationship is valid.
  82.      */
  83.     abstract protected function compareValues(mixed $value1mixed $value2): bool;
  84.     /**
  85.      * Returns the error code used if the comparison fails.
  86.      */
  87.     protected function getErrorCode(): ?string
  88.     {
  89.         return null;
  90.     }
  91. }