TYPO3 CMS  TYPO3_7-6
PropertyMapper.php
Go to the documentation of this file.
1 <?php
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('Type converter for "' . $source . '" -> "' . $targetType . '" not found.');
172  }
173 
174  $convertedChildProperties = [];
175  foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) {
176  $targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName);
177  if ($configuration->shouldSkip($targetPropertyName)) {
178  continue;
179  }
180 
181  if (!$configuration->shouldMap($targetPropertyName)) {
182  if ($configuration->shouldSkipUnknownProperties()) {
183  continue;
184  }
185  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);
186  }
187 
188  $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration);
189 
190  $subConfiguration = $configuration->getConfigurationFor($targetPropertyName);
191 
192  $currentPropertyPath[] = $targetPropertyName;
193  $targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath);
194  array_pop($currentPropertyPath);
195  if (!($targetPropertyValue instanceof \TYPO3\CMS\Extbase\Error\Error)) {
196  $convertedChildProperties[$targetPropertyName] = $targetPropertyValue;
197  }
198  }
199  $result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration);
200 
201  if ($result instanceof \TYPO3\CMS\Extbase\Error\Error) {
202  $this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result);
203  }
204 
205  return $result;
206  }
207 
218  protected function findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
219  {
220  if ($configuration->getTypeConverter() !== null) {
221  return $configuration->getTypeConverter();
222  }
223 
224  $sourceType = $this->determineSourceType($source);
225 
226  if (!is_string($targetType)) {
227  throw new Exception\InvalidTargetException('The target type was no string, but of type "' . gettype($targetType) . '"', 1297941727);
228  }
229 
230  $targetType = $this->parseCompositeType($targetType);
231  // This is needed to correctly convert old class names to new ones
232  // This compatibility layer will be removed with 7.0
233  $targetType = \TYPO3\CMS\Core\Core\ClassLoadingInformation::getClassNameForAlias($targetType);
234 
235  $targetType = TypeHandlingUtility::normalizeType($targetType);
236 
237  $converter = null;
238 
239  if (TypeHandlingUtility::isSimpleType($targetType)) {
240  if (isset($this->typeConverters[$sourceType][$targetType])) {
241  $converter = $this->findEligibleConverterWithHighestPriority($this->typeConverters[$sourceType][$targetType], $source, $targetType);
242  }
243  } else {
244  $converter = $this->findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetType);
245  }
246 
247  if ($converter === null) {
248  throw new Exception\TypeConverterException('No converter found which can be used to convert from "' . $sourceType . '" to "' . $targetType . '".');
249  }
250 
251  return $converter;
252  }
253 
263  protected function findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $targetClass)
264  {
265  if (!class_exists($targetClass) && !interface_exists($targetClass)) {
266  throw new Exception\InvalidTargetException('Could not find a suitable type converter for "' . $targetClass . '" because no such class or interface exists.', 1297948764);
267  }
268 
269  if (!isset($this->typeConverters[$sourceType])) {
270  return null;
271  }
272 
273  $convertersForSource = $this->typeConverters[$sourceType];
274  if (isset($convertersForSource[$targetClass])) {
275  $converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$targetClass], $source, $targetClass);
276  if ($converter !== null) {
277  return $converter;
278  }
279  }
280 
281  foreach (class_parents($targetClass) as $parentClass) {
282  if (!isset($convertersForSource[$parentClass])) {
283  continue;
284  }
285 
286  $converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$parentClass], $source, $targetClass);
287  if ($converter !== null) {
288  return $converter;
289  }
290  }
291 
292  $converters = $this->getConvertersForInterfaces($convertersForSource, class_implements($targetClass));
293  $converter = $this->findEligibleConverterWithHighestPriority($converters, $source, $targetClass);
294 
295  if ($converter !== null) {
296  return $converter;
297  }
298  if (isset($convertersForSource['object'])) {
299  return $this->findEligibleConverterWithHighestPriority($convertersForSource['object'], $source, $targetClass);
300  } else {
301  return null;
302  }
303  }
304 
311  protected function findEligibleConverterWithHighestPriority($converters, $source, $targetType)
312  {
313  if (!is_array($converters)) {
314  return null;
315  }
316  krsort($converters);
317  reset($converters);
319  foreach ($converters as $converter) {
320  if ($converter->canConvertFrom($source, $targetType)) {
321  return $converter;
322  }
323  }
324  return null;
325  }
326 
333  protected function getConvertersForInterfaces(array $convertersForSource, array $interfaceNames)
334  {
335  $convertersForInterface = [];
336  foreach ($interfaceNames as $implementedInterface) {
337  if (isset($convertersForSource[$implementedInterface])) {
338  foreach ($convertersForSource[$implementedInterface] as $priority => $converter) {
339  if (isset($convertersForInterface[$priority])) {
340  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);
341  }
342  $convertersForInterface[$priority] = $converter;
343  }
344  }
345  }
346  return $convertersForInterface;
347  }
348 
356  protected function determineSourceType($source)
357  {
358  if (is_string($source)) {
359  return 'string';
360  } elseif (is_array($source)) {
361  return 'array';
362  } elseif (is_float($source)) {
363  return 'float';
364  } elseif (is_int($source)) {
365  return 'integer';
366  } elseif (is_bool($source)) {
367  return 'boolean';
368  } else {
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  }
372 
380  public function parseCompositeType($compositeType)
381  {
382  if (strpos($compositeType, '<') !== false) {
383  $compositeType = substr($compositeType, 0, strpos($compositeType, '<'));
384  }
385  return $compositeType;
386  }
387 }
findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
injectConfigurationBuilder(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder $configurationBuilder)
doMapping($source, $targetType, PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath)
convert($source, $targetType, PropertyMappingConfigurationInterface $configuration=null)
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
getConvertersForInterfaces(array $convertersForSource, array $interfaceNames)