‪TYPO3CMS  10.4
ObjectConverter.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
30 
35 {
40 
45 
49  protected ‪$sourceTypes = ['array'];
50 
54  protected ‪$targetType = 'object';
55 
59  protected ‪$priority = 10;
60 
65 
70 
75  {
76  $this->objectContainer = ‪$objectContainer;
77  }
78 
83  {
84  $this->reflectionService = ‪$reflectionService;
85  }
86 
95  public function ‪canConvertFrom($source, string ‪$targetType): bool
96  {
97  return !is_subclass_of(‪$targetType, AbstractDomainObject::class);
98  }
99 
107  public function ‪getSourceChildPropertiesToBeConverted($source): array
108  {
109  if (isset($source['__type'])) {
110  unset($source['__type']);
111  }
112  return $source;
113  }
114 
125  public function ‪getTypeOfChildProperty(string ‪$targetType, string $propertyName, PropertyMappingConfigurationInterface $configuration): string
126  {
127  $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue(\‪TYPO3\CMS\‪Extbase\Property\TypeConverter\ObjectConverter::class, self::CONFIGURATION_TARGET_TYPE);
128  if ($configuredTargetType !== null) {
129  return $configuredTargetType;
130  }
131 
132  $specificTargetType = $this->objectContainer->getImplementationClassName(‪$targetType);
133  $classSchema = $this->reflectionService->getClassSchema($specificTargetType);
134 
135  $methodName = 'set' . ucfirst($propertyName);
136  if ($classSchema->hasMethod($methodName)) {
137  $methodParameters = $classSchema->getMethod($methodName)->getParameters() ?? [];
138  $methodParameter = current($methodParameters);
139  if ($methodParameter->getType() === null) {
140  throw new InvalidTargetException('Setter for property "' . $propertyName . '" had no type hint or documentation in target object of type "' . $specificTargetType . '".', 1303379158);
141  }
142  return $methodParameter->getType();
143  }
144  try {
145  $parameterType = $classSchema->getMethod('__construct')->getParameter($propertyName)->getType();
146  } catch (NoSuchMethodException $e) {
147  $exceptionMessage = sprintf('Type of child property "%s" of class "%s" could not be '
148  . 'derived from constructor arguments as said class does not have a constructor '
149  . 'defined.', $propertyName, $specificTargetType);
150  throw new InvalidTargetException($exceptionMessage, 1582385098);
151  } catch (NoSuchMethodParameterException $e) {
152  $exceptionMessage = sprintf('Type of child property "%1$s" of class "%2$s" could not be '
153  . 'derived from constructor arguments as the constructor of said class does not '
154  . 'have a parameter with property name "%1$s".', $propertyName, $specificTargetType);
155  throw new InvalidTargetException($exceptionMessage, 1303379126);
156  }
157 
158  if ($parameterType === null) {
159  $exceptionMessage = sprintf('Type of child property "%1$s" of class "%2$s" could not be '
160  . 'derived from constructor argument "%1$s". This usually happens if the argument '
161  . 'misses a type hint.', $propertyName, $specificTargetType);
162  throw new InvalidTargetException($exceptionMessage, 1582385619);
163  }
164  return $parameterType;
165  }
166 
178  public function ‪convertFrom($source, string ‪$targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null): ?object
179  {
180  $object = $this->‪buildObject($convertedChildProperties, ‪$targetType);
181  foreach ($convertedChildProperties as $propertyName => $propertyValue) {
182  $result = ‪ObjectAccess::setProperty($object, $propertyName, $propertyValue);
183  if ($result === false) {
184  $exceptionMessage = sprintf(
185  'Property "%s" having a value of type "%s" could not be set in target object of type "%s". Make sure that the property is accessible properly, for example via an appropriate setter method.',
186  $propertyName,
187  (is_object($propertyValue) ? get_class($propertyValue) : gettype($propertyValue)),
189  );
190  throw new InvalidTargetException($exceptionMessage, 1304538165);
191  }
192  }
193 
194  return $object;
195  }
196 
209  public function ‪getTargetTypeForSource($source, string $originalTargetType, PropertyMappingConfigurationInterface $configuration = null): string
210  {
211  ‪$targetType = $originalTargetType;
212 
213  if (is_array($source) && array_key_exists('__type', $source)) {
214  ‪$targetType = $source['__type'];
215 
216  if ($configuration === null) {
217  // todo: this is impossible to achieve since this methods is always called via (convert -> doMapping -> getTargetTypeForSource) and convert and doMapping create configuration objects if missing.
218  throw new \InvalidArgumentException('A property mapping configuration must be given, not NULL.', 1326277369);
219  }
220  if ($configuration->getConfigurationValue(\‪TYPO3\CMS\‪Extbase\Property\TypeConverter\ObjectConverter::class, self::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED) !== true) {
221  throw new InvalidPropertyMappingConfigurationException('Override of target type not allowed. To enable this, you need to set the PropertyMappingConfiguration Value "CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED" to TRUE.', 1317050430);
222  }
223 
224  if (‪$targetType !== $originalTargetType && is_a(‪$targetType, $originalTargetType, true) === false) {
225  throw new InvalidDataTypeException('The given type "' . ‪$targetType . '" is not a subtype of "' . $originalTargetType . '".', 1317048056);
226  }
227  }
228 
229  return ‪$targetType;
230  }
231 
242  protected function ‪buildObject(array &$possibleConstructorArgumentValues, string $objectType): object
243  {
244  $specificObjectType = $this->objectContainer->getImplementationClassName($objectType);
245  $classSchema = $this->reflectionService->getClassSchema($specificObjectType);
246 
247  if ($classSchema->hasConstructor()) {
248  $constructor = $classSchema->getMethod('__construct');
249  $constructorArguments = [];
250  foreach ($constructor->getParameters() as $parameterName => $parameter) {
251  if (array_key_exists($parameterName, $possibleConstructorArgumentValues)) {
252  $constructorArguments[] = $possibleConstructorArgumentValues[$parameterName];
253  unset($possibleConstructorArgumentValues[$parameterName]);
254  } elseif ($parameter->isOptional()) {
255  $constructorArguments[] = $parameter->getDefaultValue();
256  } else {
257  throw new InvalidTargetException('Missing constructor argument "' . $parameterName . '" for object of type "' . $objectType . '".', 1268734872);
258  }
259  }
260  return call_user_func_array([$this->objectManager, 'get'], array_merge([$objectType], $constructorArguments));
261  }
262  return $this->objectManager->get($objectType);
263  }
264 }
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\CONFIGURATION_TARGET_TYPE
‪const CONFIGURATION_TARGET_TYPE
Definition: ObjectConverter.php:39
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\getConfigurationValue
‪mixed getConfigurationValue($typeConverterClassName, $key)
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\buildObject
‪object buildObject(array &$possibleConstructorArgumentValues, string $objectType)
Definition: ObjectConverter.php:237
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\$sourceTypes
‪array $sourceTypes
Definition: ObjectConverter.php:48
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\injectReflectionService
‪injectReflectionService(ReflectionService $reflectionService)
Definition: ObjectConverter.php:77
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\$objectContainer
‪TYPO3 CMS Extbase Object Container Container $objectContainer
Definition: ObjectConverter.php:60
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\$reflectionService
‪TYPO3 CMS Extbase Reflection ReflectionService $reflectionService
Definition: ObjectConverter.php:64
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\getConfigurationFor
‪TYPO3 CMS Extbase Property PropertyMappingConfigurationInterface getConfigurationFor($propertyName)
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:18
‪TYPO3\CMS\Extbase\Object\Container\Container
Definition: Container.php:39
‪TYPO3\CMS\Extbase\Reflection\ObjectAccess\setProperty
‪static bool setProperty(&$subject, string $propertyName, $propertyValue, bool $forceDirectAccess=false)
Definition: ObjectAccess.php:170
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\$targetType
‪string $targetType
Definition: ObjectConverter.php:52
‪TYPO3
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchMethodParameterException
Definition: NoSuchMethodParameterException.php:24
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\injectObjectContainer
‪injectObjectContainer(Container $objectContainer)
Definition: ObjectConverter.php:69
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface
Definition: PropertyMappingConfigurationInterface.php:22
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED
‪const CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED
Definition: ObjectConverter.php:44
‪TYPO3\CMS\Extbase\Reflection\ObjectAccess
Definition: ObjectAccess.php:38
‪TYPO3\CMS\Extbase\Reflection\ReflectionService
Definition: ReflectionService.php:31
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchMethodException
Definition: NoSuchMethodException.php:24
‪TYPO3\CMS\Extbase\Property\TypeConverter
Definition: AbstractFileCollectionConverter.php:18
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\getTargetTypeForSource
‪string getTargetTypeForSource($source, string $originalTargetType, PropertyMappingConfigurationInterface $configuration=null)
Definition: ObjectConverter.php:204
‪TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject
Definition: AbstractDomainObject.php:31
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\getTypeOfChildProperty
‪string getTypeOfChildProperty(string $targetType, string $propertyName, PropertyMappingConfigurationInterface $configuration)
Definition: ObjectConverter.php:120
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\canConvertFrom
‪bool canConvertFrom($source, string $targetType)
Definition: ObjectConverter.php:90
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\getSourceChildPropertiesToBeConverted
‪array getSourceChildPropertiesToBeConverted($source)
Definition: ObjectConverter.php:102
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter
Definition: ObjectConverter.php:35
‪TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
Definition: AbstractTypeConverter.php:34
‪TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
Definition: InvalidPropertyMappingConfigurationException.php:26
‪TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
Definition: InvalidTargetException.php:26
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\convertFrom
‪object null convertFrom($source, string $targetType, array $convertedChildProperties=[], PropertyMappingConfigurationInterface $configuration=null)
Definition: ObjectConverter.php:173
‪TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter\$priority
‪int $priority
Definition: ObjectConverter.php:56
‪TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException
Definition: InvalidDataTypeException.php:26