PropertyMappingConfiguration   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 359
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 35
eloc 72
dl 0
loc 359
rs 9.6
c 0
b 0
f 0

19 Methods

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

357
        return $this->subConfigurationForProperty[$currentProperty]->/** @scrutinizer ignore-call */ traverseProperties($splittedPropertyPath);
Loading history...
358
    }
359
360
    /**
361
     * Return the type converter set for this configuration.
362
     *
363
     * @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface|null
364
     */
365
    public function getTypeConverter()
366
    {
367
        return $this->typeConverter;
368
    }
369
370
    /**
371
     * Set a type converter which should be used for this specific conversion.
372
     *
373
     * @param \TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter
374
     * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
375
     */
376
    public function setTypeConverter(TypeConverterInterface $typeConverter)
377
    {
378
        $this->typeConverter = $typeConverter;
379
        return $this;
380
    }
381
}
382