84b0e3f4f7731dc573c18c4d8764fd23bb3340ff
[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
21 /**
22  * Default implementation of {@link ClassMetadataInterface}.
23  *
24  * This class supports serialization and cloning.
25  *
26  * @author Bernhard Schussek <bschussek@gmail.com>
27  * @author Fabien Potencier <fabien@symfony.com>
28  */
29 class ClassMetadata extends GenericMetadata implements ClassMetadataInterface
30 {
31     /**
32      * @var string
33      *
34      * @internal This property is public in order to reduce the size of the
35      *           class' serialized representation. Do not access it. Use
36      *           {@link getClassName()} instead.
37      */
38     public $name;
39
40     /**
41      * @var string
42      *
43      * @internal This property is public in order to reduce the size of the
44      *           class' serialized representation. Do not access it. Use
45      *           {@link getDefaultGroup()} instead.
46      */
47     public $defaultGroup;
48
49     /**
50      * @var MemberMetadata[][]
51      *
52      * @internal This property is public in order to reduce the size of the
53      *           class' serialized representation. Do not access it. Use
54      *           {@link getPropertyMetadata()} instead.
55      */
56     public $members = array();
57
58     /**
59      * @var PropertyMetadata[]
60      *
61      * @internal This property is public in order to reduce the size of the
62      *           class' serialized representation. Do not access it. Use
63      *           {@link getPropertyMetadata()} instead.
64      */
65     public $properties = array();
66
67     /**
68      * @var GetterMetadata[]
69      *
70      * @internal This property is public in order to reduce the size of the
71      *           class' serialized representation. Do not access it. Use
72      *           {@link getPropertyMetadata()} instead.
73      */
74     public $getters = array();
75
76     /**
77      * @var array
78      *
79      * @internal This property is public in order to reduce the size of the
80      *           class' serialized representation. Do not access it. Use
81      *           {@link getGroupSequence()} instead.
82      */
83     public $groupSequence = array();
84
85     /**
86      * @var bool
87      *
88      * @internal This property is public in order to reduce the size of the
89      *           class' serialized representation. Do not access it. Use
90      *           {@link isGroupSequenceProvider()} instead.
91      */
92     public $groupSequenceProvider = false;
93
94     /**
95      * The strategy for traversing traversable objects.
96      *
97      * By default, only instances of {@link \Traversable} are traversed.
98      *
99      * @var int
100      *
101      * @internal This property is public in order to reduce the size of the
102      *           class' serialized representation. Do not access it. Use
103      *           {@link getTraversalStrategy()} instead.
104      */
105     public $traversalStrategy = TraversalStrategy::IMPLICIT;
106
107     /**
108      * @var \ReflectionClass
109      */
110     private $reflClass;
111
112     /**
113      * Constructs a metadata for the given class.
114      *
115      * @param string $class
116      */
117     public function __construct($class)
118     {
119         $this->name = $class;
120         // class name without namespace
121         if (false !== $nsSep = strrpos($class, '\\')) {
122             $this->defaultGroup = substr($class, $nsSep + 1);
123         } else {
124             $this->defaultGroup = $class;
125         }
126     }
127
128     /**
129      * {@inheritdoc}
130      */
131     public function __sleep()
132     {
133         $parentProperties = parent::__sleep();
134
135         // Don't store the cascading strategy. Classes never cascade.
136         unset($parentProperties[array_search('cascadingStrategy', $parentProperties)]);
137
138         return array_merge($parentProperties, array(
139             'getters',
140             'groupSequence',
141             'groupSequenceProvider',
142             'members',
143             'name',
144             'properties',
145             'defaultGroup',
146         ));
147     }
148
149     /**
150      * {@inheritdoc}
151      */
152     public function getClassName()
153     {
154         return $this->name;
155     }
156
157     /**
158      * Returns the name of the default group for this class.
159      *
160      * For each class, the group "Default" is an alias for the group
161      * "<ClassName>", where <ClassName> is the non-namespaced name of the
162      * class. All constraints implicitly or explicitly assigned to group
163      * "Default" belong to both of these groups, unless the class defines
164      * a group sequence.
165      *
166      * If a class defines a group sequence, validating the class in "Default"
167      * will validate the group sequence. The constraints assigned to "Default"
168      * can still be validated by validating the class in "<ClassName>".
169      *
170      * @return string The name of the default group
171      */
172     public function getDefaultGroup()
173     {
174         return $this->defaultGroup;
175     }
176
177     /**
178      * {@inheritdoc}
179      */
180     public function addConstraint(Constraint $constraint)
181     {
182         if (!in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets())) {
183             throw new ConstraintDefinitionException(sprintf(
184                 'The constraint "%s" cannot be put on classes.',
185                 get_class($constraint)
186             ));
187         }
188
189         if ($constraint instanceof Valid) {
190             throw new ConstraintDefinitionException(sprintf(
191                 'The constraint "%s" cannot be put on classes.',
192                 get_class($constraint)
193             ));
194         }
195
196         if ($constraint instanceof Traverse) {
197             if ($constraint->traverse) {
198                 // If traverse is true, traversal should be explicitly enabled
199                 $this->traversalStrategy = TraversalStrategy::TRAVERSE;
200             } else {
201                 // If traverse is false, traversal should be explicitly disabled
202                 $this->traversalStrategy = TraversalStrategy::NONE;
203             }
204
205             // The constraint is not added
206             return $this;
207         }
208
209         $constraint->addImplicitGroupName($this->getDefaultGroup());
210
211         parent::addConstraint($constraint);
212
213         return $this;
214     }
215
216     /**
217      * Adds a constraint to the given property.
218      *
219      * @param string     $property   The name of the property
220      * @param Constraint $constraint The constraint
221      *
222      * @return $this
223      */
224     public function addPropertyConstraint($property, Constraint $constraint)
225     {
226         if (!isset($this->properties[$property])) {
227             $this->properties[$property] = new PropertyMetadata($this->getClassName(), $property);
228
229             $this->addPropertyMetadata($this->properties[$property]);
230         }
231
232         $constraint->addImplicitGroupName($this->getDefaultGroup());
233
234         $this->properties[$property]->addConstraint($constraint);
235
236         return $this;
237     }
238
239     /**
240      * @param string       $property
241      * @param Constraint[] $constraints
242      *
243      * @return $this
244      */
245     public function addPropertyConstraints($property, array $constraints)
246     {
247         foreach ($constraints as $constraint) {
248             $this->addPropertyConstraint($property, $constraint);
249         }
250
251         return $this;
252     }
253
254     /**
255      * Adds a constraint to the getter of the given property.
256      *
257      * The name of the getter is assumed to be the name of the property with an
258      * uppercased first letter and either the prefix "get" or "is".
259      *
260      * @param string     $property   The name of the property
261      * @param Constraint $constraint The constraint
262      *
263      * @return $this
264      */
265     public function addGetterConstraint($property, Constraint $constraint)
266     {
267         if (!isset($this->getters[$property])) {
268             $this->getters[$property] = new GetterMetadata($this->getClassName(), $property);
269
270             $this->addPropertyMetadata($this->getters[$property]);
271         }
272
273         $constraint->addImplicitGroupName($this->getDefaultGroup());
274
275         $this->getters[$property]->addConstraint($constraint);
276
277         return $this;
278     }
279
280     /**
281      * Adds a constraint to the getter of the given property.
282      *
283      * @param string     $property   The name of the property
284      * @param string     $method     The name of the getter method
285      * @param Constraint $constraint The constraint
286      *
287      * @return $this
288      */
289     public function addGetterMethodConstraint($property, $method, Constraint $constraint)
290     {
291         if (!isset($this->getters[$property])) {
292             $this->getters[$property] = new GetterMetadata($this->getClassName(), $property, $method);
293
294             $this->addPropertyMetadata($this->getters[$property]);
295         }
296
297         $constraint->addImplicitGroupName($this->getDefaultGroup());
298
299         $this->getters[$property]->addConstraint($constraint);
300
301         return $this;
302     }
303
304     /**
305      * @param string       $property
306      * @param Constraint[] $constraints
307      *
308      * @return $this
309      */
310     public function addGetterConstraints($property, array $constraints)
311     {
312         foreach ($constraints as $constraint) {
313             $this->addGetterConstraint($property, $constraint);
314         }
315
316         return $this;
317     }
318
319     /**
320      * @param string       $property
321      * @param string       $method
322      * @param Constraint[] $constraints
323      *
324      * @return $this
325      */
326     public function addGetterMethodConstraints($property, $method, array $constraints)
327     {
328         foreach ($constraints as $constraint) {
329             $this->addGetterMethodConstraint($property, $method, $constraint);
330         }
331
332         return $this;
333     }
334
335     /**
336      * Merges the constraints of the given metadata into this object.
337      */
338     public function mergeConstraints(self $source)
339     {
340         if ($source->isGroupSequenceProvider()) {
341             $this->setGroupSequenceProvider(true);
342         }
343
344         foreach ($source->getConstraints() as $constraint) {
345             $this->addConstraint(clone $constraint);
346         }
347
348         foreach ($source->getConstrainedProperties() as $property) {
349             foreach ($source->getPropertyMetadata($property) as $member) {
350                 $member = clone $member;
351
352                 foreach ($member->getConstraints() as $constraint) {
353                     if (in_array($constraint::DEFAULT_GROUP, $constraint->groups, true)) {
354                         $member->constraintsByGroup[$this->getDefaultGroup()][] = $constraint;
355                     }
356
357                     $constraint->addImplicitGroupName($this->getDefaultGroup());
358                 }
359
360                 $this->addPropertyMetadata($member);
361
362                 if ($member instanceof MemberMetadata && !$member->isPrivate($this->name)) {
363                     $property = $member->getPropertyName();
364
365                     if ($member instanceof PropertyMetadata && !isset($this->properties[$property])) {
366                         $this->properties[$property] = $member;
367                     } elseif ($member instanceof GetterMetadata && !isset($this->getters[$property])) {
368                         $this->getters[$property] = $member;
369                     }
370                 }
371             }
372         }
373     }
374
375     /**
376      * {@inheritdoc}
377      */
378     public function hasPropertyMetadata($property)
379     {
380         return array_key_exists($property, $this->members);
381     }
382
383     /**
384      * {@inheritdoc}
385      */
386     public function getPropertyMetadata($property)
387     {
388         if (!isset($this->members[$property])) {
389             return array();
390         }
391
392         return $this->members[$property];
393     }
394
395     /**
396      * {@inheritdoc}
397      */
398     public function getConstrainedProperties()
399     {
400         return array_keys($this->members);
401     }
402
403     /**
404      * Sets the default group sequence for this class.
405      *
406      * @param array $groupSequence An array of group names
407      *
408      * @return $this
409      *
410      * @throws GroupDefinitionException
411      */
412     public function setGroupSequence($groupSequence)
413     {
414         if ($this->isGroupSequenceProvider()) {
415             throw new GroupDefinitionException('Defining a static group sequence is not allowed with a group sequence provider');
416         }
417
418         if (is_array($groupSequence)) {
419             $groupSequence = new GroupSequence($groupSequence);
420         }
421
422         if (in_array(Constraint::DEFAULT_GROUP, $groupSequence->groups, true)) {
423             throw new GroupDefinitionException(sprintf('The group "%s" is not allowed in group sequences', Constraint::DEFAULT_GROUP));
424         }
425
426         if (!in_array($this->getDefaultGroup(), $groupSequence->groups, true)) {
427             throw new GroupDefinitionException(sprintf('The group "%s" is missing in the group sequence', $this->getDefaultGroup()));
428         }
429
430         $this->groupSequence = $groupSequence;
431
432         return $this;
433     }
434
435     /**
436      * {@inheritdoc}
437      */
438     public function hasGroupSequence()
439     {
440         return $this->groupSequence && count($this->groupSequence->groups) > 0;
441     }
442
443     /**
444      * {@inheritdoc}
445      */
446     public function getGroupSequence()
447     {
448         return $this->groupSequence;
449     }
450
451     /**
452      * Returns a ReflectionClass instance for this class.
453      *
454      * @return \ReflectionClass
455      */
456     public function getReflectionClass()
457     {
458         if (!$this->reflClass) {
459             $this->reflClass = new \ReflectionClass($this->getClassName());
460         }
461
462         return $this->reflClass;
463     }
464
465     /**
466      * Sets whether a group sequence provider should be used.
467      *
468      * @param bool $active
469      *
470      * @throws GroupDefinitionException
471      */
472     public function setGroupSequenceProvider($active)
473     {
474         if ($this->hasGroupSequence()) {
475             throw new GroupDefinitionException('Defining a group sequence provider is not allowed with a static group sequence');
476         }
477
478         if (!$this->getReflectionClass()->implementsInterface('Symfony\Component\Validator\GroupSequenceProviderInterface')) {
479             throw new GroupDefinitionException(sprintf('Class "%s" must implement GroupSequenceProviderInterface', $this->name));
480         }
481
482         $this->groupSequenceProvider = $active;
483     }
484
485     /**
486      * {@inheritdoc}
487      */
488     public function isGroupSequenceProvider()
489     {
490         return $this->groupSequenceProvider;
491     }
492
493     /**
494      * Class nodes are never cascaded.
495      *
496      * {@inheritdoc}
497      */
498     public function getCascadingStrategy()
499     {
500         return CascadingStrategy::NONE;
501     }
502
503     private function addPropertyMetadata(PropertyMetadataInterface $metadata)
504     {
505         $property = $metadata->getPropertyName();
506
507         $this->members[$property][] = $metadata;
508     }
509 }