Skip to content

Commit 68a7d3e

Browse files
committed
-refactoring
1 parent 30e6f10 commit 68a7d3e

File tree

2 files changed

+84
-19
lines changed

2 files changed

+84
-19
lines changed

features/main/validation.feature

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,19 @@ Feature: Using validations groups
114114
"@context": "/contexts/ConstraintViolationList",
115115
"@type": "ConstraintViolationList",
116116
"hydra:title": "An error occurred",
117-
"hydra:description": "baz: This value should be of type string.\nfoo: This value should be of type bool.\nbar: This value should be of type int.\nqux: This value should be of type string.\nuuid: This value should be of type uuid.\nrelatedDummy: This value should be of type array|string.\nrelatedDummies: This value should be of type array.",
117+
"hydra:description": "baz: This value should be of type string.\nqux: This value should be of type string.\nfoo: This value should be of type bool.\nbar: This value should be of type int.\nuuid: This value should be of type uuid.\nrelatedDummy: This value should be of type array|string.\nrelatedDummies: This value should be of type array.",
118118
"violations": [
119119
{
120120
"propertyPath": "baz",
121121
"message": "This value should be of type string.",
122122
"code": "ba785a8c-82cb-4283-967c-3cf342181b40",
123123
"hint": "Failed to create object because the class misses the \"baz\" property."
124124
},
125+
{
126+
"propertyPath": "qux",
127+
"message": "This value should be of type string.",
128+
"code": "ba785a8c-82cb-4283-967c-3cf342181b40"
129+
},
125130
{
126131
"propertyPath": "foo",
127132
"message": "This value should be of type bool.",
@@ -132,11 +137,6 @@ Feature: Using validations groups
132137
"message": "This value should be of type int.",
133138
"code": "ba785a8c-82cb-4283-967c-3cf342181b40"
134139
},
135-
{
136-
"propertyPath": "qux",
137-
"message": "This value should be of type string.",
138-
"code": "ba785a8c-82cb-4283-967c-3cf342181b40"
139-
},
140140
{
141141
"propertyPath": "uuid",
142142
"message": "This value should be of type uuid.",

src/Serializer/AbstractItemNormalizer.php

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -304,18 +304,27 @@ protected function instantiateObject(array &$data, string $class, array &$contex
304304

305305
return $object;
306306
}
307+
// clean up even if no match
308+
unset($context[static::OBJECT_TO_POPULATE]);
307309

308310
$class = $this->getClassDiscriminatorResolvedClass($data, $class, $context);
309311
$reflectionClass = new \ReflectionClass($class);
310312

311313
$constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes);
312314
if ($constructor) {
313-
$constructorParameters = $constructor->getParameters();
315+
$context['has_constructor'] = true;
316+
if (true !== $constructor->isPublic()) {
317+
return $reflectionClass->newInstanceWithoutConstructor();
318+
}
314319

315-
$params = [];
320+
$constructorParameters = $constructor->getParameters();
316321
$missingConstructorArguments = [];
322+
$params = [];
323+
$unsetKeys = [];
324+
317325
foreach ($constructorParameters as $constructorParameter) {
318326
$paramName = $constructorParameter->name;
327+
$attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context);
319328
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;
320329

321330
$allowed = false === $allowedAttributes || (\is_array($allowedAttributes) && \in_array($paramName, $allowedAttributes, true));
@@ -326,7 +335,22 @@ protected function instantiateObject(array &$data, string $class, array &$contex
326335
throw new RuntimeException(\sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name));
327336
}
328337

329-
$params[] = $data[$paramName];
338+
$variadicParameters = [];
339+
foreach ($data[$key] as $parameterKey => $parameterData) {
340+
try {
341+
$variadicParameters[$parameterKey] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $attributeContext, $format);
342+
} catch (NotNormalizableValueException $exception) {
343+
if (!isset($context['not_normalizable_value_exceptions'])) {
344+
throw $exception;
345+
}
346+
347+
$context['not_normalizable_value_exceptions'][] = $exception;
348+
$params[$paramName] = $parameterData;
349+
}
350+
}
351+
352+
$params = array_merge(array_values($params), $variadicParameters);
353+
$unsetKeys[] = $key;
330354
}
331355
} elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) {
332356
$constructorContext = $context;
@@ -341,17 +365,34 @@ protected function instantiateObject(array &$data, string $class, array &$contex
341365
}
342366

343367
// Don't run set for a parameter passed to the constructor
344-
unset($data[$key]);
345-
} elseif (isset($context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key])) {
346-
$params[] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
368+
$unsetKeys[] = $key;
369+
} elseif (\array_key_exists($key, $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
370+
$params[$paramName] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
371+
} elseif (\array_key_exists($key, $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
372+
$params[$paramName] = $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
347373
} elseif ($constructorParameter->isDefaultValueAvailable()) {
348-
$params[] = $constructorParameter->getDefaultValue();
374+
$params[$paramName] = $constructorParameter->getDefaultValue();
375+
} elseif (!($context[self::REQUIRE_ALL_PROPERTIES] ?? $this->defaultContext[self::REQUIRE_ALL_PROPERTIES] ?? false) && $constructorParameter->hasType() && $constructorParameter->getType()->allowsNull()) {
376+
$params[$paramName] = null;
349377
} else {
350378
if (!isset($context['not_normalizable_value_exceptions'])) {
351379
$missingConstructorArguments[] = $constructorParameter->name;
380+
continue;
352381
}
353382

354-
$exception = NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name), $data, ['unknown'], $context['deserialization_path'] ?? null, true);
383+
$constructorParameterType = 'unknown';
384+
$reflectionType = $constructorParameter->getType();
385+
if ($reflectionType instanceof \ReflectionNamedType) {
386+
$constructorParameterType = $reflectionType->getName();
387+
}
388+
389+
$exception = NotNormalizableValueException::createForUnexpectedDataType(
390+
sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name),
391+
null,
392+
[$constructorParameterType],
393+
$attributeContext['deserialization_path'] ?? null,
394+
true
395+
);
355396
$context['not_normalizable_value_exceptions'][] = $exception;
356397
}
357398
}
@@ -360,15 +401,39 @@ protected function instantiateObject(array &$data, string $class, array &$contex
360401
throw new MissingConstructorArgumentsException(\sprintf('Cannot create an instance of "%s" from serialized data because its constructor requires the following parameters to be present : "$%s".', $class, implode('", "$', $missingConstructorArguments)), 0, null, $missingConstructorArguments, $class);
361402
}
362403

363-
if (\count($context['not_normalizable_value_exceptions'] ?? []) > 0) {
364-
return $reflectionClass->newInstanceWithoutConstructor();
404+
if (!$constructor->isConstructor()) {
405+
$instance = $constructor->invokeArgs(null, $params);
406+
407+
// do not set a parameter that has been set in the constructor
408+
foreach ($unsetKeys as $key) {
409+
unset($data[$key]);
410+
}
411+
412+
return $instance;
365413
}
366414

367-
if ($constructor->isConstructor()) {
368-
return $reflectionClass->newInstanceArgs($params);
415+
try {
416+
$instance = $reflectionClass->newInstanceArgs($params);
417+
418+
// do not set a parameter that has been set in the constructor
419+
foreach ($unsetKeys as $key) {
420+
unset($data[$key]);
421+
}
422+
423+
return $instance;
424+
} catch (\TypeError $e) {
425+
if (!isset($context['not_normalizable_value_exceptions'])) {
426+
throw $e;
427+
}
428+
429+
return $reflectionClass->newInstanceWithoutConstructor();
369430
}
431+
}
432+
433+
unset($context['has_constructor']);
370434

371-
return $constructor->invokeArgs(null, $params);
435+
if (!$reflectionClass->isInstantiable()) {
436+
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Failed to create object because the class "%s" is not instantiable.', $class), $data, ['unknown'], $context['deserialization_path'] ?? null);
372437
}
373438

374439
return new $class();

0 commit comments

Comments
 (0)