[TASK] EXT:form - Optimize file upload/ handling of files
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Domain / Validator / ValidationElementValidator.php
1 <?php
2 namespace TYPO3\CMS\Form\Domain\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 use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface;
19
20 /**
21 * A generic object validator which allows for specifying property validators
22 */
23 class ValidationElementValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator implements \TYPO3\CMS\Extbase\Validation\Validator\ObjectValidatorInterface {
24
25 /**
26 * @var \SplObjectStorage[]
27 */
28 protected $propertyValidators = array();
29
30 /**
31 * @var \TYPO3\CMS\Form\Utility\SessionUtility
32 */
33 protected $sessionUtility;
34
35 /**
36 * @param \TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility
37 */
38 public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility) {
39 $this->sessionUtility = $sessionUtility;
40 }
41
42 /**
43 * Checks if the given value is valid according to the validator, and returns
44 * the Error Messages object which occurred.
45 *
46 * @param mixed $value The value that should be validated
47 * @return \TYPO3\CMS\Extbase\Error\Result
48 * @api
49 */
50 public function validate($value) {
51 $this->result = new \TYPO3\CMS\Extbase\Error\Result();
52 if ($this->acceptsEmptyValues === FALSE || $this->isEmpty($value) === FALSE) {
53 if (!is_object($value)) {
54 $this->addError('Object expected, %1$s given.', 1241099149, array(gettype($value)));
55 } elseif ($this->isValidatedAlready($value) === FALSE) {
56 $this->isValid($value);
57 }
58 }
59
60 return $this->result;
61 }
62
63 /**
64 * Load the property value to be used for validation.
65 *
66 * In case the object is a doctrine proxy, we need to load the real instance first.
67 *
68 * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $validationElement
69 * @param string $propertyName
70 * @return mixed
71 */
72 protected function getPropertyValue(\TYPO3\CMS\Form\Domain\Model\ValidationElement $validationElement, $propertyName) {
73 /**
74 * If a confirmation page is set and a fileupload was done before
75 * there is no incoming data if the process action is called.
76 * The data is only in the session at this time.
77 * This results in a negative validation (if a validation is set).
78 * Therefore, look first in the session.
79 */
80 if ($this->sessionUtility->getSessionData($propertyName)) {
81 $propertyValue = $this->sessionUtility->getSessionData($propertyName);
82 } else {
83 $propertyValue = $validationElement->getIncomingField($propertyName);
84 }
85 return $propertyValue;
86 }
87
88 /**
89 * Checks if the specified property of the given object is valid, and adds
90 * found errors to the $messages object.
91 *
92 * @param mixed $value The value to be validated
93 * @param \Traversable $validators The validators to be called on the value
94 * @param string $propertyName Name of ther property to check
95 * @return void
96 */
97 protected function checkProperty($value, $validators, $propertyName) {
98 /** @var \TYPO3\CMS\Extbase\Error\Result $result */
99 $result = NULL;
100 foreach ($validators as $validator) {
101 if ($validator instanceof ObjectValidatorInterface) {
102 $validator->setValidatedInstancesContainer($this->validatedInstancesContainer);
103 }
104
105 /**
106 * File upload validation.
107 *
108 * If a $_FILES array is found in the request data,
109 * iterate over all requested files and validate each
110 * single file.
111 */
112 if (
113 isset($value[0]['name'])
114 && isset($value[0]['type'])
115 && isset($value[0]['tmp_name'])
116 && isset($value[0]['size'])
117 ) {
118 foreach ($value as $file) {
119 $currentResult = $validator->validate($file);
120 if ($currentResult->hasMessages()) {
121 if ($result == NULL) {
122 $result = $currentResult;
123 } else {
124 $result->merge($currentResult);
125 }
126 }
127 }
128 } else {
129 $currentResult = $validator->validate($value);
130 if ($currentResult->hasMessages()) {
131 if ($result == NULL) {
132 $result = $currentResult;
133 } else {
134 $result->merge($currentResult);
135 }
136 }
137 }
138 }
139 if ($result != NULL) {
140 $this->result->forProperty($propertyName)->merge($result);
141 }
142 }
143
144 /**
145 * Checks if the given value is valid according to the property validators.
146 *
147 * @param mixed $object The value that should be validated
148 * @return void
149 * @api
150 */
151 protected function isValid($object) {
152 foreach ($this->propertyValidators as $propertyName => $validators) {
153 $propertyValue = $this->getPropertyValue($object, $propertyName);
154 $this->checkProperty($propertyValue, $validators, $propertyName);
155 }
156 }
157
158 /**
159 * Checks the given object can be validated by the validator implementation
160 *
161 * @param mixed $object The object to be checked
162 * @return bool TRUE if the given value can be validated
163 * @api
164 */
165 public function canValidate($object) {
166 if (
167 is_object($object)
168 && $object instanceof \TYPO3\CMS\Form\Domain\Model\ValidationElement
169 ) {
170 return TRUE;
171 }
172 return FALSE;
173 }
174
175 /**
176 * Adds the given validator for validation of the specified property.
177 *
178 * @param string $propertyName Name of the property to validate
179 * @param ValidatorInterface $validator The property validator
180 * @return void
181 * @api
182 */
183 public function addPropertyValidator($propertyName, ValidatorInterface $validator) {
184 if (!isset($this->propertyValidators[$propertyName])) {
185 $this->propertyValidators[$propertyName] = new \SplObjectStorage();
186 }
187 $this->propertyValidators[$propertyName]->attach($validator);
188 }
189
190 /**
191 * @param object $object
192 * @return bool
193 */
194 protected function isValidatedAlready($object) {
195 if ($this->validatedInstancesContainer === NULL) {
196 $this->validatedInstancesContainer = new \SplObjectStorage();
197 }
198 if ($this->validatedInstancesContainer->contains($object)) {
199 return TRUE;
200 } else {
201 $this->validatedInstancesContainer->attach($object);
202
203 return FALSE;
204 }
205 }
206
207 /**
208 * Returns all property validators - or only validators of the specified property
209 *
210 * @param string $propertyName Name of the property to return validators for
211 * @return array An array of validators
212 */
213 public function getPropertyValidators($propertyName = NULL) {
214 if ($propertyName !== NULL) {
215 return (isset($this->propertyValidators[$propertyName])) ? $this->propertyValidators[$propertyName] : array();
216 } else {
217 return $this->propertyValidators;
218 }
219 }
220
221 /**
222 * @return int
223 */
224 public function countPropertyValidators() {
225 $count = 0;
226 foreach ($this->propertyValidators as $propertyValidators) {
227 $count += $propertyValidators->count();
228 }
229 return $count;
230 }
231
232 /**
233 * @var \SplObjectStorage
234 */
235 protected $validatedInstancesContainer;
236
237 /**
238 * Allows to set a container to keep track of validated instances.
239 *
240 * @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances
241 * @return void
242 * @api
243 */
244 public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer) {
245 $this->validatedInstancesContainer = $validatedInstancesContainer;
246 }
247
248 }