Security update for Core, with self-updated composer
[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      * @param ClassMetadata $source The source metadata
339      */
340     public function mergeConstraints(ClassMetadata $source)
341     {
342         foreach ($source->getConstraints() as $constraint) {
343             $this->addConstraint(clone $constraint);
344         }
345
346         foreach ($source->getConstrainedProperties() as $property) {
347             foreach ($source->getPropertyMetadata($property) as $member) {
348                 $member = clone $member;
349
350                 foreach ($member->getConstraints() as $constraint) {
351                     if (in_array($constraint::DEFAULT_GROUP, $constraint->groups, true)) {
352                         $member->constraintsByGroup[$this->getDefaultGroup()][] = $constraint;
353                     }
354
355                     $constraint->addImplicitGroupName($this->getDefaultGroup());
356                 }
357
358                 $this->addPropertyMetadata($member);
359
360                 if ($member instanceof MemberMetadata && !$member->isPrivate($this->name)) {
361                     $property = $member->getPropertyName();
362
363                     if ($member instanceof PropertyMetadata && !isset($this->properties[$property])) {
364                         $this->properties[$property] = $member;
365                     } elseif ($member instanceof GetterMetadata && !isset($this->getters[$property])) {
366                         $this->getters[$property] = $member;
367                     }
368                 }
369             }
370         }
371     }
372
373     /**
374      * {@inheritdoc}
375      */
376     public function hasPropertyMetadata($property)
377     {
378         return array_key_exists($property, $this->members);
379     }
380
381     /**
382      * {@inheritdoc}
383      */
384     public function getPropertyMetadata($property)
385     {
386         if (!isset($this->members[$property])) {
387             return array();
388         }
389
390         return $this->members[$property];
391     }
392
393     /**
394      * {@inheritdoc}
395      */
396     public function getConstrainedProperties()
397     {
398         return array_keys($this->members);
399     }
400
401     /**
402      * Sets the default group sequence for this class.
403      *
404      * @param array $groupSequence An array of group names
405      *
406      * @return $this
407      *
408      * @throws GroupDefinitionException
409      */
410     public function setGroupSequence($groupSequence)
411     {
412         if ($this->isGroupSequenceProvider()) {
413             throw new GroupDefinitionException('Defining a static group sequence is not allowed with a group sequence provider');
414         }
415
416         if (is_array($groupSequence)) {
417             $groupSequence = new GroupSequence($groupSequence);
418         }
419
420         if (in_array(Constraint::DEFAULT_GROUP, $groupSequence->groups, true)) {
421             throw new GroupDefinitionException(sprintf('The group "%s" is not allowed in group sequences', Constraint::DEFAULT_GROUP));
422         }
423
424         if (!in_array($this->getDefaultGroup(), $groupSequence->groups, true)) {
425             throw new GroupDefinitionException(sprintf('The group "%s" is missing in the group sequence', $this->getDefaultGroup()));
426         }
427
428         $this->groupSequence = $groupSequence;
429
430         return $this;
431     }
432
433     /**
434      * {@inheritdoc}
435      */
436     public function hasGroupSequence()
437     {
438         return $this->groupSequence && count($this->groupSequence->groups) > 0;
439     }
440
441     /**
442      * {@inheritdoc}
443      */
444     public function getGroupSequence()
445     {
446         return $this->groupSequence;
447     }
448
449     /**
450      * Returns a ReflectionClass instance for this class.
451      *
452      * @return \ReflectionClass
453      */
454     public function getReflectionClass()
455     {
456         if (!$this->reflClass) {
457             $this->reflClass = new \ReflectionClass($this->getClassName());
458         }
459
460         return $this->reflClass;
461     }
462
463     /**
464      * Sets whether a group sequence provider should be used.
465      *
466      * @param bool $active
467      *
468      * @throws GroupDefinitionException
469      */
470     public function setGroupSequenceProvider($active)
471     {
472         if ($this->hasGroupSequence()) {
473             throw new GroupDefinitionException('Defining a group sequence provider is not allowed with a static group sequence');
474         }
475
476         if (!$this->getReflectionClass()->implementsInterface('Symfony\Component\Validator\GroupSequenceProviderInterface')) {
477             throw new GroupDefinitionException(sprintf('Class "%s" must implement GroupSequenceProviderInterface', $this->name));
478         }
479
480         $this->groupSequenceProvider = $active;
481     }
482
483     /**
484      * {@inheritdoc}
485      */
486     public function isGroupSequenceProvider()
487     {
488         return $this->groupSequenceProvider;
489     }
490
491     /**
492      * Class nodes are never cascaded.
493      *
494      * {@inheritdoc}
495      */
496     public function getCascadingStrategy()
497     {
498         return CascadingStrategy::NONE;
499     }
500
501     /**
502      * Adds a property metadata.
503      *
504      * @param PropertyMetadataInterface $metadata
505      */
506     private function addPropertyMetadata(PropertyMetadataInterface $metadata)
507     {
508         $property = $metadata->getPropertyName();
509
510         $this->members[$property][] = $metadata;
511     }
512 }