Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Validator\Constraints; Chris@0: Chris@14: use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; Chris@14: use Symfony\Component\PropertyAccess\PropertyAccess; Chris@18: use Symfony\Component\PropertyAccess\PropertyAccessorInterface; Chris@0: use Symfony\Component\Validator\Constraint; Chris@0: use Symfony\Component\Validator\ConstraintValidator; Chris@14: use Symfony\Component\Validator\Exception\ConstraintDefinitionException; Chris@0: use Symfony\Component\Validator\Exception\UnexpectedTypeException; Chris@0: Chris@0: /** Chris@0: * Provides a base class for the validation of property comparisons. Chris@0: * Chris@0: * @author Daniel Holmes Chris@0: * @author Bernhard Schussek Chris@0: */ Chris@0: abstract class AbstractComparisonValidator extends ConstraintValidator Chris@0: { Chris@14: private $propertyAccessor; Chris@14: Chris@18: public function __construct(PropertyAccessorInterface $propertyAccessor = null) Chris@14: { Chris@14: $this->propertyAccessor = $propertyAccessor; Chris@14: } Chris@14: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function validate($value, Constraint $constraint) Chris@0: { Chris@0: if (!$constraint instanceof AbstractComparison) { Chris@0: throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\AbstractComparison'); Chris@0: } Chris@0: Chris@0: if (null === $value) { Chris@0: return; Chris@0: } Chris@0: Chris@14: if ($path = $constraint->propertyPath) { Chris@14: if (null === $object = $this->context->getObject()) { Chris@14: return; Chris@14: } Chris@14: Chris@14: try { Chris@14: $comparedValue = $this->getPropertyAccessor()->getValue($object, $path); Chris@14: } catch (NoSuchPropertyException $e) { Chris@17: throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s', $path, \get_class($constraint), $e->getMessage()), 0, $e); Chris@14: } Chris@14: } else { Chris@14: $comparedValue = $constraint->value; Chris@14: } Chris@0: Chris@0: // Convert strings to DateTimes if comparing another DateTime Chris@0: // This allows to compare with any date/time value supported by Chris@0: // the DateTime constructor: Chris@0: // http://php.net/manual/en/datetime.formats.php Chris@17: if (\is_string($comparedValue)) { Chris@0: if ($value instanceof \DateTimeImmutable) { Chris@0: // If $value is immutable, convert the compared value to a Chris@0: // DateTimeImmutable too Chris@14: $comparedValue = new \DateTimeImmutable($comparedValue); Chris@0: } elseif ($value instanceof \DateTimeInterface) { Chris@0: // Otherwise use DateTime Chris@0: $comparedValue = new \DateTime($comparedValue); Chris@0: } Chris@0: } Chris@0: Chris@0: if (!$this->compareValues($value, $comparedValue)) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING | self::PRETTY_DATE)) Chris@0: ->setParameter('{{ compared_value }}', $this->formatValue($comparedValue, self::OBJECT_TO_STRING | self::PRETTY_DATE)) Chris@0: ->setParameter('{{ compared_value_type }}', $this->formatTypeOf($comparedValue)) Chris@0: ->setCode($this->getErrorCode()) Chris@0: ->addViolation(); Chris@0: } Chris@0: } Chris@0: Chris@14: private function getPropertyAccessor() Chris@14: { Chris@14: if (null === $this->propertyAccessor) { Chris@14: $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); Chris@14: } Chris@14: Chris@14: return $this->propertyAccessor; Chris@14: } Chris@14: Chris@0: /** Chris@0: * Compares the two given values to find if their relationship is valid. Chris@0: * Chris@0: * @param mixed $value1 The first value to compare Chris@0: * @param mixed $value2 The second value to compare Chris@0: * Chris@0: * @return bool true if the relationship is valid, false otherwise Chris@0: */ Chris@0: abstract protected function compareValues($value1, $value2); Chris@0: Chris@0: /** Chris@0: * Returns the error code used if the comparison fails. Chris@0: * Chris@0: * @return string|null The error code or `null` if no code should be set Chris@0: */ Chris@0: protected function getErrorCode() Chris@0: { Chris@0: } Chris@0: }