19b25e256ef1c1e22143242b1e62ab9f4acc8d7a
[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 = array();
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 $this->result = new \TYPO3\CMS\Extbase\Error\Result();
39 if ($this->acceptsEmptyValues === FALSE || $this->isEmpty($value) === FALSE) {
40 if (!is_object($value)) {
41 $this->addError('Object expected, %1$s given.', 1241099149, array(gettype($value)));
42 } elseif ($this->isValidatedAlready($value) === FALSE) {
43 $this->isValid($value);
44 }
45 }
46
47 return $this->result;
48 }
49
50 /**
51 * Load the property value to be used for validation.
52 *
53 * In case the object is a doctrine proxy, we need to load the real instance first.
54 *
55 * @param object $object
56 * @param string $propertyName
57 * @return mixed
58 */
59 protected function getPropertyValue($object, $propertyName) {
60 // @todo add support for lazy loading proxies, if needed
61 if (ObjectAccess::isPropertyGettable($object, $propertyName)) {
62 return ObjectAccess::getProperty($object, $propertyName);
63 } else {
64 return ObjectAccess::getProperty($object, $propertyName, TRUE);
65 }
66 }
67
68 /**
69 * Checks if the specified property of the given object is valid, and adds
70 * found errors to the $messages object.
71 *
72 * @param mixed $value The value to be validated
73 * @param \Traversable $validators The validators to be called on the value
74 * @param string $propertyName Name of ther property to check
75 * @return void
76 */
77 protected function checkProperty($value, $validators, $propertyName) {
78 /** @var \TYPO3\CMS\Extbase\Error\Result $result */
79 $result = NULL;
80 foreach ($validators as $validator) {
81 if ($validator instanceof ObjectValidatorInterface) {
82 $validator->setValidatedInstancesContainer($this->validatedInstancesContainer);
83 }
84 $currentResult = $validator->validate($value);
85 if ($currentResult->hasMessages()) {
86 if ($result == NULL) {
87 $result = $currentResult;
88 } else {
89 $result->merge($currentResult);
90 }
91 }
92 }
93 if ($result != NULL) {
94 $this->result->forProperty($propertyName)->merge($result);
95 }
96 }
97
98 /**
99 * Checks if the given value is valid according to the property validators.
100 *
101 * @param mixed $object The value that should be validated
102 * @return void
103 * @api
104 */
105 protected function isValid($object) {
106 foreach ($this->propertyValidators as $propertyName => $validators) {
107 $propertyValue = $this->getPropertyValue($object, $propertyName);
108 $this->checkProperty($propertyValue, $validators, $propertyName);
109 }
110 }
111
112 /**
113 * Checks the given object can be validated by the validator implementation
114 *
115 * @param object $object The object to be checked
116 * @return bool TRUE if the given value is an object
117 * @api
118 */
119 public function canValidate($object) {
120 return is_object($object);
121 }
122
123 /**
124 * Adds the given validator for validation of the specified property.
125 *
126 * @param string $propertyName Name of the property to validate
127 * @param ValidatorInterface $validator The property validator
128 * @return void
129 * @api
130 */
131 public function addPropertyValidator($propertyName, ValidatorInterface $validator) {
132 if (!isset($this->propertyValidators[$propertyName])) {
133 $this->propertyValidators[$propertyName] = new \SplObjectStorage();
134 }
135 $this->propertyValidators[$propertyName]->attach($validator);
136 }
137
138 /**
139 * @param object $object
140 * @return bool
141 */
142 protected function isValidatedAlready($object) {
143 if ($this->validatedInstancesContainer === NULL) {
144 $this->validatedInstancesContainer = new \SplObjectStorage();
145 }
146 if ($this->validatedInstancesContainer->contains($object)) {
147 return TRUE;
148 } else {
149 $this->validatedInstancesContainer->attach($object);
150
151 return FALSE;
152 }
153 }
154
155 /**
156 * Returns all property validators - or only validators of the specified property
157 *
158 * @param string $propertyName Name of the property to return validators for
159 * @return array An array of validators
160 */
161 public function getPropertyValidators($propertyName = NULL) {
162 if ($propertyName !== NULL) {
163 return (isset($this->propertyValidators[$propertyName])) ? $this->propertyValidators[$propertyName] : array();
164 } else {
165 return $this->propertyValidators;
166 }
167 }
168
169 /**
170 * @var \SplObjectStorage
171 */
172 protected $validatedInstancesContainer;
173
174 /**
175 * Allows to set a container to keep track of validated instances.
176 *
177 * @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances
178 * @return void
179 * @api
180 */
181 public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer) {
182 $this->validatedInstancesContainer = $validatedInstancesContainer;
183 }
184
185 }