4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Validator\Mapping;
14 use Symfony\Component\Validator\Constraint;
15 use Symfony\Component\Validator\Constraints\GroupSequence;
16 use Symfony\Component\Validator\Constraints\Traverse;
17 use Symfony\Component\Validator\Constraints\Valid;
18 use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
19 use Symfony\Component\Validator\Exception\GroupDefinitionException;
20 use Symfony\Component\Validator\ValidationVisitorInterface;
23 * Default implementation of {@link ClassMetadataInterface}.
25 * This class supports serialization and cloning.
27 * @author Bernhard Schussek <bschussek@gmail.com>
28 * @author Fabien Potencier <fabien@symfony.com>
30 class ClassMetadata extends ElementMetadata implements ClassMetadataInterface
35 * @internal This property is public in order to reduce the size of the
36 * class' serialized representation. Do not access it. Use
37 * {@link getClassName()} instead.
44 * @internal This property is public in order to reduce the size of the
45 * class' serialized representation. Do not access it. Use
46 * {@link getDefaultGroup()} instead.
51 * @var MemberMetadata[]
53 * @internal This property is public in order to reduce the size of the
54 * class' serialized representation. Do not access it. Use
55 * {@link getPropertyMetadata()} instead.
57 public $members = array();
60 * @var PropertyMetadata[]
62 * @internal This property is public in order to reduce the size of the
63 * class' serialized representation. Do not access it. Use
64 * {@link getPropertyMetadata()} instead.
66 public $properties = array();
69 * @var GetterMetadata[]
71 * @internal This property is public in order to reduce the size of the
72 * class' serialized representation. Do not access it. Use
73 * {@link getPropertyMetadata()} instead.
75 public $getters = array();
80 * @internal This property is public in order to reduce the size of the
81 * class' serialized representation. Do not access it. Use
82 * {@link getGroupSequence()} instead.
84 public $groupSequence = array();
89 * @internal This property is public in order to reduce the size of the
90 * class' serialized representation. Do not access it. Use
91 * {@link isGroupSequenceProvider()} instead.
93 public $groupSequenceProvider = false;
96 * The strategy for traversing traversable objects.
98 * By default, only instances of {@link \Traversable} are traversed.
102 * @internal This property is public in order to reduce the size of the
103 * class' serialized representation. Do not access it. Use
104 * {@link getTraversalStrategy()} instead.
106 public $traversalStrategy = TraversalStrategy::IMPLICIT;
109 * @var \ReflectionClass
114 * Constructs a metadata for the given class.
116 * @param string $class
118 public function __construct($class)
120 $this->name = $class;
121 // class name without namespace
122 if (false !== $nsSep = strrpos($class, '\\')) {
123 $this->defaultGroup = substr($class, $nsSep + 1);
125 $this->defaultGroup = $class;
132 * @deprecated since version 2.5, to be removed in 3.0.
134 public function accept(ValidationVisitorInterface $visitor, $value, $group, $propertyPath, $propagatedGroup = null)
136 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
138 if (null === $propagatedGroup && Constraint::DEFAULT_GROUP === $group
139 && ($this->hasGroupSequence() || $this->isGroupSequenceProvider())) {
140 if ($this->hasGroupSequence()) {
141 $groups = $this->getGroupSequence()->groups;
143 $groups = $value->getGroupSequence();
146 foreach ($groups as $group) {
147 $this->accept($visitor, $value, $group, $propertyPath, Constraint::DEFAULT_GROUP);
149 if (count($visitor->getViolations()) > 0) {
157 $visitor->visit($this, $value, $group, $propertyPath);
159 if (null !== $value) {
160 $pathPrefix = empty($propertyPath) ? '' : $propertyPath.'.';
162 foreach ($this->getConstrainedProperties() as $property) {
163 foreach ($this->getPropertyMetadata($property) as $member) {
164 $member->accept($visitor, $member->getPropertyValue($value), $group, $pathPrefix.$property, $propagatedGroup);
173 public function __sleep()
175 $parentProperties = parent::__sleep();
177 // Don't store the cascading strategy. Classes never cascade.
178 unset($parentProperties[array_search('cascadingStrategy', $parentProperties)]);
180 return array_merge($parentProperties, array(
183 'groupSequenceProvider',
194 public function getClassName()
200 * Returns the name of the default group for this class.
202 * For each class, the group "Default" is an alias for the group
203 * "<ClassName>", where <ClassName> is the non-namespaced name of the
204 * class. All constraints implicitly or explicitly assigned to group
205 * "Default" belong to both of these groups, unless the class defines
208 * If a class defines a group sequence, validating the class in "Default"
209 * will validate the group sequence. The constraints assigned to "Default"
210 * can still be validated by validating the class in "<ClassName>".
212 * @return string The name of the default group
214 public function getDefaultGroup()
216 return $this->defaultGroup;
222 public function addConstraint(Constraint $constraint)
224 if (!in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets())) {
225 throw new ConstraintDefinitionException(sprintf(
226 'The constraint "%s" cannot be put on classes.',
227 get_class($constraint)
231 if ($constraint instanceof Valid) {
232 throw new ConstraintDefinitionException(sprintf(
233 'The constraint "%s" cannot be put on classes.',
234 get_class($constraint)
238 if ($constraint instanceof Traverse) {
239 if ($constraint->traverse) {
240 // If traverse is true, traversal should be explicitly enabled
241 $this->traversalStrategy = TraversalStrategy::TRAVERSE;
243 // If traverse is false, traversal should be explicitly disabled
244 $this->traversalStrategy = TraversalStrategy::NONE;
247 // The constraint is not added
251 $constraint->addImplicitGroupName($this->getDefaultGroup());
253 parent::addConstraint($constraint);
259 * Adds a constraint to the given property.
261 * @param string $property The name of the property
262 * @param Constraint $constraint The constraint
266 public function addPropertyConstraint($property, Constraint $constraint)
268 if (!isset($this->properties[$property])) {
269 $this->properties[$property] = new PropertyMetadata($this->getClassName(), $property);
271 $this->addPropertyMetadata($this->properties[$property]);
274 $constraint->addImplicitGroupName($this->getDefaultGroup());
276 $this->properties[$property]->addConstraint($constraint);
282 * @param string $property
283 * @param Constraint[] $constraints
287 public function addPropertyConstraints($property, array $constraints)
289 foreach ($constraints as $constraint) {
290 $this->addPropertyConstraint($property, $constraint);
297 * Adds a constraint to the getter of the given property.
299 * The name of the getter is assumed to be the name of the property with an
300 * uppercased first letter and either the prefix "get" or "is".
302 * @param string $property The name of the property
303 * @param Constraint $constraint The constraint
307 public function addGetterConstraint($property, Constraint $constraint)
309 if (!isset($this->getters[$property])) {
310 $this->getters[$property] = new GetterMetadata($this->getClassName(), $property);
312 $this->addPropertyMetadata($this->getters[$property]);
315 $constraint->addImplicitGroupName($this->getDefaultGroup());
317 $this->getters[$property]->addConstraint($constraint);
323 * Adds a constraint to the getter of the given property.
325 * @param string $property The name of the property
326 * @param string $method The name of the getter method
327 * @param Constraint $constraint The constraint
331 public function addGetterMethodConstraint($property, $method, Constraint $constraint)
333 if (!isset($this->getters[$property])) {
334 $this->getters[$property] = new GetterMetadata($this->getClassName(), $property, $method);
336 $this->addPropertyMetadata($this->getters[$property]);
339 $constraint->addImplicitGroupName($this->getDefaultGroup());
341 $this->getters[$property]->addConstraint($constraint);
347 * @param string $property
348 * @param Constraint[] $constraints
352 public function addGetterConstraints($property, array $constraints)
354 foreach ($constraints as $constraint) {
355 $this->addGetterConstraint($property, $constraint);
362 * @param string $property
363 * @param string $method
364 * @param Constraint[] $constraints
368 public function addGetterMethodConstraints($property, $method, array $constraints)
370 foreach ($constraints as $constraint) {
371 $this->addGetterMethodConstraint($property, $method, $constraint);
378 * Merges the constraints of the given metadata into this object.
380 * @param ClassMetadata $source The source metadata
382 public function mergeConstraints(ClassMetadata $source)
384 foreach ($source->getConstraints() as $constraint) {
385 $this->addConstraint(clone $constraint);
388 foreach ($source->getConstrainedProperties() as $property) {
389 foreach ($source->getPropertyMetadata($property) as $member) {
390 $member = clone $member;
392 foreach ($member->getConstraints() as $constraint) {
393 if (in_array($constraint::DEFAULT_GROUP, $constraint->groups, true)) {
394 $member->constraintsByGroup[$this->getDefaultGroup()][] = $constraint;
397 $constraint->addImplicitGroupName($this->getDefaultGroup());
400 $this->addPropertyMetadata($member);
402 if ($member instanceof MemberMetadata && !$member->isPrivate($this->name)) {
403 $property = $member->getPropertyName();
405 if ($member instanceof PropertyMetadata && !isset($this->properties[$property])) {
406 $this->properties[$property] = $member;
407 } elseif ($member instanceof GetterMetadata && !isset($this->getters[$property])) {
408 $this->getters[$property] = $member;
416 * Adds a member metadata.
418 * @param MemberMetadata $metadata
420 * @deprecated since version 2.6, to be removed in 3.0.
422 protected function addMemberMetadata(MemberMetadata $metadata)
424 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the addPropertyMetadata() method instead.', E_USER_DEPRECATED);
426 $this->addPropertyMetadata($metadata);
430 * Returns true if metadatas of members is present for the given property.
432 * @param string $property The name of the property
436 * @deprecated since version 2.6, to be removed in 3.0. Use {@link hasPropertyMetadata} instead.
438 public function hasMemberMetadatas($property)
440 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the hasPropertyMetadata() method instead.', E_USER_DEPRECATED);
442 return $this->hasPropertyMetadata($property);
446 * Returns all metadatas of members describing the given property.
448 * @param string $property The name of the property
450 * @return MemberMetadata[] An array of MemberMetadata
452 * @deprecated since version 2.6, to be removed in 3.0. Use {@link getPropertyMetadata} instead.
454 public function getMemberMetadatas($property)
456 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the getPropertyMetadata() method instead.', E_USER_DEPRECATED);
458 return $this->getPropertyMetadata($property);
464 public function hasPropertyMetadata($property)
466 return array_key_exists($property, $this->members);
472 public function getPropertyMetadata($property)
474 if (!isset($this->members[$property])) {
478 return $this->members[$property];
484 public function getConstrainedProperties()
486 return array_keys($this->members);
490 * Sets the default group sequence for this class.
492 * @param array $groupSequence An array of group names
496 * @throws GroupDefinitionException
498 public function setGroupSequence($groupSequence)
500 if ($this->isGroupSequenceProvider()) {
501 throw new GroupDefinitionException('Defining a static group sequence is not allowed with a group sequence provider');
504 if (is_array($groupSequence)) {
505 $groupSequence = new GroupSequence($groupSequence);
508 if (in_array(Constraint::DEFAULT_GROUP, $groupSequence->groups, true)) {
509 throw new GroupDefinitionException(sprintf('The group "%s" is not allowed in group sequences', Constraint::DEFAULT_GROUP));
512 if (!in_array($this->getDefaultGroup(), $groupSequence->groups, true)) {
513 throw new GroupDefinitionException(sprintf('The group "%s" is missing in the group sequence', $this->getDefaultGroup()));
516 $this->groupSequence = $groupSequence;
524 public function hasGroupSequence()
526 return $this->groupSequence && count($this->groupSequence->groups) > 0;
532 public function getGroupSequence()
534 return $this->groupSequence;
538 * Returns a ReflectionClass instance for this class.
540 * @return \ReflectionClass
542 public function getReflectionClass()
544 if (!$this->reflClass) {
545 $this->reflClass = new \ReflectionClass($this->getClassName());
548 return $this->reflClass;
552 * Sets whether a group sequence provider should be used.
554 * @param bool $active
556 * @throws GroupDefinitionException
558 public function setGroupSequenceProvider($active)
560 if ($this->hasGroupSequence()) {
561 throw new GroupDefinitionException('Defining a group sequence provider is not allowed with a static group sequence');
564 if (!$this->getReflectionClass()->implementsInterface('Symfony\Component\Validator\GroupSequenceProviderInterface')) {
565 throw new GroupDefinitionException(sprintf('Class "%s" must implement GroupSequenceProviderInterface', $this->name));
568 $this->groupSequenceProvider = $active;
574 public function isGroupSequenceProvider()
576 return $this->groupSequenceProvider;
580 * Class nodes are never cascaded.
584 public function getCascadingStrategy()
586 return CascadingStrategy::NONE;
590 * Adds a property metadata.
592 * @param PropertyMetadataInterface $metadata
594 private function addPropertyMetadata(PropertyMetadataInterface $metadata)
596 $property = $metadata->getPropertyName();
598 $this->members[$property][] = $metadata;