[TASK] Update php-cs-fixer to 2.5.0
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Property / TypeConverter / ObjectConverter.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Property\TypeConverter;
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 * */
23 /**
24 * This converter transforms arrays to simple objects (POPO) by setting properties.
25 *
26 * @api
27 */
28 class ObjectConverter extends AbstractTypeConverter implements \TYPO3\CMS\Core\SingletonInterface
29 {
30 /**
31 * @var int
32 */
33 const CONFIGURATION_TARGET_TYPE = 3;
34
35 /**
36 * @var int
37 */
38 const CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED = 4;
39
40 /**
41 * @var array
42 */
43 protected $sourceTypes = ['array'];
44
45 /**
46 * @var string
47 */
48 protected $targetType = 'object';
49
50 /**
51 * @var int
52 */
53 protected $priority = 10;
54
55 /**
56 * @var \TYPO3\CMS\Extbase\Object\Container\Container
57 */
58 protected $objectContainer;
59
60 /**
61 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
62 */
63 protected $reflectionService;
64
65 /**
66 * @param \TYPO3\CMS\Extbase\Object\Container\Container $objectContainer
67 */
68 public function injectObjectContainer(\TYPO3\CMS\Extbase\Object\Container\Container $objectContainer)
69 {
70 $this->objectContainer = $objectContainer;
71 }
72
73 /**
74 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
75 */
76 public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
77 {
78 $this->reflectionService = $reflectionService;
79 }
80
81 /**
82 * Only convert non-persistent types
83 *
84 * @param mixed $source
85 * @param string $targetType
86 * @return bool
87 */
88 public function canConvertFrom($source, $targetType)
89 {
90 return !is_subclass_of($targetType, \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject::class);
91 }
92
93 /**
94 * Convert all properties in the source array
95 *
96 * @param mixed $source
97 * @return array
98 */
99 public function getSourceChildPropertiesToBeConverted($source)
100 {
101 if (isset($source['__type'])) {
102 unset($source['__type']);
103 }
104 return $source;
105 }
106
107 /**
108 * The type of a property is determined by the reflection service.
109 *
110 * @param string $targetType
111 * @param string $propertyName
112 * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
113 * @return string
114 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
115 */
116 public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration)
117 {
118 $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter::class, self::CONFIGURATION_TARGET_TYPE);
119 if ($configuredTargetType !== null) {
120 return $configuredTargetType;
121 }
122
123 $specificTargetType = $this->objectContainer->getImplementationClassName($targetType);
124 if ($this->reflectionService->hasMethod($specificTargetType, \TYPO3\CMS\Extbase\Reflection\ObjectAccess::buildSetterMethodName($propertyName))) {
125 $methodParameters = $this->reflectionService->getMethodParameters($specificTargetType, \TYPO3\CMS\Extbase\Reflection\ObjectAccess::buildSetterMethodName($propertyName));
126 $methodParameter = current($methodParameters);
127 if (!isset($methodParameter['type'])) {
128 throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Setter for property "' . $propertyName . '" had no type hint or documentation in target object of type "' . $specificTargetType . '".', 1303379158);
129 }
130 return $methodParameter['type'];
131 }
132 $methodParameters = $this->reflectionService->getMethodParameters($specificTargetType, '__construct');
133 if (isset($methodParameters[$propertyName]) && isset($methodParameters[$propertyName]['type'])) {
134 return $methodParameters[$propertyName]['type'];
135 }
136 throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Property "' . $propertyName . '" had no setter or constructor argument in target object of type "' . $specificTargetType . '".', 1303379126);
137 }
138
139 /**
140 * Convert an object from $source to an object.
141 *
142 * @param mixed $source
143 * @param string $targetType
144 * @param array $convertedChildProperties
145 * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
146 * @return object the target type
147 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
148 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException
149 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
150 */
151 public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
152 {
153 $object = $this->buildObject($convertedChildProperties, $targetType);
154 foreach ($convertedChildProperties as $propertyName => $propertyValue) {
155 $result = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::setProperty($object, $propertyName, $propertyValue);
156 if ($result === false) {
157 $exceptionMessage = sprintf(
158 '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.',
159 $propertyName,
160 (is_object($propertyValue) ? get_class($propertyValue) : gettype($propertyValue)),
161 $targetType
162 );
163 throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException($exceptionMessage, 1304538165);
164 }
165 }
166
167 return $object;
168 }
169
170 /**
171 * Determines the target type based on the source's (optional) __type key.
172 *
173 * @param mixed $source
174 * @param string $originalTargetType
175 * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
176 * @return string
177 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException
178 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
179 * @throws \InvalidArgumentException
180 */
181 public function getTargetTypeForSource($source, $originalTargetType, \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
182 {
183 $targetType = $originalTargetType;
184
185 if (is_array($source) && array_key_exists('__type', $source)) {
186 $targetType = $source['__type'];
187
188 if ($configuration === null) {
189 throw new \InvalidArgumentException('A property mapping configuration must be given, not NULL.', 1326277369);
190 }
191 if ($configuration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\ObjectConverter::class, self::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED) !== true) {
192 throw new \TYPO3\CMS\Extbase\Property\Exception\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);
193 }
194
195 if ($targetType !== $originalTargetType && is_a($targetType, $originalTargetType, true) === false) {
196 throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException('The given type "' . $targetType . '" is not a subtype of "' . $originalTargetType . '".', 1317048056);
197 }
198 }
199
200 return $targetType;
201 }
202
203 /**
204 * Builds a new instance of $objectType with the given $possibleConstructorArgumentValues. If
205 * constructor argument values are missing from the given array the method
206 * looks for a default value in the constructor signature. Furthermore, the constructor arguments are removed from $possibleConstructorArgumentValues
207 *
208 * @param array &$possibleConstructorArgumentValues
209 * @param string $objectType
210 * @return object The created instance
211 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException if a required constructor argument is missing
212 */
213 protected function buildObject(array &$possibleConstructorArgumentValues, $objectType)
214 {
215 $specificObjectType = $this->objectContainer->getImplementationClassName($objectType);
216 if ($this->reflectionService->hasMethod($specificObjectType, '__construct')) {
217 $constructorSignature = $this->reflectionService->getMethodParameters($specificObjectType, '__construct');
218 $constructorArguments = [];
219 foreach ($constructorSignature as $constructorArgumentName => $constructorArgumentInformation) {
220 if (array_key_exists($constructorArgumentName, $possibleConstructorArgumentValues)) {
221 $constructorArguments[] = $possibleConstructorArgumentValues[$constructorArgumentName];
222 unset($possibleConstructorArgumentValues[$constructorArgumentName]);
223 } elseif ($constructorArgumentInformation['optional'] === true) {
224 $constructorArguments[] = $constructorArgumentInformation['defaultValue'];
225 } else {
226 throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Missing constructor argument "' . $constructorArgumentName . '" for object of type "' . $objectType . '".', 1268734872);
227 }
228 }
229 return call_user_func_array([$this->objectManager, 'get'], array_merge([$objectType], $constructorArguments));
230 }
231 return $this->objectManager->get($objectType);
232 }
233 }