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