8000 Recursive denormalize with property info by mihai-stancu · Pull Request #17193 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

Recursive denormalize with property info #17193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Prev Previous commit
Next Next commit
Recursive denormalize using PropertyInfo
- Refactoring:
  - Extract method for property denormalization
  - Guard clauses
  - Avoid else/elseif
  - 2 indents per function
  - CS fix
  • Loading branch information
Mihai Stancu committed Dec 30, 2015
commit bbc0adf91dc7805ddaf19615dc5dbb3b746b677f
135 changes: 83 additions & 52 deletions src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -266,62 +266,93 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
}

$constructor = $reflectionClass->getConstructor();
if ($constructor) {
$constructorParameters = $constructor->getParameters();

$params = array();
foreach ($constructorParameters as $constructorParameter) {
$paramName = $constructorParameter->name;
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName) : $paramName;

$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
$ignored = in_array($paramName, $this->ignoredAttributes);
if (method_exists($constructorParameter, 'isVariadic') && $constructorParameter->isVariadic()) {
if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) {
if (!is_array($data[$paramName])) {
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));
}

$params = array_merge($params, $data[$paramName]);
}
} elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) {
if ($this->propertyInfoExtractor) {
$types = $this->propertyInfoExtractor->getTypes($class, $key);

foreach ($types as $type) {
if ($type && $type->getClassName() && (!empty($data[$key]) || !$type->isNullable())) {
if (!$this->serializer instanceof DenormalizerInterface) {
throw new RuntimeException(sprintf('Cannot denormalize attribute "%s" because injected serializer is not a denormalizer', $key));
}

$value = $data[$paramName];
$data[$paramName] = $this->serializer->denormalize($value, $type->getClassName(), $format, $context);

break;
}
}
}

$params[] = $data[$key];
// don't run set for a parameter passed to the constructor
unset($data[$key]);
} elseif ($constructorParameter->isDefaultValueAvailable()) {
$params[] = $constructorParameter->getDefaultValue();
} else {
throw new RuntimeException(
sprintf(
'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.',
$class,
$constructorParameter->name
)
);
}
if (!$constructor) {
return new $class();
}

$constructorParameters = $constructor->getParameters();

$params = array();
foreach ($constructorParameters as $constructorParameter) {
$paramName = $constructorParameter->name;
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName) : $paramName;

$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
$ignored = in_array($paramName, $this->ignoredAttributes);

if (!$allowed || $ignored) {
continue;
}

$missing = !isset($data[$key]) && !array_key_exists($key, $data);
$variadic = method_exists($constructorParameter, 'isVariadic') && $constructorParameter->isVariadic();

if ($variadic && !$missing && !is_array($data[$paramName])) {
$message = 'Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.';
throw new RuntimeException(sprintf($message, $class, $constructorParameter->name));
}

if ($variadic && !$missing) {
$params = array_merge($params, $data[$paramName]);

continue;
}

if ($missing && !$variadic && !$constructorParameter->isDefaultValueAvailable()) {
$message = 'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.';
throw new RuntimeException(sprintf($message, $class, $constructorParameter->name));
}

if ($missing && $constructorParameter->isDefaultValueAvailable()) {
$params[] = $constructorParameter->getDefaultValue();

continue;
}

if (!$missing) {
$params[] = $this->denormalizeProperty($data[$key], $class, $key, $format, $context);

unset($data[$key]);
}
}

return $reflectionClass->newInstanceArgs($params);
}

/**
* @param mixed $data
* @param string $class
* @param string $name
* @param string $format
* @param array $context
*
* @return mixed|object
*/
protected function denormalizeProperty($data, $class, $name, $format = null, array $context = array())
{
if (!$this->propertyInfoExtractor) {
return $data;
}

$types = $this->propertyInfoExtractor->getTypes($class, $name);

if (empty($types)) {
return $data;
}

foreach ($types as $type) {
if (empty($data) && !$type->isNullable()) {
return $data;
}

if (!$this->serializer instanceof DenormalizerInterface) {
$message = 'Cannot denormalize attribute "%s" because injected serializer is not a denormalizer';
throw new RuntimeException(sprintf($message, $name));
}

return $reflectionClass->newInstanceArgs($params);
return $this->serializer->denormalize($data, $type->getClassName(), $format, $context);
}

return new $class();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,18 @@ public function denormalize($data, $class, $format = null, array $context = arra
}
}

$object->$setter($value);
}
if (!$allowed || $ignored) {
continue;
}

$setter = 'set'.ucfirst($attribute);
if (!in_array($setter, $classMethods)) {
continue;
}

$value = $this->denormalizeProperty($value, $class, $attribute, $format, $context);

$object->$setter($value);
}

return $object;
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Component/Serializer/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"doctrine/cache": "For using the default cached annotation reader and metadata cache.",
"symfony/yaml": "For using the default YAML mapping loader.",
"symfony/config": "For using the XML mapping loader.",
"symfony/property-access": "For using the ObjectNormalizer."
"symfony/property-access": "For using the ObjectNormalizer.",
"symfony/property-info": "For using recursive denormalizations"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Serializer\\": "" },
Expand Down
0