a45aaf95943aca79ba2f0a7156ad2deeffeaba62
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Validation / Validator / GenericObjectValidator.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Validation\Validator;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
18
19 /**
20 * A generic object validator which allows for specifying property validators
21 */
22 class GenericObjectValidator extends AbstractValidator implements ObjectValidatorInterface
23 {
24 /**
25 * @var \SplObjectStorage[]
26 */
27 protected $propertyValidators = [];
28
29 /**
30 * Checks if the given value is valid according to the validator, and returns
31 * the Error Messages object which occurred.
32 *
33 * @param mixed $value The value that should be validated
34 * @return \TYPO3\CMS\Extbase\Error\Result
35 * @api
36 */
37 public function validate($value)
38 {
39 $this->result = new \TYPO3\CMS\Extbase\Error\Result();
40 if ($this->acceptsEmptyValues === false || $this->isEmpty($value) === false) {
41 if (!is_object($value)) {
42 $this->addError('Object expected, %1$s given.', 1241099149, [gettype($value)]);
43 } elseif ($this->isValidatedAlready($value) === false) {
44 $this->isValid($value);
45 }
46 }
47
48 return $this->result;
49 }
50
51 /**
52 * Load the property value to be used for validation.
53 *
54 * In case the object is a doctrine proxy, we need to load the real instance first.
55 *
56 * @param object $object
57 * @param string $propertyName
58 * @return mixed
59 */
60 protected function getPropertyValue($object, $propertyName)
61 {
62 // @todo add support for lazy loading proxies, if needed
63 if (ObjectAccess::isPropertyGettable($object, $propertyName)) {
64 return ObjectAccess::getProperty($object, $propertyName);
65 }
66 return ObjectAccess::getProperty($object, $propertyName, true);
67 }
68
69 /**
70 * Checks if the specified property of the given object is valid, and adds
71 * found errors to the $messages object.
72 *
73 * @param mixed $value The value to be validated
74 * @param \Traversable $validators The validators to be called on the value
75 * @param string $propertyName Name of ther property to check
76 */
77 protected function checkProperty($value, $validators, $propertyName)
78 {
79 /** @var \TYPO3\CMS\Extbase\Error\Result $result */
80 $result = null;
81 foreach ($validators as $validator) {
82 if ($validator instanceof ObjectValidatorInterface) {
83 $validator->setValidatedInstancesContainer($this->validatedInstancesContainer);
84 }
85 $currentResult = $validator->validate($value);
86 if ($currentResult->hasMessages()) {
87 if ($result == null) {
88 $result = $currentResult;
89 } else {
90 $result->merge($currentResult);
91 }
92 }
93 }
94 if ($result != null) {
95 $this->result->forProperty($propertyName)->merge($result);
96 }
97 }
98
99 /**
100 * Checks if the given value is valid according to the property validators.
101 *
102 * @param mixed $object The value that should be validated
103 * @api
104 */
105 protected function isValid($object)
106 {
107 foreach ($this->propertyValidators as $propertyName => $validators) {
108 $propertyValue = $this->getPropertyValue($object, $propertyName);
109 $this->checkProperty($propertyValue, $validators, $propertyName);
110 }
111 }
112
113 /**
114 * Checks the given object can be validated by the validator implementation
115 *
116 * @param mixed $object The object to be checked
117 * @return bool TRUE if the given value is an object
118 * @api
119 */
120 public function canValidate($object)
121 {
122 return is_object($object);
123 }
124
125 /**
126 * Adds the given validator for validation of the specified property.
127 *
128 * @param string $propertyName Name of the property to validate
129 * @param ValidatorInterface $validator The property validator
130 * @api
131 */
132 public function addPropertyValidator($propertyName, ValidatorInterface $validator)
133 {
134 if (!isset($this->propertyValidators[$propertyName])) {
135 $this->propertyValidators[$propertyName] = new \SplObjectStorage();
136 }
137 $this->propertyValidators[$propertyName]->attach($validator);
138 }
139
140 /**
141 * @param object $object
142 * @return bool
143 */
144 protected function isValidatedAlready($object)
145 {
146 if ($this->validatedInstancesContainer === null) {
147 $this->validatedInstancesContainer = new \SplObjectStorage();
148 }
149 if ($this->validatedInstancesContainer->contains($object)) {
150 return true;
151 }
152 $this->validatedInstancesContainer->attach($object);
153
154 return false;
155 }
156
157 /**
158 * Returns all property validators - or only validators of the specified property
159 *
160 * @param string $propertyName Name of the property to return validators for
161 * @return array An array of validators
162 */
163 public function getPropertyValidators($propertyName = null)
164 {
165 if ($propertyName !== null) {
166 return (isset($this->propertyValidators[$propertyName])) ? $this->propertyValidators[$propertyName] : [];
167 }
168 return $this->propertyValidators;
169 }
170
171 /**
172 * @var \SplObjectStorage
173 */
174 protected $validatedInstancesContainer;
175
176 /**
177 * Allows to set a container to keep track of validated instances.
178 *
179 * @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances
180 * @api
181 */
182 public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer)
183 {
184 $this->validatedInstancesContainer = $validatedInstancesContainer;
185 }
186 }