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@0: use Symfony\Component\Validator\Constraint; Chris@0: use Symfony\Component\Validator\ConstraintValidator; Chris@0: use Symfony\Component\Validator\Exception\UnexpectedTypeException; Chris@0: Chris@0: /** Chris@0: * @author Bernhard Schussek Chris@0: */ Chris@0: class CollectionValidator extends ConstraintValidator Chris@0: { Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function validate($value, Constraint $constraint) Chris@0: { Chris@0: if (!$constraint instanceof Collection) { Chris@0: throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Collection'); Chris@0: } Chris@0: Chris@0: if (null === $value) { Chris@0: return; Chris@0: } Chris@0: Chris@17: if (!\is_array($value) && !($value instanceof \Traversable && $value instanceof \ArrayAccess)) { Chris@0: throw new UnexpectedTypeException($value, 'array or Traversable and ArrayAccess'); Chris@0: } Chris@0: Chris@0: // We need to keep the initialized context when CollectionValidator Chris@0: // calls itself recursively (Collection constraints can be nested). Chris@0: // Since the context of the validator is overwritten when initialize() Chris@0: // is called for the nested constraint, the outer validator is Chris@0: // acting on the wrong context when the nested validation terminates. Chris@0: // Chris@0: // A better solution - which should be approached in Symfony 3.0 - is to Chris@0: // remove the initialize() method and pass the context as last argument Chris@0: // to validate() instead. Chris@0: $context = $this->context; Chris@0: Chris@0: foreach ($constraint->fields as $field => $fieldConstraint) { Chris@0: // bug fix issue #2779 Chris@18: $existsInArray = \is_array($value) && \array_key_exists($field, $value); Chris@0: $existsInArrayAccess = $value instanceof \ArrayAccess && $value->offsetExists($field); Chris@0: Chris@0: if ($existsInArray || $existsInArrayAccess) { Chris@17: if (\count($fieldConstraint->constraints) > 0) { Chris@0: $context->getValidator() Chris@0: ->inContext($context) Chris@0: ->atPath('['.$field.']') Chris@0: ->validate($value[$field], $fieldConstraint->constraints); Chris@0: } Chris@0: } elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) { Chris@0: $context->buildViolation($constraint->missingFieldsMessage) Chris@0: ->atPath('['.$field.']') Chris@0: ->setParameter('{{ field }}', $this->formatValue($field)) Chris@0: ->setInvalidValue(null) Chris@0: ->setCode(Collection::MISSING_FIELD_ERROR) Chris@0: ->addViolation(); Chris@0: } Chris@0: } Chris@0: Chris@0: if (!$constraint->allowExtraFields) { Chris@0: foreach ($value as $field => $fieldValue) { Chris@0: if (!isset($constraint->fields[$field])) { Chris@0: $context->buildViolation($constraint->extraFieldsMessage) Chris@0: ->atPath('['.$field.']') Chris@0: ->setParameter('{{ field }}', $this->formatValue($field)) Chris@0: ->setInvalidValue($fieldValue) Chris@0: ->setCode(Collection::NO_SUCH_FIELD_ERROR) Chris@0: ->addViolation(); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: }