[!!!][TASK] Remove support for non namespaced classes in Extbase
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Property / PropertyMappingConfiguration.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Property;
3
4 /* *
5 * This script belongs to the Extbase framework *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License as published by the *
9 * Free Software Foundation, either version 3 of the License, or (at your *
10 * option) any later version. *
11 * *
12 * This script is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
15 * General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with the script. *
19 * If not, see http://www.gnu.org/licenses/lgpl.html *
20 * *
21 * The TYPO3 project - inspiring people to share! *
22 * */
23
24 /**
25 * Concrete configuration object for the PropertyMapper.
26 *
27 * @api
28 */
29 class PropertyMappingConfiguration implements PropertyMappingConfigurationInterface
30 {
31 /**
32 * Placeholder in property paths for multi-valued types
33 */
34 const PROPERTY_PATH_PLACEHOLDER = '*';
35
36 /**
37 * multi-dimensional array which stores type-converter specific configuration:
38 * 1. Dimension: Fully qualified class name of the type converter
39 * 2. Dimension: Configuration Key
40 * Value: Configuration Value
41 *
42 * @var array
43 */
44 protected $configuration;
45
46 /**
47 * Stores the configuration for specific child properties.
48 *
49 * @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface[]
50 */
51 protected $subConfigurationForProperty = [];
52
53 /**
54 * Keys which should be renamed
55 *
56 * @var array
57 */
58 protected $mapping = [];
59
60 /**
61 * @var \TYPO3\CMS\Extbase\Property\TypeConverterInterface
62 */
63 protected $typeConverter = null;
64
65 /**
66 * List of allowed property names to be converted
67 *
68 * @var array
69 */
70 protected $propertiesToBeMapped = [];
71
72 /**
73 * List of property names to be skipped during property mapping
74 *
75 * @var array
76 */
77 protected $propertiesToSkip = [];
78
79 /**
80 * List of disallowed property names which will be ignored while property mapping
81 *
82 * @var array
83 */
84 protected $propertiesNotToBeMapped = [];
85
86 /**
87 * If TRUE, unknown properties will be skipped during property mapping
88 *
89 * @var bool
90 */
91 protected $skipUnknownProperties = false;
92
93 /**
94 * If TRUE, unknown properties will be mapped.
95 *
96 * @var bool
97 */
98 protected $mapUnknownProperties = false;
99
100 /**
101 * The behavior is as follows:
102 *
103 * - if a property has been explicitly forbidden using allowAllPropertiesExcept(...), it is directly rejected
104 * - if a property has been allowed using allowProperties(...), it is directly allowed.
105 * - if allowAllProperties* has been called, we allow unknown properties
106 * - else, return FALSE.
107 *
108 * @param string $propertyName
109 * @return bool TRUE if the given propertyName should be mapped, FALSE otherwise.
110 * @api
111 */
112 public function shouldMap($propertyName)
113 {
114 if (isset($this->propertiesNotToBeMapped[$propertyName])) {
115 return false;
116 }
117
118 if (isset($this->propertiesToBeMapped[$propertyName])) {
119 return true;
120 }
121
122 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
123 return true;
124 }
125
126 return $this->mapUnknownProperties;
127 }
128
129 /**
130 * Check if the given $propertyName should be skipped during mapping.
131 *
132 * @param string $propertyName
133 * @return bool
134 * @api
135 */
136 public function shouldSkip($propertyName)
137 {
138 return isset($this->propertiesToSkip[$propertyName]);
139 }
140
141 /**
142 * Allow all properties in property mapping, even unknown ones.
143 *
144 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
145 * @api
146 */
147 public function allowAllProperties()
148 {
149 $this->mapUnknownProperties = true;
150 return $this;
151 }
152
153 /**
154 * Allow a list of specific properties. All arguments of
155 * allowProperties are used here (varargs).
156 *
157 * Example: allowProperties('title', 'content', 'author')
158 *
159 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
160 * @api
161 */
162 public function allowProperties()
163 {
164 foreach (func_get_args() as $propertyName) {
165 $this->propertiesToBeMapped[$propertyName] = $propertyName;
166 }
167 return $this;
168 }
169
170 /**
171 * Skip a list of specific properties. All arguments of
172 * skipProperties are used here (varargs).
173 *
174 * Example: skipProperties('unused', 'dummy')
175 *
176 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
177 * @api
178 */
179 public function skipProperties()
180 {
181 foreach (func_get_args() as $propertyName) {
182 $this->propertiesToSkip[$propertyName] = $propertyName;
183 }
184 return $this;
185 }
186
187 /**
188 * Allow all properties during property mapping, but reject a few
189 * selected ones (blacklist).
190 *
191 * Example: allowAllPropertiesExcept('password', 'userGroup')
192 *
193 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
194 * @api
195 */
196 public function allowAllPropertiesExcept()
197 {
198 $this->mapUnknownProperties = true;
199
200 foreach (func_get_args() as $propertyName) {
201 $this->propertiesNotToBeMapped[$propertyName] = $propertyName;
202 }
203 return $this;
204 }
205
206 /**
207 * When this is enabled, properties that are disallowed will be skipped
208 * instead of triggering an error during mapping.
209 *
210 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
211 * @api
212 */
213 public function skipUnknownProperties()
214 {
215 $this->skipUnknownProperties = true;
216 return $this;
217 }
218
219 /**
220 * Whether unknown (unconfigured) properties should be skipped during
221 * mapping, instead if causing an error.
222 *
223 * @return bool
224 * @api
225 */
226 public function shouldSkipUnknownProperties()
227 {
228 return $this->skipUnknownProperties;
229 }
230
231 /**
232 * Returns the sub-configuration for the passed $propertyName. Must ALWAYS return a valid configuration object!
233 *
234 * @param string $propertyName
235 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface the property mapping configuration for the given $propertyName.
236 * @api
237 */
238 public function getConfigurationFor($propertyName)
239 {
240 if (isset($this->subConfigurationForProperty[$propertyName])) {
241 return $this->subConfigurationForProperty[$propertyName];
242 }
243 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
244 return $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
245 }
246
247 return new self();
248 }
249
250 /**
251 * Maps the given $sourcePropertyName to a target property name.
252 *
253 * @param string $sourcePropertyName
254 * @return string property name of target
255 * @api
256 */
257 public function getTargetPropertyName($sourcePropertyName)
258 {
259 if (isset($this->mapping[$sourcePropertyName])) {
260 return $this->mapping[$sourcePropertyName];
261 }
262 return $sourcePropertyName;
263 }
264
265 /**
266 * @param string $typeConverterClassName
267 * @param string $key
268 * @return mixed configuration value for the specific $typeConverterClassName. Can be used by Type Converters to fetch converter-specific configuration.
269 * @api
270 */
271 public function getConfigurationValue($typeConverterClassName, $key)
272 {
273 if (!isset($this->configuration[$typeConverterClassName][$key])) {
274 return null;
275 }
276
277 return $this->configuration[$typeConverterClassName][$key];
278 }
279
280 /**
281 * Define renaming from Source to Target property.
282 *
283 * @param string $sourcePropertyName
284 * @param string $targetPropertyName
285 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
286 * @api
287 */
288 public function setMapping($sourcePropertyName, $targetPropertyName)
289 {
290 $this->mapping[$sourcePropertyName] = $targetPropertyName;
291 return $this;
292 }
293
294 /**
295 * Set all options for the given $typeConverter.
296 *
297 * @param string $typeConverter class name of type converter
298 * @param array $options
299 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
300 * @api
301 */
302 public function setTypeConverterOptions($typeConverter, array $options)
303 {
304 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
305 $this->configuration[$typeConverter] = $options;
306 }
307 return $this;
308 }
309
310 /**
311 * Set a single option (denoted by $optionKey) for the given $typeConverter.
312 *
313 * @param string $typeConverter class name of type converter
314 * @param string $optionKey
315 * @param mixed $optionValue
316 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
317 * @api
318 */
319 public function setTypeConverterOption($typeConverter, $optionKey, $optionValue)
320 {
321 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
322 $this->configuration[$typeConverter][$optionKey] = $optionValue;
323 }
324 return $this;
325 }
326
327 /**
328 * Get type converter classes including parents for the given type converter
329 *
330 * When setting an option on a subclassed type converter, this option must also be set on
331 * all its parent type converters.
332 *
333 * @param string $typeConverter The type converter class
334 * @return array Class names of type converters
335 */
336 protected function getTypeConvertersWithParentClasses($typeConverter)
337 {
338 $typeConverterClasses = class_parents($typeConverter);
339 $typeConverterClasses = $typeConverterClasses === false ? [] : $typeConverterClasses;
340 $typeConverterClasses[] = $typeConverter;
341 return $typeConverterClasses;
342 }
343
344 /**
345 * Returns the configuration for the specific property path, ready to be modified. Should be used
346 * inside a fluent interface like:
347 * $configuration->forProperty('foo.bar')->setTypeConverterOption(....)
348 *
349 * @param string $propertyPath
350 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
351 * @api
352 */
353 public function forProperty($propertyPath)
354 {
355 $splittedPropertyPath = explode('.', $propertyPath);
356 return $this->traverseProperties($splittedPropertyPath);
357 }
358
359 /**
360 * Traverse the property configuration. Only used by forProperty().
361 *
362 * @param array $splittedPropertyPath
363 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
364 */
365 public function traverseProperties(array $splittedPropertyPath)
366 {
367 if (empty($splittedPropertyPath)) {
368 return $this;
369 }
370
371 $currentProperty = array_shift($splittedPropertyPath);
372 if (!isset($this->subConfigurationForProperty[$currentProperty])) {
373 $type = static::class;
374 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
375 $this->subConfigurationForProperty[$currentProperty] = clone $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
376 } else {
377 $this->subConfigurationForProperty[$currentProperty] = new $type;
378 }
379 }
380 return $this->subConfigurationForProperty[$currentProperty]->traverseProperties($splittedPropertyPath);
381 }
382
383 /**
384 * Return the type converter set for this configuration.
385 *
386 * @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface
387 * @api
388 */
389 public function getTypeConverter()
390 {
391 return $this->typeConverter;
392 }
393
394 /**
395 * Set a type converter which should be used for this specific conversion.
396 *
397 * @param \TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter
398 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
399 * @api
400 */
401 public function setTypeConverter(\TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter)
402 {
403 $this->typeConverter = $typeConverter;
404 return $this;
405 }
406 }