/** * Recursively parse constraints. * * @param $className * @param array $visited * @return array */ protected function doParse($className, array $visited) { $params = array(); $classdata = $this->factory->getMetadataFor($className); $properties = $classdata->getConstrainedProperties(); $refl = $classdata->getReflectionClass(); $defaults = $refl->getDefaultProperties(); foreach ($properties as $property) { $vparams = array(); $vparams['default'] = isset($defaults[$property]) ? $defaults[$property] : null; $pds = $classdata->getPropertyMetadata($property); foreach ($pds as $propdata) { $constraints = $propdata->getConstraints(); foreach ($constraints as $constraint) { $vparams = $this->parseConstraint($constraint, $vparams, $className, $visited); } } if (isset($vparams['format'])) { $vparams['format'] = join(', ', $vparams['format']); } foreach (array('dataType', 'readonly', 'required', 'subType') as $reqprop) { if (!isset($vparams[$reqprop])) { $vparams[$reqprop] = null; } } // check for nested classes with All constraint if (isset($vparams['class']) && !in_array($vparams['class'], $visited) && null !== $this->factory->getMetadataFor($vparams['class'])) { $visited[] = $vparams['class']; $vparams['children'] = $this->doParse($vparams['class'], $visited); } $vparams['actualType'] = isset($vparams['actualType']) ? $vparams['actualType'] : DataTypes::STRING; $params[$property] = $vparams; } return $params; }
/** * Gets ClassMetadata of desired class with annotations and others (xml, yml, php) using metadata factory * * @param string $className * * @return ClassMetadata Returns ClassMetadata object of desired entity with annotations info */ public function getClassMetadata($className) { if (!isset($this->classesMetadata[$className])) { $this->classesMetadata[$className] = $this->metadataFactory->getMetadataFor($className); } return $this->classesMetadata[$className]; }
/** * {@inheritDoc} * * @throws ValidatorException If the metadata for the value does not support properties. */ public function validatePropertyValue($containingValue, $property, $value, $groups = null) { $visitor = $this->createVisitor($containingValue); $metadata = $this->metadataFactory->getMetadataFor($containingValue); if (!$metadata instanceof PropertyMetadataContainerInterface) { $valueAsString = is_scalar($containingValue) ? '"' . $containingValue . '"' : 'the value of type ' . gettype($containingValue); throw new ValidatorException(sprintf('The metadata for ' . $valueAsString . ' does not support properties.')); } foreach ($this->resolveGroups($groups) as $group) { if (!$metadata->hasPropertyMetadata($property)) { continue; } foreach ($metadata->getPropertyMetadata($property) as $propMeta) { $propMeta->accept($visitor, $value, $group, $property); } } return $visitor->getViolations(); }
/** * @param Node $node * @return void */ public function enterNode(Node $node) { if ($node instanceof Node\Stmt\Namespace_) { if (isset($node->name)) { $this->namespace = implode('\\', $node->name->parts); } return; } if (!$node instanceof Node\Stmt\Class_) { return; } $name = '' === $this->namespace ? $node->name : $this->namespace . '\\' . $node->name; if (!class_exists($name)) { return; } $metadata = $this->metadataFactory instanceof ClassMetadataFactoryInterface ? $this->metadataFactory->getClassMetadata($name) : $this->metadataFactory->getMetadataFor($name); if (!$metadata->hasConstraints() && !count($metadata->getConstrainedProperties())) { return; } $this->extractFromConstraints($metadata->constraints); foreach ($metadata->members as $members) { foreach ($members as $member) { $this->extractFromConstraints($member->constraints); } } }
/** * Extracts constraints based on validation metadata * * @param FormInterface $form * @return array */ protected function extractMetadataPropertiesConstraints(FormInterface $form) { $constraints = []; if ($form->getConfig()->getDataClass()) { /** @var ClassMetadata $metadata */ $metadata = $this->metadataFactory->getMetadataFor($form->getConfig()->getDataClass()); $constraints = $metadata->properties; } $errorMapping = $form->getConfig()->getOption('error_mapping'); if (!empty($constraints) && !empty($errorMapping)) { foreach ($errorMapping as $originalName => $mappedName) { if (isset($constraints[$originalName])) { $constraints[$mappedName] = $constraints[$originalName]; } } } return $constraints; }
/** * {@inheritdoc} */ public function validate($value, $group, $propertyPath, $traverse = false, $deep = false) { if (null === $value) { return; } if (is_object($value)) { $hash = spl_object_hash($value); // Exit, if the object is already validated for the current group if (isset($this->validatedObjects[$hash][$group])) { return; } // Initialize if the object wasn't initialized before if (!isset($this->validatedObjects[$hash])) { foreach ($this->objectInitializers as $initializer) { if (!$initializer instanceof ObjectInitializerInterface) { throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.'); } $initializer->initialize($value); } } // Remember validating this object before starting and possibly // traversing the object graph $this->validatedObjects[$hash][$group] = true; } // Validate arrays recursively by default, otherwise every driver needs // to implement special handling for arrays. // https://github.com/symfony/symfony/issues/6246 if (is_array($value) || ($traverse && $value instanceof \Traversable)) { foreach ($value as $key => $element) { // Ignore any scalar values in the collection if (is_object($element) || is_array($element)) { // Only repeat the traversal if $deep is set $this->validate($element, $group, $propertyPath.'['.$key.']', $deep, $deep); } } try { $this->metadataFactory->getMetadataFor($value)->accept($this, $value, $group, $propertyPath); } catch (NoSuchMetadataException $e) { // Metadata doesn't necessarily have to exist for // traversable objects, because we know how to validate // them anyway. Optionally, additional metadata is supported. } } else { $this->metadataFactory->getMetadataFor($value)->accept($this, $value, $group, $propertyPath); } }
/** * Validates a value against a constraint. * * @param Constraint $constraint * @param $value * @param $group * @param $propertyPath * @param null $currentClass * @param null $currentProperty * * @deprecated Deprecated since version 2.2, to be removed in 2.3. */ public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath, $currentClass = null, $currentProperty = null) { $metadata = null; // BC code to make getCurrentClass() and getCurrentProperty() work when // called from within this method if (null !== $currentClass) { $metadata = $this->metadataFactory->getMetadataFor($currentClass); if (null !== $currentProperty && $metadata instanceof PropertyMetadataContainerInterface) { $metadata = current($metadata->getPropertyMetadata($currentProperty)); } } $context = new ExecutionContext($this->visitor, $metadata, $value, $group, $propertyPath); $context->validateValue($value, $constraint); }
/** * Validates a value against a constraint. * * @param Constraint $constraint * @param $value * @param $group * @param $propertyPath * @param null $currentClass * @param null $currentProperty * * @deprecated Deprecated since version 2.2, to be removed in 2.3. */ public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath, $currentClass = null, $currentProperty = null) { trigger_error('walkConstraint() is deprecated since version 2.2 and will be removed in 2.3.', E_USER_DEPRECATED); $metadata = null; // BC code to make getCurrentClass() and getCurrentProperty() work when // called from within this method if (null !== $currentClass) { $metadata = $this->metadataFactory->getMetadataFor($currentClass); if (null !== $currentProperty && $metadata instanceof PropertyMetadataContainerInterface) { $metadata = current($metadata->getPropertyMetadata($currentProperty)); } } $context = new ExecutionContext($this->visitor, $this->translator, $this->translationDomain, $metadata, $value, $group, $propertyPath); $context->validateValue($value, $constraint); }
/** * Validates an object against the constraints defined for its class. * * If no metadata is available for the class, but the class is an instance * of {@link \Traversable} and the selected traversal strategy allows * traversal, the object will be iterated and each nested object will be * validated instead. * * @param object $object The object to cascade * @param string $propertyPath The current property path * @param string[] $groups The validated groups * @param int $traversalStrategy The strategy for traversing the * cascaded object * @param ExecutionContextInterface $context The current execution context * * @throws NoSuchMetadataException If the object has no associated metadata * and does not implement {@link \Traversable} * or if traversal is disabled via the * $traversalStrategy argument * @throws UnsupportedMetadataException If the metadata returned by the * metadata factory does not implement * {@link ClassMetadataInterface} */ private function validateObject($object, $propertyPath, array $groups, $traversalStrategy, ExecutionContextInterface $context) { try { $classMetadata = $this->metadataFactory->getMetadataFor($object); if (!$classMetadata instanceof ClassMetadataInterface) { throw new UnsupportedMetadataException(sprintf('The metadata factory should return instances of ' . '"Symfony\\Component\\Validator\\Mapping\\ClassMetadataInterface", ' . 'got: "%s".', is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata))); } $this->validateClassNode($object, spl_object_hash($object), $classMetadata, $propertyPath, $groups, null, $traversalStrategy, $context); } catch (NoSuchMetadataException $e) { // Rethrow if not Traversable if (!$object instanceof \Traversable) { throw $e; } // Rethrow unless IMPLICIT or TRAVERSE if (!($traversalStrategy & (TraversalStrategy::IMPLICIT | TraversalStrategy::TRAVERSE))) { throw $e; } $this->validateEachObjectIn($object, $propertyPath, $groups, $traversalStrategy & TraversalStrategy::STOP_RECURSION, $context); } }
/** * {@inheritdoc} * * @throws ValidatorException If the metadata for the value does not support properties. */ public function validatePropertyValue($containingValue, $property, $value, $groups = null) { $visitor = $this->createVisitor(is_object($containingValue) ? $containingValue : $value); $metadata = $this->metadataFactory->getMetadataFor($containingValue); if (!$metadata instanceof PropertyMetadataContainerInterface) { $valueAsString = is_scalar($containingValue) ? '"' . $containingValue . '"' : 'the value of type ' . gettype($containingValue); throw new ValidatorException(sprintf('The metadata for ' . $valueAsString . ' does not support properties.')); } // If $containingValue is passed as class name, take $value as root // and start the traversal with an empty property path $propertyPath = is_object($containingValue) ? $property : ''; foreach ($this->resolveGroups($groups) as $group) { if (!$metadata->hasPropertyMetadata($property)) { continue; } foreach ($metadata->getPropertyMetadata($property) as $propMeta) { $propMeta->accept($visitor, $value, $group, $propertyPath); } } return $visitor->getViolations(); }
public function testExecuteEmptyValidator() { $metadata = $this->getMock('Symfony\\Component\\Validator\\MetadataInterface'); $this->validatorFactory->expects($this->once())->method('getMetadataFor')->with($this->equalTo('Acme\\Entity\\Foo'))->will($this->returnValue($metadata)); $metadata->properties = array(); $metadata->getters = array(); $modelManager = $this->getMock('Sonata\\AdminBundle\\Model\\ModelManagerInterface'); $this->admin->expects($this->any())->method('getModelManager')->will($this->returnValue($modelManager)); // @todo Mock of \Traversable is available since Phpunit 3.8. This should be completed after stable release of Phpunit 3.8. // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 // $formBuilder = $this->getMock('Symfony\Component\Form\FormBuilderInterface'); // // $this->admin->expects($this->any()) // ->method('getFormBuilder') // ->will($this->returnValue($formBuilder)); $datagridBuilder = $this->getMock('\\Sonata\\AdminBundle\\Builder\\DatagridBuilderInterface'); $this->admin->expects($this->any())->method('getDatagridBuilder')->will($this->returnValue($datagridBuilder)); $listBuilder = $this->getMock('Sonata\\AdminBundle\\Builder\\ListBuilderInterface'); $this->admin->expects($this->any())->method('getListBuilder')->will($this->returnValue($listBuilder)); $command = $this->application->find('sonata:admin:explain'); $commandTester = new CommandTester($command); $commandTester->execute(array('command' => $command->getName(), 'admin' => 'acme.admin.foo')); $this->assertEquals(sprintf(str_replace("\n", PHP_EOL, file_get_contents(__DIR__ . '/../Fixtures/Command/explain_admin_empty_validator.txt')), get_class($this->admin), get_class($modelManager), get_class($datagridBuilder), get_class($listBuilder)), $commandTester->getDisplay()); }
/** * {@inheritdoc} */ public function hasMetadataFor($object) { return $this->metadataFactory->hasMetadataFor($object); }
public function find(FormInterface $form) { $propertyPath = $form->getPropertyPath(); if ($form->getPropertyPath() === null) { return new ConstraintCollection(); } $class = $this->resolveDataClass($form); if ($class === null) { return new ConstraintCollection(); } $metadata = $this->metadataFactory->getMetadataFor($class); if (!$metadata instanceof ClassMetadata) { return new ConstraintCollection(); } if ($propertyPath->getLength() < 1) { throw new UnsupportedException('Not supported please submit a issue with the form that produces this error!'); } // Retrieve the last property element $propertyLastElementIndex = $propertyPath->getLength() - 1; $propertyName = $propertyPath->getElement($propertyLastElementIndex); if ($propertyPath->getLength() > 1) { // When we have multiple parts to the path then resolve it // To return the actual property and metadata // Resolve parent data list($dataSource, $dataSourceClass) = $this->resolveDataSource($form); for ($i = 0; $i < $propertyPath->getLength() - 1; $i++) { $element = $propertyPath->getElement($i); $property = $this->guessProperty($metadata, $element); // If the Valid tag is missing the property will return null. // Or if there is no data set on the form if ($property === null) { return new ConstraintCollection(); } foreach ($metadata->getPropertyMetadata($property) as $propertyMetadata) { if (!$propertyMetadata instanceof MemberMetadata) { continue; } $dataSourceInfo = $this->findPropertyDataTypeInfo($propertyMetadata, $dataSource, $dataSourceClass); if ($dataSourceInfo === null) { return new ConstraintCollection(); } list($dataSourceClass, $dataSource) = $dataSourceInfo; // Handle arrays/index based properties while ($dataSourceClass === null) { $i++; if (!$propertyPath->isIndex($i)) { // For some strange reason the findPropertyDataTypeInfo is wrong // or the form is wrong return new ConstraintCollection(); } $dataSource = $dataSource[$propertyPath->getElement($i)]; if (is_object($dataSource)) { $dataSourceClass = get_class($dataSource); } } // Ok we failed to find the data source class if ($dataSourceClass === null) { return new ConstraintCollection(); } $metadata = $this->metadataFactory->getMetadataFor($dataSourceClass); if (!$metadata instanceof ClassMetadata) { continue; } continue 2; } // We where unable to locate a class/array property return new ConstraintCollection(); } } // Handle array properties $propertyCascadeOnly = false; if ($propertyPath->isIndex($propertyLastElementIndex)) { $propertyCascadeOnly = true; $elements = $form->getParent()->getPropertyPath()->getElements(); $propertyName = end($elements); } // Find property constraints return $this->findPropertyConstraints($metadata, $propertyName, $propertyCascadeOnly); }