PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.23.1
MailPoet – Newsletters, Email Marketing, and Automation v5.23.1
5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor-prefixed / symfony / validator / Validator / RecursiveContextualValidator.php
mailpoet / vendor-prefixed / symfony / validator / Validator Last commit date
ContextualValidatorInterface.php 4 years ago LazyProperty.php 4 years ago RecursiveContextualValidator.php 6 months ago RecursiveValidator.php 4 years ago TraceableValidator.php 4 years ago ValidatorInterface.php 4 years ago index.php 3 years ago
RecursiveContextualValidator.php
417 lines
1 <?php
2 namespace MailPoetVendor\Symfony\Component\Validator\Validator;
3 if (!defined('ABSPATH')) exit;
4 use MailPoetVendor\Symfony\Component\Validator\Constraint;
5 use MailPoetVendor\Symfony\Component\Validator\Constraints\Composite;
6 use MailPoetVendor\Symfony\Component\Validator\Constraints\Existence;
7 use MailPoetVendor\Symfony\Component\Validator\Constraints\GroupSequence;
8 use MailPoetVendor\Symfony\Component\Validator\Constraints\Valid;
9 use MailPoetVendor\Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
10 use MailPoetVendor\Symfony\Component\Validator\Context\ExecutionContext;
11 use MailPoetVendor\Symfony\Component\Validator\Context\ExecutionContextInterface;
12 use MailPoetVendor\Symfony\Component\Validator\Exception\ConstraintDefinitionException;
13 use MailPoetVendor\Symfony\Component\Validator\Exception\NoSuchMetadataException;
14 use MailPoetVendor\Symfony\Component\Validator\Exception\RuntimeException;
15 use MailPoetVendor\Symfony\Component\Validator\Exception\UnexpectedValueException;
16 use MailPoetVendor\Symfony\Component\Validator\Exception\UnsupportedMetadataException;
17 use MailPoetVendor\Symfony\Component\Validator\Exception\ValidatorException;
18 use MailPoetVendor\Symfony\Component\Validator\Mapping\CascadingStrategy;
19 use MailPoetVendor\Symfony\Component\Validator\Mapping\ClassMetadataInterface;
20 use MailPoetVendor\Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
21 use MailPoetVendor\Symfony\Component\Validator\Mapping\GenericMetadata;
22 use MailPoetVendor\Symfony\Component\Validator\Mapping\GetterMetadata;
23 use MailPoetVendor\Symfony\Component\Validator\Mapping\MetadataInterface;
24 use MailPoetVendor\Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
25 use MailPoetVendor\Symfony\Component\Validator\Mapping\TraversalStrategy;
26 use MailPoetVendor\Symfony\Component\Validator\ObjectInitializerInterface;
27 use MailPoetVendor\Symfony\Component\Validator\Util\PropertyPath;
28 class RecursiveContextualValidator implements ContextualValidatorInterface
29 {
30 private $context;
31 private $defaultPropertyPath;
32 private $defaultGroups;
33 private $metadataFactory;
34 private $validatorFactory;
35 private $objectInitializers;
36 public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = [])
37 {
38 $this->context = $context;
39 $this->defaultPropertyPath = $context->getPropertyPath();
40 $this->defaultGroups = [$context->getGroup() ?: Constraint::DEFAULT_GROUP];
41 $this->metadataFactory = $metadataFactory;
42 $this->validatorFactory = $validatorFactory;
43 $this->objectInitializers = $objectInitializers;
44 }
45 public function atPath(string $path)
46 {
47 $this->defaultPropertyPath = $this->context->getPropertyPath($path);
48 return $this;
49 }
50 public function validate($value, $constraints = null, $groups = null)
51 {
52 $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
53 $previousValue = $this->context->getValue();
54 $previousObject = $this->context->getObject();
55 $previousMetadata = $this->context->getMetadata();
56 $previousPath = $this->context->getPropertyPath();
57 $previousGroup = $this->context->getGroup();
58 $previousConstraint = null;
59 if ($this->context instanceof ExecutionContext || \method_exists($this->context, 'getConstraint')) {
60 $previousConstraint = $this->context->getConstraint();
61 }
62 // If explicit constraints are passed, validate the value against
63 // those constraints
64 if (null !== $constraints) {
65 // You can pass a single constraint or an array of constraints
66 // Make sure to deal with an array in the rest of the code
67 if (!\is_array($constraints)) {
68 $constraints = [$constraints];
69 }
70 $metadata = new GenericMetadata();
71 $metadata->addConstraints($constraints);
72 $this->validateGenericNode($value, $previousObject, \is_object($value) ? $this->generateCacheKey($value) : null, $metadata, $this->defaultPropertyPath, $groups, null, TraversalStrategy::IMPLICIT, $this->context);
73 $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath);
74 $this->context->setGroup($previousGroup);
75 if (null !== $previousConstraint) {
76 $this->context->setConstraint($previousConstraint);
77 }
78 return $this;
79 }
80 // If an object is passed without explicit constraints, validate that
81 // object against the constraints defined for the object's class
82 if (\is_object($value)) {
83 $this->validateObject($value, $this->defaultPropertyPath, $groups, TraversalStrategy::IMPLICIT, $this->context);
84 $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath);
85 $this->context->setGroup($previousGroup);
86 return $this;
87 }
88 // If an array is passed without explicit constraints, validate each
89 // object in the array
90 if (\is_array($value)) {
91 $this->validateEachObjectIn($value, $this->defaultPropertyPath, $groups, $this->context);
92 $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath);
93 $this->context->setGroup($previousGroup);
94 return $this;
95 }
96 throw new RuntimeException(\sprintf('Cannot validate values of type "%s" automatically. Please provide a constraint.', \get_debug_type($value)));
97 }
98 public function validateProperty(object $object, string $propertyName, $groups = null)
99 {
100 $classMetadata = $this->metadataFactory->getMetadataFor($object);
101 if (!$classMetadata instanceof ClassMetadataInterface) {
102 throw new ValidatorException(\sprintf('The metadata factory should return instances of "\\Symfony\\Component\\Validator\\Mapping\\ClassMetadataInterface", got: "%s".', \get_debug_type($classMetadata)));
103 }
104 $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName);
105 $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
106 $cacheKey = $this->generateCacheKey($object);
107 $propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName);
108 $previousValue = $this->context->getValue();
109 $previousObject = $this->context->getObject();
110 $previousMetadata = $this->context->getMetadata();
111 $previousPath = $this->context->getPropertyPath();
112 $previousGroup = $this->context->getGroup();
113 foreach ($propertyMetadatas as $propertyMetadata) {
114 $propertyValue = $propertyMetadata->getPropertyValue($object);
115 $this->validateGenericNode($propertyValue, $object, $cacheKey . ':' . \get_class($object) . ':' . $propertyName, $propertyMetadata, $propertyPath, $groups, null, TraversalStrategy::IMPLICIT, $this->context);
116 }
117 $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath);
118 $this->context->setGroup($previousGroup);
119 return $this;
120 }
121 public function validatePropertyValue($objectOrClass, string $propertyName, $value, $groups = null)
122 {
123 $classMetadata = $this->metadataFactory->getMetadataFor($objectOrClass);
124 if (!$classMetadata instanceof ClassMetadataInterface) {
125 throw new ValidatorException(\sprintf('The metadata factory should return instances of "\\Symfony\\Component\\Validator\\Mapping\\ClassMetadataInterface", got: "%s".', \get_debug_type($classMetadata)));
126 }
127 $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName);
128 $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
129 if (\is_object($objectOrClass)) {
130 $object = $objectOrClass;
131 $class = \get_class($object);
132 $cacheKey = $this->generateCacheKey($objectOrClass);
133 $propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName);
134 } else {
135 // $objectOrClass contains a class ?name
136 $object = null;
137 $class = $objectOrClass;
138 $cacheKey = null;
139 $propertyPath = $this->defaultPropertyPath;
140 }
141 $previousValue = $this->context->getValue();
142 $previousObject = $this->context->getObject();
143 $previousMetadata = $this->context->getMetadata();
144 $previousPath = $this->context->getPropertyPath();
145 $previousGroup = $this->context->getGroup();
146 foreach ($propertyMetadatas as $propertyMetadata) {
147 $this->validateGenericNode($value, $object, $cacheKey . ':' . $class . ':' . $propertyName, $propertyMetadata, $propertyPath, $groups, null, TraversalStrategy::IMPLICIT, $this->context);
148 }
149 $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath);
150 $this->context->setGroup($previousGroup);
151 return $this;
152 }
153 public function getViolations()
154 {
155 return $this->context->getViolations();
156 }
157 protected function normalizeGroups($groups)
158 {
159 if (\is_array($groups)) {
160 return $groups;
161 }
162 return [$groups];
163 }
164 private function validateObject(object $object, string $propertyPath, array $groups, int $traversalStrategy, ExecutionContextInterface $context)
165 {
166 try {
167 $classMetadata = $this->metadataFactory->getMetadataFor($object);
168 if (!$classMetadata instanceof ClassMetadataInterface) {
169 throw new UnsupportedMetadataException(\sprintf('The metadata factory should return instances of "Symfony\\Component\\Validator\\Mapping\\ClassMetadataInterface", got: "%s".', \get_debug_type($classMetadata)));
170 }
171 $this->validateClassNode($object, $this->generateCacheKey($object), $classMetadata, $propertyPath, $groups, null, $traversalStrategy, $context);
172 } catch (NoSuchMetadataException $e) {
173 // Rethrow if not Traversable
174 if (!$object instanceof \Traversable) {
175 throw $e;
176 }
177 // Rethrow unless IMPLICIT or TRAVERSE
178 if (!($traversalStrategy & (TraversalStrategy::IMPLICIT | TraversalStrategy::TRAVERSE))) {
179 throw $e;
180 }
181 $this->validateEachObjectIn($object, $propertyPath, $groups, $context);
182 }
183 }
184 private function validateEachObjectIn(iterable $collection, string $propertyPath, array $groups, ExecutionContextInterface $context)
185 {
186 foreach ($collection as $key => $value) {
187 if (\is_array($value)) {
188 // Also traverse nested arrays
189 $this->validateEachObjectIn($value, $propertyPath . '[' . $key . ']', $groups, $context);
190 continue;
191 }
192 // Scalar and null values in the collection are ignored
193 if (\is_object($value)) {
194 $this->validateObject($value, $propertyPath . '[' . $key . ']', $groups, TraversalStrategy::IMPLICIT, $context);
195 }
196 }
197 }
198 private function validateClassNode(object $object, ?string $cacheKey, ClassMetadataInterface $metadata, string $propertyPath, array $groups, ?array $cascadedGroups, int $traversalStrategy, ExecutionContextInterface $context)
199 {
200 $context->setNode($object, $object, $metadata, $propertyPath);
201 if (!$context->isObjectInitialized($cacheKey)) {
202 foreach ($this->objectInitializers as $initializer) {
203 $initializer->initialize($object);
204 }
205 $context->markObjectAsInitialized($cacheKey);
206 }
207 foreach ($groups as $key => $group) {
208 // If the "Default" group is replaced by a group sequence, remember
209 // to cascade the "Default" group when traversing the group
210 // sequence
211 $defaultOverridden = \false;
212 // Use the object hash for group sequences
213 $groupHash = \is_object($group) ? $this->generateCacheKey($group, \true) : $group;
214 if ($context->isGroupValidated($cacheKey, $groupHash)) {
215 // Skip this group when validating the properties and when
216 // traversing the object
217 unset($groups[$key]);
218 continue;
219 }
220 $context->markGroupAsValidated($cacheKey, $groupHash);
221 // Replace the "Default" group by the group sequence defined
222 // for the class, if applicable.
223 // This is done after checking the cache, so that
224 // spl_object_hash() isn't called for this sequence and
225 // "Default" is used instead in the cache. This is useful
226 // if the getters below return different group sequences in
227 // every call.
228 if (Constraint::DEFAULT_GROUP === $group) {
229 if ($metadata->hasGroupSequence()) {
230 // The group sequence is statically defined for the class
231 $group = $metadata->getGroupSequence();
232 $defaultOverridden = \true;
233 } elseif ($metadata->isGroupSequenceProvider()) {
234 // The group sequence is dynamically obtained from the validated
235 // object
236 $group = $object->getGroupSequence();
237 $defaultOverridden = \true;
238 if (!$group instanceof GroupSequence) {
239 $group = new GroupSequence($group);
240 }
241 }
242 }
243 // If the groups (=[<G1,G2>,G3,G4]) contain a group sequence
244 // (=<G1,G2>), then call validateClassNode() with each entry of the
245 // group sequence and abort if necessary (G1, G2)
246 if ($group instanceof GroupSequence) {
247 $this->stepThroughGroupSequence($object, $object, $cacheKey, $metadata, $propertyPath, $traversalStrategy, $group, $defaultOverridden ? Constraint::DEFAULT_GROUP : null, $context);
248 // Skip the group sequence when validating properties, because
249 // stepThroughGroupSequence() already validates the properties
250 unset($groups[$key]);
251 continue;
252 }
253 $this->validateInGroup($object, $cacheKey, $metadata, $group, $context);
254 }
255 // If no more groups should be validated for the property nodes,
256 // we can safely quit
257 if (0 === \count($groups)) {
258 return;
259 }
260 // Validate all properties against their constraints
261 foreach ($metadata->getConstrainedProperties() as $propertyName) {
262 // If constraints are defined both on the getter of a property as
263 // well as on the property itself, then getPropertyMetadata()
264 // returns two metadata objects, not just one
265 foreach ($metadata->getPropertyMetadata($propertyName) as $propertyMetadata) {
266 if (!$propertyMetadata instanceof PropertyMetadataInterface) {
267 throw new UnsupportedMetadataException(\sprintf('The property metadata instances should implement "Symfony\\Component\\Validator\\Mapping\\PropertyMetadataInterface", got: "%s".', \get_debug_type($propertyMetadata)));
268 }
269 if ($propertyMetadata instanceof GetterMetadata) {
270 $propertyValue = new LazyProperty(static function () use($propertyMetadata, $object) {
271 return $propertyMetadata->getPropertyValue($object);
272 });
273 } else {
274 $propertyValue = $propertyMetadata->getPropertyValue($object);
275 }
276 $this->validateGenericNode($propertyValue, $object, $cacheKey . ':' . \get_class($object) . ':' . $propertyName, $propertyMetadata, PropertyPath::append($propertyPath, $propertyName), $groups, $cascadedGroups, TraversalStrategy::IMPLICIT, $context);
277 }
278 }
279 // If no specific traversal strategy was requested when this method
280 // was called, use the traversal strategy of the class' metadata
281 if ($traversalStrategy & TraversalStrategy::IMPLICIT) {
282 $traversalStrategy = $metadata->getTraversalStrategy();
283 }
284 // Traverse only if IMPLICIT or TRAVERSE
285 if (!($traversalStrategy & (TraversalStrategy::IMPLICIT | TraversalStrategy::TRAVERSE))) {
286 return;
287 }
288 // If IMPLICIT, stop unless we deal with a Traversable
289 if ($traversalStrategy & TraversalStrategy::IMPLICIT && !$object instanceof \Traversable) {
290 return;
291 }
292 // If TRAVERSE, fail if we have no Traversable
293 if (!$object instanceof \Traversable) {
294 throw new ConstraintDefinitionException(\sprintf('Traversal was enabled for "%s", but this class does not implement "\\Traversable".', \get_debug_type($object)));
295 }
296 $this->validateEachObjectIn($object, $propertyPath, $groups, $context);
297 }
298 private function validateGenericNode($value, ?object $object, ?string $cacheKey, ?MetadataInterface $metadata, string $propertyPath, array $groups, ?array $cascadedGroups, int $traversalStrategy, ExecutionContextInterface $context)
299 {
300 $context->setNode($value, $object, $metadata, $propertyPath);
301 foreach ($groups as $key => $group) {
302 if ($group instanceof GroupSequence) {
303 $this->stepThroughGroupSequence($value, $object, $cacheKey, $metadata, $propertyPath, $traversalStrategy, $group, null, $context);
304 // Skip the group sequence when cascading, as the cascading
305 // logic is already done in stepThroughGroupSequence()
306 unset($groups[$key]);
307 continue;
308 }
309 $this->validateInGroup($value, $cacheKey, $metadata, $group, $context);
310 }
311 if (0 === \count($groups)) {
312 return;
313 }
314 if (null === $value) {
315 return;
316 }
317 $cascadingStrategy = $metadata->getCascadingStrategy();
318 // Quit unless we cascade
319 if (!($cascadingStrategy & CascadingStrategy::CASCADE)) {
320 return;
321 }
322 // If no specific traversal strategy was requested when this method
323 // was called, use the traversal strategy of the node's metadata
324 if ($traversalStrategy & TraversalStrategy::IMPLICIT) {
325 $traversalStrategy = $metadata->getTraversalStrategy();
326 }
327 // The $cascadedGroups property is set, if the "Default" group is
328 // overridden by a group sequence
329 // See validateClassNode()
330 $cascadedGroups = null !== $cascadedGroups && \count($cascadedGroups) > 0 ? $cascadedGroups : $groups;
331 if ($value instanceof LazyProperty) {
332 $value = $value->getPropertyValue();
333 if (null === $value) {
334 return;
335 }
336 }
337 if (\is_array($value)) {
338 // Arrays are always traversed, independent of the specified
339 // traversal strategy
340 $this->validateEachObjectIn($value, $propertyPath, $cascadedGroups, $context);
341 return;
342 }
343 if (!\is_object($value)) {
344 throw new NoSuchMetadataException(\sprintf('Cannot create metadata for non-objects. Got: "%s".', \gettype($value)));
345 }
346 $this->validateObject($value, $propertyPath, $cascadedGroups, $traversalStrategy, $context);
347 // Currently, the traversal strategy can only be TRAVERSE for a
348 // generic node if the cascading strategy is CASCADE. Thus, traversable
349 // objects will always be handled within validateObject() and there's
350 // nothing more to do here.
351 // see GenericMetadata::addConstraint()
352 }
353 private function stepThroughGroupSequence($value, ?object $object, ?string $cacheKey, ?MetadataInterface $metadata, string $propertyPath, int $traversalStrategy, GroupSequence $groupSequence, ?string $cascadedGroup, ExecutionContextInterface $context)
354 {
355 $violationCount = \count($context->getViolations());
356 $cascadedGroups = $cascadedGroup ? [$cascadedGroup] : null;
357 foreach ($groupSequence->groups as $groupInSequence) {
358 $groups = (array) $groupInSequence;
359 if ($metadata instanceof ClassMetadataInterface) {
360 $this->validateClassNode($value, $cacheKey, $metadata, $propertyPath, $groups, $cascadedGroups, $traversalStrategy, $context);
361 } else {
362 $this->validateGenericNode($value, $object, $cacheKey, $metadata, $propertyPath, $groups, $cascadedGroups, $traversalStrategy, $context);
363 }
364 // Abort sequence validation if a violation was generated
365 if (\count($context->getViolations()) > $violationCount) {
366 break;
367 }
368 }
369 }
370 private function validateInGroup($value, ?string $cacheKey, MetadataInterface $metadata, string $group, ExecutionContextInterface $context)
371 {
372 $context->setGroup($group);
373 foreach ($metadata->findConstraints($group) as $constraint) {
374 if ($constraint instanceof Existence) {
375 continue;
376 }
377 // Prevent duplicate validation of constraints, in the case
378 // that constraints belong to multiple validated groups
379 if (null !== $cacheKey) {
380 $constraintHash = $this->generateCacheKey($constraint, \true);
381 // instanceof Valid: In case of using a Valid constraint with many groups
382 // it makes a reference object get validated by each group
383 if ($constraint instanceof Composite || $constraint instanceof Valid) {
384 $constraintHash .= $group;
385 }
386 if ($context->isConstraintValidated($cacheKey, $constraintHash)) {
387 continue;
388 }
389 $context->markConstraintAsValidated($cacheKey, $constraintHash);
390 }
391 $context->setConstraint($constraint);
392 $validator = $this->validatorFactory->getInstance($constraint);
393 $validator->initialize($context);
394 if ($value instanceof LazyProperty) {
395 $value = $value->getPropertyValue();
396 }
397 try {
398 $validator->validate($value, $constraint);
399 } catch (UnexpectedValueException $e) {
400 $context->buildViolation('This value should be of type {{ type }}.')->setParameter('{{ type }}', $e->getExpectedType())->addViolation();
401 }
402 }
403 }
404 private function generateCacheKey(object $object, bool $dependsOnPropertyPath = \false) : string
405 {
406 if ($this->context instanceof ExecutionContext) {
407 $cacheKey = $this->context->generateCacheKey($object);
408 } else {
409 $cacheKey = \spl_object_hash($object);
410 }
411 if ($dependsOnPropertyPath) {
412 $cacheKey .= $this->context->getPropertyPath();
413 }
414 return $cacheKey;
415 }
416 }
417