97ccecccb27b0e7f88c288f374725f0fa87e3344
[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 } else {
66 return ObjectAccess::getProperty($object, $propertyName, true);
67 }
68 }
69
70 /**
71 * Checks if the specified property of the given object is valid, and adds
72 * found errors to the $messages object.
73 *
74 * @param mixed $value The value to be validated
75 * @param \Traversable $validators The validators to be called on the value
76 * @param string $propertyName Name of ther property to check
77 */
78 protected function checkProperty($value, $validators, $propertyName)
79 {
80 /** @var \TYPO3\CMS\Extbase\Error\Result $result */
81 $result = null;
82 foreach ($validators as $validator) {
83 if ($validator instanceof ObjectValidatorInterface) {
84 $validator->setValidatedInstancesContainer($this->validatedInstancesContainer);
85 }
86 $currentResult = $validator->validate($value);
87 if ($currentResult->hasMessages()) {
88 if ($result == null) {
89 $result = $currentResult;
90 } else {
91 $result->merge($currentResult);
92 }
93 }
94 }
95 if ($result != null) {
96 $this->result->forProperty($propertyName)->merge($result);
97 }
98 }
99
100 /**
101 * Checks if the given value is valid according to the property validators.
102 *
103 * @param mixed $object The value that should be validated
104 * @api
105 */
106 protected function isValid($object)
107 {
108 foreach ($this->propertyValidators as $propertyName => $validators) {
109 $propertyValue = $this->getPropertyValue($object, $propertyName);
110 $this->checkProperty($propertyValue, $validators, $propertyName);
111 }
112 }
113
114 /**
115 * Checks the given object can be validated by the validator implementation
116 *
117 * @param mixed $object The object to be checked
118 * @return bool TRUE if the given value is an object
119 * @api
120 */
121 public function canValidate($object)
122 {
123 return is_object($object);
124 }
125
126 /**
127 * Adds the given validator for validation of the specified property.
128 *
129 * @param string $propertyName Name of the property to validate
130 * @param ValidatorInterface $validator The property validator
131 * @api
132 */
133 public function addPropertyValidator($propertyName, ValidatorInterface $validator)
134 {
135 if (!isset($this->propertyValidators[$propertyName])) {
136 $this->propertyValidators[$propertyName] = new \SplObjectStorage();
137 }
138 $this->propertyValidators[$propertyName]->attach($validator);
139 }
140
141 /**
142 * @param object $object
143 * @return bool
144 */
145 protected function isValidatedAlready($object)
146 {
147 if ($this->validatedInstancesContainer === null) {
148 $this->validatedInstancesContainer = new \SplObjectStorage();
149 }
150 if ($this->validatedInstancesContainer->contains($object)) {
151 return true;
152 } else {
153 $this->validatedInstancesContainer->attach($object);
154
155 return false;
156 }
157 }
158
159 /**
160 * Returns all property validators - or only validators of the specified property
161 *
162 * @param string $propertyName Name of the property to return validators for
163 * @return array An array of validators
164 */
165 public function getPropertyValidators($propertyName = null)
166 {
167 if ($propertyName !== null) {
168 return (isset($this->propertyValidators[$propertyName])) ? $this->propertyValidators[$propertyName] : [];
169 } else {
170 return $this->propertyValidators;
171 }
172 }
173
174 /**
175 * @var \SplObjectStorage
176 */
177 protected $validatedInstancesContainer;
178
179 /**
180 * Allows to set a container to keep track of validated instances.
181 *
182 * @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances
183 * @api
184 */
185 public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer)
186 {
187 $this->validatedInstancesContainer = $validatedInstancesContainer;
188 }
189 }