f895ad40402dc79e04b5e87c03d5c7edaaa0e563
[yaffs-website] / vendor / symfony / validator / Mapping / ClassMetadata.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Validator\Mapping;
13
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;
21
22 /**
23  * Default implementation of {@link ClassMetadataInterface}.
24  *
25  * This class supports serialization and cloning.
26  *
27  * @author Bernhard Schussek <bschussek@gmail.com>
28  * @author Fabien Potencier <fabien@symfony.com>
29  */
30 class ClassMetadata extends ElementMetadata implements ClassMetadataInterface
31 {
32     /**
33      * @var string
34      *
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.
38      */
39     public $name;
40
41     /**
42      * @var string
43      *
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.
47      */
48     public $defaultGroup;
49
50     /**
51      * @var MemberMetadata[]
52      *
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.
56      */
57     public $members = array();
58
59     /**
60      * @var PropertyMetadata[]
61      *
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.
65      */
66     public $properties = array();
67
68     /**
69      * @var GetterMetadata[]
70      *
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.
74      */
75     public $getters = array();
76
77     /**
78      * @var array
79      *
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.
83      */
84     public $groupSequence = array();
85
86     /**
87      * @var bool
88      *
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.
92      */
93     public $groupSequenceProvider = false;
94
95     /**
96      * The strategy for traversing traversable objects.
97      *
98      * By default, only instances of {@link \Traversable} are traversed.
99      *
100      * @var int
101      *
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.
105      */
106     public $traversalStrategy = TraversalStrategy::IMPLICIT;
107
108     /**
109      * @var \ReflectionClass
110      */
111     private $reflClass;
112
113     /**
114      * Constructs a metadata for the given class.
115      *
116      * @param string $class
117      */
118     public function __construct($class)
119     {
120         $this->name = $class;
121         // class name without namespace
122         if (false !== $nsSep = strrpos($class, '\\')) {
123             $this->defaultGroup = substr($class, $nsSep + 1);
124         } else {
125             $this->defaultGroup = $class;
126         }
127     }
128
129     /**
130      * {@inheritdoc}
131      *
132      * @deprecated since version 2.5, to be removed in 3.0.
133      */
134     public function accept(ValidationVisitorInterface $visitor, $value, $group, $propertyPath, $propagatedGroup = null)
135     {
136         @trigger_error('The '.__METHOD__.' method is deprecated since version 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
137
138         if (null === $propagatedGroup && Constraint::DEFAULT_GROUP === $group
139                 && ($this->hasGroupSequence() || $this->isGroupSequenceProvider())) {
140             if ($this->hasGroupSequence()) {
141                 $groups = $this->getGroupSequence()->groups;
142             } else {
143                 $groups = $value->getGroupSequence();
144             }
145
146             foreach ($groups as $group) {
147                 $this->accept($visitor, $value, $group, $propertyPath, Constraint::DEFAULT_GROUP);
148
149                 if (count($visitor->getViolations()) > 0) {
150                     break;
151                 }
152             }
153
154             return;
155         }
156
157         $visitor->visit($this, $value, $group, $propertyPath);
158
159         if (null !== $value) {
160             $pathPrefix = empty($propertyPath) ? '' : $propertyPath.'.';
161
162             foreach ($this->getConstrainedProperties() as $property) {
163                 foreach ($this->getPropertyMetadata($property) as $member) {
164                     $member->accept($visitor, $member->getPropertyValue($value), $group, $pathPrefix.$property, $propagatedGroup);
165                 }
166             }
167         }
168     }
169
170     /**
171      * {@inheritdoc}
172      */
173     public function __sleep()
174     {
175         $parentProperties = parent::__sleep();
176
177         // Don't store the cascading strategy. Classes never cascade.
178         unset($parentProperties[array_search('cascadingStrategy', $parentProperties)]);
179
180         return array_merge($parentProperties, array(
181             'getters',
182             'groupSequence',
183             'groupSequenceProvider',
184             'members',
185             'name',
186             'properties',
187             'defaultGroup',
188         ));
189     }
190
191     /**
192      * {@inheritdoc}
193      */
194     public function getClassName()
195     {
196         return $this->name;
197     }
198
199     /**
200      * Returns the name of the default group for this class.
201      *
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
206      * a group sequence.
207      *
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>".
211      *
212      * @return string The name of the default group
213      */
214     public function getDefaultGroup()
215     {
216         return $this->defaultGroup;
217     }
218
219     /**
220      * {@inheritdoc}
221      */
222     public function addConstraint(Constraint $constraint)
223     {
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)
228             ));
229         }
230
231         if ($constraint instanceof Valid) {
232             throw new ConstraintDefinitionException(sprintf(
233                 'The constraint "%s" cannot be put on classes.',
234                 get_class($constraint)
235             ));
236         }
237
238         if ($constraint instanceof Traverse) {
239             if ($constraint->traverse) {
240                 // If traverse is true, traversal should be explicitly enabled
241                 $this->traversalStrategy = TraversalStrategy::TRAVERSE;
242             } else {
243                 // If traverse is false, traversal should be explicitly disabled
244                 $this->traversalStrategy = TraversalStrategy::NONE;
245             }
246
247             // The constraint is not added
248             return $this;
249         }
250
251         $constraint->addImplicitGroupName($this->getDefaultGroup());
252
253         parent::addConstraint($constraint);
254
255         return $this;
256     }
257
258     /**
259      * Adds a constraint to the given property.
260      *
261      * @param string     $property   The name of the property
262      * @param Constraint $constraint The constraint
263      *
264      * @return $this
265      */
266     public function addPropertyConstraint($property, Constraint $constraint)
267     {
268         if (!isset($this->properties[$property])) {
269             $this->properties[$property] = new PropertyMetadata($this->getClassName(), $property);
270
271             $this->addPropertyMetadata($this->properties[$property]);
272         }
273
274         $constraint->addImplicitGroupName($this->getDefaultGroup());
275
276         $this->properties[$property]->addConstraint($constraint);
277
278         return $this;
279     }
280
281     /**
282      * @param string       $property
283      * @param Constraint[] $constraints
284      *
285      * @return $this
286      */
287     public function addPropertyConstraints($property, array $constraints)
288     {
289         foreach ($constraints as $constraint) {
290             $this->addPropertyConstraint($property, $constraint);
291         }
292
293         return $this;
294     }
295
296     /**
297      * Adds a constraint to the getter of the given property.
298      *
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".
301      *
302      * @param string     $property   The name of the property
303      * @param Constraint $constraint The constraint
304      *
305      * @return $this
306      */
307     public function addGetterConstraint($property, Constraint $constraint)
308     {
309         if (!isset($this->getters[$property])) {
310             $this->getters[$property] = new GetterMetadata($this->getClassName(), $property);
311
312             $this->addPropertyMetadata($this->getters[$property]);
313         }
314
315         $constraint->addImplicitGroupName($this->getDefaultGroup());
316
317         $this->getters[$property]->addConstraint($constraint);
318
319         return $this;
320     }
321
322     /**
323      * Adds a constraint to the getter of the given property.
324      *
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
328      *
329      * @return $this
330      */
331     public function addGetterMethodConstraint($property, $method, Constraint $constraint)
332     {
333         if (!isset($this->getters[$property])) {
334             $this->getters[$property] = new GetterMetadata($this->getClassName(), $property, $method);
335
336             $this->addPropertyMetadata($this->getters[$property]);
337         }
338
339         $constraint->addImplicitGroupName($this->getDefaultGroup());
340
341         $this->getters[$property]->addConstraint($constraint);
342
343         return $this;
344     }
345
346     /**
347      * @param string       $property
348      * @param Constraint[] $constraints
349      *
350      * @return $this
351      */
352     public function addGetterConstraints($property, array $constraints)
353     {
354         foreach ($constraints as $constraint) {
355             $this->addGetterConstraint($property, $constraint);
356         }
357
358         return $this;
359     }
360
361     /**
362      * @param string       $property
363      * @param string       $method
364      * @param Constraint[] $constraints
365      *
366      * @return $this
367      */
368     public function addGetterMethodConstraints($property, $method, array $constraints)
369     {
370         foreach ($constraints as $constraint) {
371             $this->addGetterMethodConstraint($property, $method, $constraint);
372         }
373
374         return $this;
375     }
376
377     /**
378      * Merges the constraints of the given metadata into this object.
379      *
380      * @param ClassMetadata $source The source metadata
381      */
382     public function mergeConstraints(ClassMetadata $source)
383     {
384         foreach ($source->getConstraints() as $constraint) {
385             $this->addConstraint(clone $constraint);
386         }
387
388         foreach ($source->getConstrainedProperties() as $property) {
389             foreach ($source->getPropertyMetadata($property) as $member) {
390                 $member = clone $member;
391
392                 foreach ($member->getConstraints() as $constraint) {
393                     if (in_array($constraint::DEFAULT_GROUP, $constraint->groups, true)) {
394                         $member->constraintsByGroup[$this->getDefaultGroup()][] = $constraint;
395                     }
396
397                     $constraint->addImplicitGroupName($this->getDefaultGroup());
398                 }
399
400                 $this->addPropertyMetadata($member);
401
402                 if ($member instanceof MemberMetadata && !$member->isPrivate($this->name)) {
403                     $property = $member->getPropertyName();
404
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;
409                     }
410                 }
411             }
412         }
413     }
414
415     /**
416      * Adds a member metadata.
417      *
418      * @param MemberMetadata $metadata
419      *
420      * @deprecated since version 2.6, to be removed in 3.0.
421      */
422     protected function addMemberMetadata(MemberMetadata $metadata)
423     {
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);
425
426         $this->addPropertyMetadata($metadata);
427     }
428
429     /**
430      * Returns true if metadatas of members is present for the given property.
431      *
432      * @param string $property The name of the property
433      *
434      * @return bool
435      *
436      * @deprecated since version 2.6, to be removed in 3.0. Use {@link hasPropertyMetadata} instead.
437      */
438     public function hasMemberMetadatas($property)
439     {
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);
441
442         return $this->hasPropertyMetadata($property);
443     }
444
445     /**
446      * Returns all metadatas of members describing the given property.
447      *
448      * @param string $property The name of the property
449      *
450      * @return MemberMetadata[] An array of MemberMetadata
451      *
452      * @deprecated since version 2.6, to be removed in 3.0. Use {@link getPropertyMetadata} instead.
453      */
454     public function getMemberMetadatas($property)
455     {
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);
457
458         return $this->getPropertyMetadata($property);
459     }
460
461     /**
462      * {@inheritdoc}
463      */
464     public function hasPropertyMetadata($property)
465     {
466         return array_key_exists($property, $this->members);
467     }
468
469     /**
470      * {@inheritdoc}
471      */
472     public function getPropertyMetadata($property)
473     {
474         if (!isset($this->members[$property])) {
475             return array();
476         }
477
478         return $this->members[$property];
479     }
480
481     /**
482      * {@inheritdoc}
483      */
484     public function getConstrainedProperties()
485     {
486         return array_keys($this->members);
487     }
488
489     /**
490      * Sets the default group sequence for this class.
491      *
492      * @param array $groupSequence An array of group names
493      *
494      * @return $this
495      *
496      * @throws GroupDefinitionException
497      */
498     public function setGroupSequence($groupSequence)
499     {
500         if ($this->isGroupSequenceProvider()) {
501             throw new GroupDefinitionException('Defining a static group sequence is not allowed with a group sequence provider');
502         }
503
504         if (is_array($groupSequence)) {
505             $groupSequence = new GroupSequence($groupSequence);
506         }
507
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));
510         }
511
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()));
514         }
515
516         $this->groupSequence = $groupSequence;
517
518         return $this;
519     }
520
521     /**
522      * {@inheritdoc}
523      */
524     public function hasGroupSequence()
525     {
526         return $this->groupSequence && count($this->groupSequence->groups) > 0;
527     }
528
529     /**
530      * {@inheritdoc}
531      */
532     public function getGroupSequence()
533     {
534         return $this->groupSequence;
535     }
536
537     /**
538      * Returns a ReflectionClass instance for this class.
539      *
540      * @return \ReflectionClass
541      */
542     public function getReflectionClass()
543     {
544         if (!$this->reflClass) {
545             $this->reflClass = new \ReflectionClass($this->getClassName());
546         }
547
548         return $this->reflClass;
549     }
550
551     /**
552      * Sets whether a group sequence provider should be used.
553      *
554      * @param bool $active
555      *
556      * @throws GroupDefinitionException
557      */
558     public function setGroupSequenceProvider($active)
559     {
560         if ($this->hasGroupSequence()) {
561             throw new GroupDefinitionException('Defining a group sequence provider is not allowed with a static group sequence');
562         }
563
564         if (!$this->getReflectionClass()->implementsInterface('Symfony\Component\Validator\GroupSequenceProviderInterface')) {
565             throw new GroupDefinitionException(sprintf('Class "%s" must implement GroupSequenceProviderInterface', $this->name));
566         }
567
568         $this->groupSequenceProvider = $active;
569     }
570
571     /**
572      * {@inheritdoc}
573      */
574     public function isGroupSequenceProvider()
575     {
576         return $this->groupSequenceProvider;
577     }
578
579     /**
580      * Class nodes are never cascaded.
581      *
582      * {@inheritdoc}
583      */
584     public function getCascadingStrategy()
585     {
586         return CascadingStrategy::NONE;
587     }
588
589     /**
590      * Adds a property metadata.
591      *
592      * @param PropertyMetadataInterface $metadata
593      */
594     private function addPropertyMetadata(PropertyMetadataInterface $metadata)
595     {
596         $property = $metadata->getPropertyName();
597
598         $this->members[$property][] = $metadata;
599     }
600 }