‪TYPO3CMS  9.5
PropertyMapper.php
Go to the documentation of this file.
1 <?php
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 
20 
26 {
30  protected ‪$objectManager;
31 
35  protected ‪$configurationBuilder;
36 
47  protected ‪$typeConverters = [];
48 
54  protected ‪$messages;
55 
60  public function ‪injectObjectManager(\‪TYPO3\CMS\‪Extbase\Object\ObjectManagerInterface ‪$objectManager)
61  {
62  $this->objectManager = ‪$objectManager;
63  }
64 
70  {
71  $this->configurationBuilder = ‪$configurationBuilder;
72  }
73 
81  public function ‪initializeObject()
82  {
83  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'] as $typeConverterClassName) {
84  $typeConverter = $this->objectManager->get($typeConverterClassName);
85  foreach ($typeConverter->getSupportedSourceTypes() as $supportedSourceType) {
86  if (isset($this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()])) {
87  throw new Exception\DuplicateTypeConverterException('There exist at least two converters which handle the conversion from "' . $supportedSourceType . '" to "' . $typeConverter->getSupportedTargetType() . '" with priority "' . $typeConverter->getPriority() . '": ' . get_class($this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()]) . ' and ' . get_class($typeConverter), 1297951378);
88  }
89  $this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()] = $typeConverter;
90  }
91  }
92  }
93 
103  public function ‪convert($source, $targetType, ‪PropertyMappingConfigurationInterface $configuration = null)
104  {
105  if ($configuration === null) {
106  $configuration = $this->configurationBuilder->build();
107  }
108  $currentPropertyPath = [];
109  $this->messages = new \TYPO3\CMS\Extbase\Error\Result();
110  try {
111  $result = $this->‪doMapping($source, $targetType, $configuration, $currentPropertyPath);
112  if ($result instanceof \‪TYPO3\CMS\‪Extbase\Error\Error) {
113  return null;
114  }
115 
116  return $result;
117  } catch (‪TargetNotFoundException $e) {
118  throw $e;
119  } catch (\‪Exception $e) {
120  throw new ‪Exception('Exception while property mapping at property path "' . implode('.', $currentPropertyPath) . '": ' . $e->getMessage(), 1297759968, $e);
121  }
122  }
123 
129  public function ‪getMessages()
130  {
131  return ‪$this->messages;
132  }
133 
145  protected function ‪doMapping($source, $targetType, ‪PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath)
146  {
147  if (is_object($source)) {
148  $targetType = $this->‪parseCompositeType($targetType);
149  if ($source instanceof $targetType) {
150  return $source;
151  }
152  }
153 
154  if ($source === null) {
155  $source = '';
156  }
157 
158  $typeConverter = $this->‪findTypeConverter($source, $targetType, $configuration);
159  $targetType = $typeConverter->getTargetTypeForSource($source, $targetType, $configuration);
160 
161  if (!is_object($typeConverter) || !$typeConverter instanceof \‪TYPO3\CMS\‪Extbase\Property\‪TypeConverterInterface) {
162  throw new Exception\TypeConverterException(
163  'Type converter for "' . $source . '" -> "' . $targetType . '" not found.',
164  1476045062
165  );
166  }
167 
168  $convertedChildProperties = [];
169  foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) {
170  $targetPropertyName = $configuration->‪getTargetPropertyName($sourcePropertyName);
171  if ($configuration->‪shouldSkip($targetPropertyName)) {
172  continue;
173  }
174 
175  if (!$configuration->shouldMap($targetPropertyName)) {
176  if ($configuration->‪shouldSkipUnknownProperties()) {
177  continue;
178  }
179  throw new Exception\InvalidPropertyMappingConfigurationException('It is not allowed to map property "' . $targetPropertyName . '". You need to use $propertyMappingConfiguration->allowProperties(\'' . $targetPropertyName . '\') to enable mapping of this property.', 1355155913);
180  }
181 
182  $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration);
183 
184  $subConfiguration = $configuration->getConfigurationFor($targetPropertyName);
185 
186  $currentPropertyPath[] = $targetPropertyName;
187  $targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath);
188  array_pop($currentPropertyPath);
189  if (!($targetPropertyValue instanceof \TYPO3\CMS\Extbase\Error\Error)) {
190  $convertedChildProperties[$targetPropertyName] = $targetPropertyValue;
191  }
192  }
193  $result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration);
194 
195  if ($result instanceof \TYPO3\CMS\Extbase\Error\Error) {
196  $this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result);
197  }
198 
199  return $result;
200  }
201 
212  protected function findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
213  {
214  if ($configuration->getTypeConverter() !== null) {
215  return $configuration->getTypeConverter();
216  }
217 
218  $sourceType = $this->determineSourceType($source);
219 
220  if (!is_string($targetType)) {
221  throw new Exception\InvalidTargetException('The target type was no string, but of type "' . gettype($targetType) . '"', 1297941727);
222  }
223 
224  $targetType = $this->parseCompositeType($targetType);
225  // This is needed to correctly convert old class names to new ones
226  // This compatibility layer will be removed with 7.0
227  $targetType = \TYPO3\CMS\Core\Core\ClassLoadingInformation::getClassNameForAlias($targetType);
228 
229  $targetType = TypeHandlingUtility::normalizeType($targetType);
230 
231  $converter = null;
232 
233  if (TypeHandlingUtility::isSimpleType($targetType)) {
234  if (isset($this->typeConverters[$sourceType][$targetType])) {
235  $converter = $this->findEligibleConverterWithHighestPriority($this->typeConverters[$sourceType][$targetType], $source, $targetType);
236  }
237  } else {
238  $converter = $this->findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetType);
239  }
240 
241  if ($converter === null) {
242  throw new Exception\TypeConverterException(
243  'No converter found which can be used to ‪convert from "' . $sourceType . '" to "' . $targetType . '".',
244  1476044883
245  );
246  }
247 
248  return $converter;
249  }
250 
260  protected function findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetClass)
261  {
262  if (!class_exists($targetClass) && !interface_exists($targetClass)) {
263  throw new Exception\InvalidTargetException('Could not find a suitable type converter for "' . $targetClass . '" because no such class or interface exists.', 1297948764);
264  }
265 
266  if (!isset($this->typeConverters[$sourceType])) {
267  return null;
268  }
269 
270  $convertersForSource = $this->typeConverters[$sourceType];
271  if (isset($convertersForSource[$targetClass])) {
272  $converter = $this->‪findEligibleConverterWithHighestPriority($convertersForSource[$targetClass], $source, $targetClass);
273  if ($converter !== null) {
274  return $converter;
275  }
276  }
277 
278  foreach (class_parents($targetClass) as $parentClass) {
279  if (!isset($convertersForSource[$parentClass])) {
280  continue;
281  }
282 
283  $converter = $this->‪findEligibleConverterWithHighestPriority($convertersForSource[$parentClass], $source, $targetClass);
284  if ($converter !== null) {
285  return $converter;
286  }
287  }
288 
289  $converters = $this->‪getConvertersForInterfaces($convertersForSource, class_implements($targetClass));
290  $converter = $this->‪findEligibleConverterWithHighestPriority($converters, $source, $targetClass);
291 
292  if ($converter !== null) {
293  return $converter;
294  }
295  if (isset($convertersForSource['object'])) {
296  return $this->‪findEligibleConverterWithHighestPriority($convertersForSource['object'], $source, $targetClass);
297  }
298  return null;
299  }
300 
307  protected function ‪findEligibleConverterWithHighestPriority($converters, $source, $targetType)
308  {
309  if (!is_array($converters)) {
310  return null;
311  }
312  krsort($converters, SORT_NUMERIC);
313  reset($converters);
315  foreach ($converters as $converter) {
316  if ($converter->canConvertFrom($source, $targetType)) {
317  return $converter;
318  }
319  }
320  return null;
321  }
322 
329  protected function ‪getConvertersForInterfaces(array $convertersForSource, array $interfaceNames)
330  {
331  $convertersForInterface = [];
332  foreach ($interfaceNames as $implementedInterface) {
333  if (isset($convertersForSource[$implementedInterface])) {
334  foreach ($convertersForSource[$implementedInterface] as $priority => $converter) {
335  if (isset($convertersForInterface[$priority])) {
336  throw new Exception\DuplicateTypeConverterException('There exist at least two converters which handle the conversion to an interface with priority "' . $priority . '". ' . get_class($convertersForInterface[$priority]) . ' and ' . get_class($converter), 1297951338);
337  }
338  $convertersForInterface[$priority] = $converter;
339  }
340  }
341  }
342  return $convertersForInterface;
343  }
344 
352  protected function ‪determineSourceType($source)
353  {
354  if (is_string($source)) {
355  return 'string';
356  }
357  if (is_array($source)) {
358  return 'array';
359  }
360  if (is_float($source)) {
361  return 'float';
362  }
363  if (is_int($source)) {
364  return 'integer';
365  }
366  if (is_bool($source)) {
367  return 'boolean';
368  }
369  throw new Exception\InvalidSourceException('The source is not of type string, array, float, integer or boolean, but of type "' . gettype($source) . '"', 1297773150);
370  }
371 
380  public function ‪parseCompositeType($compositeType)
381  {
382  if (strpos($compositeType, '<') !== false) {
383  $compositeType = substr($compositeType, 0, strpos($compositeType, '<'));
384  }
385  return $compositeType;
386  }
387 }
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:4
‪TYPO3\CMS\Extbase\Property\TypeConverterInterface
Definition: TypeConverterInterface.php:23
‪TYPO3
‪TYPO3\CMS\Extbase\Property\PropertyMapper\$typeConverters
‪array $typeConverters
Definition: PropertyMapper.php:44
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\getTargetPropertyName
‪string getTargetPropertyName($sourcePropertyName)
‪TYPO3\CMS\Extbase\Property\PropertyMapper\convert
‪mixed convert($source, $targetType, PropertyMappingConfigurationInterface $configuration=null)
Definition: PropertyMapper.php:99
‪TYPO3\CMS\Extbase\Property
‪TYPO3\CMS\Extbase\Property\PropertyMapper\$configurationBuilder
‪TYPO3 CMS Extbase Property PropertyMappingConfigurationBuilder $configurationBuilder
Definition: PropertyMapper.php:33
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface
Definition: PropertyMappingConfigurationInterface.php:21
‪TYPO3\CMS\Extbase\Property\PropertyMapper\injectObjectManager
‪injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
Definition: PropertyMapper.php:56
‪TYPO3\CMS\Extbase\Property\PropertyMapper\$objectManager
‪TYPO3 CMS Extbase Object ObjectManagerInterface $objectManager
Definition: PropertyMapper.php:29
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\shouldSkip
‪bool shouldSkip($propertyName)
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\shouldSkipUnknownProperties
‪bool shouldSkipUnknownProperties()
‪TYPO3\CMS\Extbase\Utility\TypeHandlingUtility
Definition: TypeHandlingUtility.php:19
‪TYPO3\CMS\Extbase\Property\PropertyMapper\parseCompositeType
‪string parseCompositeType($compositeType)
Definition: PropertyMapper.php:376
‪TYPO3\CMS\Extbase\Property\PropertyMapper\injectConfigurationBuilder
‪injectConfigurationBuilder(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder $configurationBuilder)
Definition: PropertyMapper.php:65
‪TYPO3\CMS\Extbase\Property\PropertyMapper\findTypeConverter
‪TYPO3 CMS Extbase Property TypeConverterInterface findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
Definition: PropertyMapper.php:208
‪TYPO3\CMS\Extbase\Property\PropertyMapper\getConvertersForInterfaces
‪array getConvertersForInterfaces(array $convertersForSource, array $interfaceNames)
Definition: PropertyMapper.php:325
‪TYPO3\CMS\Extbase\Property\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Extbase\Property\PropertyMapper
Definition: PropertyMapper.php:26
‪TYPO3\CMS\Extbase\Property\PropertyMapper\$messages
‪TYPO3 CMS Extbase Error Result $messages
Definition: PropertyMapper.php:50
‪TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
Definition: AbstractTypeConverter.php:26
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder
Definition: PropertyMappingConfigurationBuilder.php:22
‪TYPO3\CMS\Extbase\Property\PropertyMapper\determineSourceType
‪string determineSourceType($source)
Definition: PropertyMapper.php:348
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Extbase\Property\PropertyMapper\findEligibleConverterWithHighestPriority
‪mixed findEligibleConverterWithHighestPriority($converters, $source, $targetType)
Definition: PropertyMapper.php:303
‪TYPO3\CMS\Extbase\Property\PropertyMapper\getMessages
‪TYPO3 CMS Extbase Error Result getMessages()
Definition: PropertyMapper.php:125
‪TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException
Definition: TargetNotFoundException.php:21
‪TYPO3\CMS\Extbase\Property\PropertyMapper\doMapping
‪mixed doMapping($source, $targetType, PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath)
Definition: PropertyMapper.php:141
‪TYPO3\CMS\Extbase\Property\PropertyMapper\initializeObject
‪initializeObject()
Definition: PropertyMapper.php:77