TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
PropertyMapper.php
Go to the documentation of this file.
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  * */
26 
35 {
39  protected $objectManager;
40 
45 
56  protected $typeConverters = [];
57 
63  protected $messages;
64 
68  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
69  {
70  $this->objectManager = $objectManager;
71  }
72 
77  {
78  $this->configurationBuilder = $configurationBuilder;
79  }
80 
88  public function initializeObject()
89  {
90  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'] as $typeConverterClassName) {
91  $typeConverter = $this->objectManager->get($typeConverterClassName);
92  foreach ($typeConverter->getSupportedSourceTypes() as $supportedSourceType) {
93  if (isset($this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()])) {
94  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);
95  }
96  $this->typeConverters[$supportedSourceType][$typeConverter->getSupportedTargetType()][$typeConverter->getPriority()] = $typeConverter;
97  }
98  }
99  }
100 
111  public function convert($source, $targetType, PropertyMappingConfigurationInterface $configuration = null)
112  {
113  if ($configuration === null) {
114  $configuration = $this->configurationBuilder->build();
115  }
116  $currentPropertyPath = [];
117  $this->messages = new \TYPO3\CMS\Extbase\Error\Result();
118  try {
119  $result = $this->doMapping($source, $targetType, $configuration, $currentPropertyPath);
120  if ($result instanceof \TYPO3\CMS\Extbase\Error\Error) {
121  return null;
122  }
123 
124  return $result;
125  } catch (TargetNotFoundException $e) {
126  throw $e;
127  } catch (\Exception $e) {
128  throw new Exception('Exception while property mapping at property path "' . implode('.', $currentPropertyPath) . '": ' . $e->getMessage(), 1297759968, $e);
129  }
130  }
131 
138  public function getMessages()
139  {
140  return $this->messages;
141  }
142 
154  protected function doMapping($source, $targetType, PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath)
155  {
156  if (is_object($source)) {
157  $targetType = $this->parseCompositeType($targetType);
158  if ($source instanceof $targetType) {
159  return $source;
160  }
161  }
162 
163  if ($source === null) {
164  $source = '';
165  }
166 
167  $typeConverter = $this->findTypeConverter($source, $targetType, $configuration);
168  $targetType = $typeConverter->getTargetTypeForSource($source, $targetType, $configuration);
169 
170  if (!is_object($typeConverter) || !$typeConverter instanceof \TYPO3\CMS\Extbase\Property\TypeConverterInterface) {
171  throw new Exception\TypeConverterException(
172  'Type converter for "' . $source . '" -> "' . $targetType . '" not found.',
173  1476045062
174  );
175  }
176 
177  $convertedChildProperties = [];
178  foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) {
179  $targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName);
180  if ($configuration->shouldSkip($targetPropertyName)) {
181  continue;
182  }
183 
184  if (!$configuration->shouldMap($targetPropertyName)) {
185  if ($configuration->shouldSkipUnknownProperties()) {
186  continue;
187  }
188  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);
189  }
190 
191  $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration);
192 
193  $subConfiguration = $configuration->getConfigurationFor($targetPropertyName);
194 
195  $currentPropertyPath[] = $targetPropertyName;
196  $targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath);
197  array_pop($currentPropertyPath);
198  if (!($targetPropertyValue instanceof \TYPO3\CMS\Extbase\Error\Error)) {
199  $convertedChildProperties[$targetPropertyName] = $targetPropertyValue;
200  }
201  }
202  $result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration);
203 
204  if ($result instanceof \TYPO3\CMS\Extbase\Error\Error) {
205  $this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result);
206  }
207 
208  return $result;
209  }
210 
221  protected function findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
222  {
223  if ($configuration->getTypeConverter() !== null) {
224  return $configuration->getTypeConverter();
225  }
226 
227  $sourceType = $this->determineSourceType($source);
228 
229  if (!is_string($targetType)) {
230  throw new Exception\InvalidTargetException('The target type was no string, but of type "' . gettype($targetType) . '"', 1297941727);
231  }
232 
233  $targetType = $this->parseCompositeType($targetType);
234  // This is needed to correctly convert old class names to new ones
235  // This compatibility layer will be removed with 7.0
236  $targetType = \TYPO3\CMS\Core\Core\ClassLoadingInformation::getClassNameForAlias($targetType);
237 
238  $targetType = TypeHandlingUtility::normalizeType($targetType);
239 
240  $converter = null;
241 
242  if (TypeHandlingUtility::isSimpleType($targetType)) {
243  if (isset($this->typeConverters[$sourceType][$targetType])) {
244  $converter = $this->findEligibleConverterWithHighestPriority($this->typeConverters[$sourceType][$targetType], $source, $targetType);
245  }
246  } else {
247  $converter = $this->findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetType);
248  }
249 
250  if ($converter === null) {
251  throw new Exception\TypeConverterException(
252  'No converter found which can be used to convert from "' . $sourceType . '" to "' . $targetType . '".',
253  1476044883
254  );
255  }
256 
257  return $converter;
258  }
259 
269  protected function findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetClass)
270  {
271  if (!class_exists($targetClass) && !interface_exists($targetClass)) {
272  throw new Exception\InvalidTargetException('Could not find a suitable type converter for "' . $targetClass . '" because no such class or interface exists.', 1297948764);
273  }
274 
275  if (!isset($this->typeConverters[$sourceType])) {
276  return null;
277  }
278 
279  $convertersForSource = $this->typeConverters[$sourceType];
280  if (isset($convertersForSource[$targetClass])) {
281  $converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$targetClass], $source, $targetClass);
282  if ($converter !== null) {
283  return $converter;
284  }
285  }
286 
287  foreach (class_parents($targetClass) as $parentClass) {
288  if (!isset($convertersForSource[$parentClass])) {
289  continue;
290  }
291 
292  $converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$parentClass], $source, $targetClass);
293  if ($converter !== null) {
294  return $converter;
295  }
296  }
297 
298  $converters = $this->getConvertersForInterfaces($convertersForSource, class_implements($targetClass));
299  $converter = $this->findEligibleConverterWithHighestPriority($converters, $source, $targetClass);
300 
301  if ($converter !== null) {
302  return $converter;
303  }
304  if (isset($convertersForSource['object'])) {
305  return $this->findEligibleConverterWithHighestPriority($convertersForSource['object'], $source, $targetClass);
306  } else {
307  return null;
308  }
309  }
310 
317  protected function findEligibleConverterWithHighestPriority($converters, $source, $targetType)
318  {
319  if (!is_array($converters)) {
320  return null;
321  }
322  krsort($converters, SORT_NUMERIC);
323  reset($converters);
325  foreach ($converters as $converter) {
326  if ($converter->canConvertFrom($source, $targetType)) {
327  return $converter;
328  }
329  }
330  return null;
331  }
332 
339  protected function getConvertersForInterfaces(array $convertersForSource, array $interfaceNames)
340  {
341  $convertersForInterface = [];
342  foreach ($interfaceNames as $implementedInterface) {
343  if (isset($convertersForSource[$implementedInterface])) {
344  foreach ($convertersForSource[$implementedInterface] as $priority => $converter) {
345  if (isset($convertersForInterface[$priority])) {
346  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);
347  }
348  $convertersForInterface[$priority] = $converter;
349  }
350  }
351  }
352  return $convertersForInterface;
353  }
354 
362  protected function determineSourceType($source)
363  {
364  if (is_string($source)) {
365  return 'string';
366  } elseif (is_array($source)) {
367  return 'array';
368  } elseif (is_float($source)) {
369  return 'float';
370  } elseif (is_integer($source)) {
371  return 'integer';
372  } elseif (is_bool($source)) {
373  return 'boolean';
374  } else {
375  throw new Exception\InvalidSourceException('The source is not of type string, array, float, integer or boolean, but of type "' . gettype($source) . '"', 1297773150);
376  }
377  }
378 
386  public function parseCompositeType($compositeType)
387  {
388  if (strpos($compositeType, '<') !== false) {
389  $compositeType = substr($compositeType, 0, strpos($compositeType, '<'));
390  }
391  return $compositeType;
392  }
393 }
findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
getConvertersForInterfaces(array $convertersForSource, array $interfaceNames)
convert($source, $targetType, PropertyMappingConfigurationInterface $configuration=null)
injectConfigurationBuilder(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder $configurationBuilder)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
doMapping($source, $targetType, PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath)