name: AbstractObjectNormalizer class_comment: null dependencies: - name: PropertyAccessInvalidArgumentException type: class source: Symfony\Component\PropertyAccess\Exception\InvalidArgumentException - name: InvalidTypeException type: class source: Symfony\Component\PropertyAccess\Exception\InvalidTypeException - name: NoSuchPropertyException type: class source: Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException - name: UninitializedPropertyException type: class source: Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException - name: PropertyAccess type: class source: Symfony\Component\PropertyAccess\PropertyAccess - name: PropertyTypeExtractorInterface type: class source: Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface - name: LegacyType type: class source: Symfony\Component\PropertyInfo\Type - name: CsvEncoder type: class source: Symfony\Component\Serializer\Encoder\CsvEncoder - name: JsonEncoder type: class source: Symfony\Component\Serializer\Encoder\JsonEncoder - name: XmlEncoder type: class source: Symfony\Component\Serializer\Encoder\XmlEncoder - name: ExtraAttributesException type: class source: Symfony\Component\Serializer\Exception\ExtraAttributesException - name: InvalidArgumentException type: class source: Symfony\Component\Serializer\Exception\InvalidArgumentException - name: LogicException type: class source: Symfony\Component\Serializer\Exception\LogicException - name: MissingConstructorArgumentsException type: class source: Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException - name: NotNormalizableValueException type: class source: Symfony\Component\Serializer\Exception\NotNormalizableValueException - name: AttributeMetadataInterface type: class source: Symfony\Component\Serializer\Mapping\AttributeMetadataInterface - name: ClassDiscriminatorFromClassMetadata type: class source: Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata - name: ClassDiscriminatorResolverInterface type: class source: Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface - name: ClassMetadataInterface type: class source: Symfony\Component\Serializer\Mapping\ClassMetadataInterface - name: ClassMetadataFactoryInterface type: class source: Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface - name: NameConverterInterface type: class source: Symfony\Component\Serializer\NameConverter\NameConverterInterface - name: TypeInfoLogicException type: class source: Symfony\Component\TypeInfo\Exception\LogicException - name: Type type: class source: Symfony\Component\TypeInfo\Type - name: CollectionType type: class source: Symfony\Component\TypeInfo\Type\CollectionType - name: IntersectionType type: class source: Symfony\Component\TypeInfo\Type\IntersectionType - name: ObjectType type: class source: Symfony\Component\TypeInfo\Type\ObjectType - name: UnionType type: class source: Symfony\Component\TypeInfo\Type\UnionType - name: TypeIdentifier type: class source: Symfony\Component\TypeInfo\TypeIdentifier properties: [] methods: - name: getAttributes visibility: protected parameters: - name: object - name: format - name: context comment: "# * Base class for a normalizer dealing with objects.\n# *\n# * @author\ \ K\xE9vin Dunglas \n# */\n# abstract class AbstractObjectNormalizer\ \ extends AbstractNormalizer\n# {\n# /**\n# * Set to true to respect the max depth\ \ metadata on fields.\n# */\n# public const ENABLE_MAX_DEPTH = 'enable_max_depth';\n\ # \n# /**\n# * How to track the current depth in the context.\n# */\n# public\ \ const DEPTH_KEY_PATTERN = 'depth_%s::%s';\n# \n# /**\n# * While denormalizing,\ \ we can verify that type matches.\n# *\n# * You can disable this by setting this\ \ flag to true.\n# */\n# public const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement';\n\ # \n# /**\n# * Flag to control whether fields with the value `null` should be\ \ output\n# * when normalizing or omitted.\n# */\n# public const SKIP_NULL_VALUES\ \ = 'skip_null_values';\n# \n# /**\n# * Flag to control whether uninitialized\ \ PHP>=7.4 typed class properties\n# * should be excluded when normalizing.\n\ # */\n# public const SKIP_UNINITIALIZED_VALUES = 'skip_uninitialized_values';\n\ # \n# /**\n# * Callback to allow to set a value for an attribute when the max\ \ depth has\n# * been reached.\n# *\n# * If no callback is given, the attribute\ \ is skipped. If a callable is\n# * given, its return value is used (even if null).\n\ # *\n# * The arguments are:\n# *\n# * - mixed $attributeValue value of this field\n\ # * - object $object the whole object being normalized\n# * - string $attributeName\ \ name of the attribute being normalized\n# * - string $format the requested\ \ format\n# * - array $context the serialization context\n# */\n# public\ \ const MAX_DEPTH_HANDLER = 'max_depth_handler';\n# \n# /**\n# * Specify which\ \ context key are not relevant to determine which attributes\n# * of an object\ \ to (de)normalize.\n# */\n# public const EXCLUDE_FROM_CACHE_KEY = 'exclude_from_cache_key';\n\ # \n# /**\n# * Flag to tell the denormalizer to also populate existing objects\ \ on\n# * attributes of the main object.\n# *\n# * Setting this to true is only\ \ useful if you also specify the root object\n# * in OBJECT_TO_POPULATE.\n# */\n\ # public const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate';\n# \n# /**\n\ # * Flag to control whether an empty object should be kept as an object (in\n\ # * JSON: {}) or converted to a list (in JSON: []).\n# */\n# public const PRESERVE_EMPTY_OBJECTS\ \ = 'preserve_empty_objects';\n# \n# protected ?ClassDiscriminatorResolverInterface\ \ $classDiscriminatorResolver;\n# \n# /**\n# * @var array|false>\n\ # */\n# private array $typeCache = [];\n# private array $attributesCache = [];\n\ # private readonly \\Closure $objectClassResolver;\n# \n# public function __construct(\n\ # ?ClassMetadataFactoryInterface $classMetadataFactory = null,\n# ?NameConverterInterface\ \ $nameConverter = null,\n# private ?PropertyTypeExtractorInterface $propertyTypeExtractor\ \ = null,\n# ?ClassDiscriminatorResolverInterface $classDiscriminatorResolver\ \ = null,\n# ?callable $objectClassResolver = null,\n# array $defaultContext =\ \ [],\n# ) {\n# parent::__construct($classMetadataFactory, $nameConverter, $defaultContext);\n\ # \n# if (isset($this->defaultContext[self::MAX_DEPTH_HANDLER]) && !\\is_callable($this->defaultContext[self::MAX_DEPTH_HANDLER]))\ \ {\n# throw new InvalidArgumentException(\\sprintf('The \"%s\" given in the default\ \ context is not callable.', self::MAX_DEPTH_HANDLER));\n# }\n# \n# $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY]\ \ = array_merge($this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] ?? [], [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS]);\n\ # \n# if ($classMetadataFactory) {\n# $classDiscriminatorResolver ??= new ClassDiscriminatorFromClassMetadata($classMetadataFactory);\n\ # }\n# $this->classDiscriminatorResolver = $classDiscriminatorResolver;\n# $this->objectClassResolver\ \ = ($objectClassResolver ?? 'get_class')(...);\n# }\n# \n# public function supportsNormalization(mixed\ \ $data, ?string $format = null, array $context = []): bool\n# {\n# return \\\ is_object($data) && !$data instanceof \\Traversable;\n# }\n# \n# public function\ \ normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\\\ ArrayObject|null\n# {\n# $context['_read_attributes'] = true;\n# \n# if (!isset($context['cache_key']))\ \ {\n# $context['cache_key'] = $this->getCacheKey($format, $context);\n# }\n#\ \ \n# $this->validateCallbackContext($context);\n# \n# if ($this->isCircularReference($object,\ \ $context)) {\n# return $this->handleCircularReference($object, $format, $context);\n\ # }\n# \n# $data = [];\n# $stack = [];\n# $attributes = $this->getAttributes($object,\ \ $format, $context);\n# $class = ($this->objectClassResolver)($object);\n# $classMetadata\ \ = $this->classMetadataFactory?->getMetadataFor($class);\n# $attributesMetadata\ \ = $this->classMetadataFactory?->getMetadataFor($class)->getAttributesMetadata();\n\ # if (isset($context[self::MAX_DEPTH_HANDLER])) {\n# $maxDepthHandler = $context[self::MAX_DEPTH_HANDLER];\n\ # if (!\\is_callable($maxDepthHandler)) {\n# throw new InvalidArgumentException(\\\ sprintf('The \"%s\" given in the context is not callable.', self::MAX_DEPTH_HANDLER));\n\ # }\n# } else {\n# $maxDepthHandler = null;\n# }\n# \n# foreach ($attributes as\ \ $attribute) {\n# $maxDepthReached = false;\n# if (null !== $attributesMetadata\ \ && ($maxDepthReached = $this->isMaxDepthReached($attributesMetadata, $class,\ \ $attribute, $context)) && !$maxDepthHandler) {\n# continue;\n# }\n# \n# $attributeContext\ \ = $this->getAttributeNormalizationContext($object, $attribute, $context);\n\ # \n# try {\n# $attributeValue = $attribute === $this->classDiscriminatorResolver?->getMappingForMappedObject($object)?->getTypeProperty()\n\ # ? $this->classDiscriminatorResolver?->getTypeForMappedObject($object)\n# : $this->getAttributeValue($object,\ \ $attribute, $format, $attributeContext);\n# } catch (UninitializedPropertyException|\\\ Error $e) {\n# if (($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES]\ \ ?? true) && $this->isUninitializedValueError($e)) {\n# continue;\n# }\n# throw\ \ $e;\n# }\n# \n# if ($maxDepthReached) {\n# $attributeValue = $maxDepthHandler($attributeValue,\ \ $object, $attribute, $format, $attributeContext);\n# }\n# \n# $stack[$attribute]\ \ = $this->applyCallbacks($attributeValue, $object, $attribute, $format, $attributeContext);\n\ # }\n# \n# foreach ($stack as $attribute => $attributeValue) {\n# $attributeContext\ \ = $this->getAttributeNormalizationContext($object, $attribute, $context);\n\ # \n# if (null === $attributeValue || \\is_scalar($attributeValue)) {\n# $data\ \ = $this->updateData($data, $attribute, $attributeValue, $class, $format, $attributeContext,\ \ $attributesMetadata, $classMetadata);\n# continue;\n# }\n# \n# if (!$this->serializer\ \ instanceof NormalizerInterface) {\n# throw new LogicException(\\sprintf('Cannot\ \ normalize attribute \"%s\" because the injected serializer is not a normalizer.',\ \ $attribute));\n# }\n# \n# $childContext = $this->createChildContext($attributeContext,\ \ $attribute, $format);\n# \n# $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue,\ \ $format, $childContext), $class, $format, $attributeContext, $attributesMetadata,\ \ $classMetadata);\n# }\n# \n# $preserveEmptyObjects = $context[self::PRESERVE_EMPTY_OBJECTS]\ \ ?? $this->defaultContext[self::PRESERVE_EMPTY_OBJECTS] ?? false;\n# if ($preserveEmptyObjects\ \ && !$data) {\n# return new \\ArrayObject();\n# }\n# \n# return $data;\n# }\n\ # \n# protected function instantiateObject(array &$data, string $class, array\ \ &$context, \\ReflectionClass $reflectionClass, array|bool $allowedAttributes,\ \ ?string $format = null): object\n# {\n# if ($class !== $mappedClass = $this->getMappedClass($data,\ \ $class, $context)) {\n# return $this->instantiateObject($data, $mappedClass,\ \ $context, new \\ReflectionClass($mappedClass), $allowedAttributes, $format);\n\ # }\n# \n# return parent::instantiateObject($data, $class, $context, $reflectionClass,\ \ $allowedAttributes, $format);\n# }\n# \n# /**\n# * Gets and caches attributes\ \ for the given object, format and context.\n# *\n# * @return string[]" - name: validateAndDenormalizeLegacy visibility: private parameters: - name: types - name: currentClass - name: attribute - name: data - name: format - name: context comment: "# * Extracts attributes to normalize from the class of the given object,\ \ format and context.\n# *\n# * @return string[]\n# */\n# abstract protected function\ \ extractAttributes(object $object, ?string $format = null, array $context = []):\ \ array;\n# \n# /**\n# * Gets the attribute value.\n# */\n# abstract protected\ \ function getAttributeValue(object $object, string $attribute, ?string $format\ \ = null, array $context = []): mixed;\n# \n# public function supportsDenormalization(mixed\ \ $data, string $type, ?string $format = null, array $context = []): bool\n# {\n\ # return class_exists($type) || (interface_exists($type, false) && null !== $this->classDiscriminatorResolver?->getMappingForClass($type));\n\ # }\n# \n# public function denormalize(mixed $data, string $type, ?string $format\ \ = null, array $context = []): mixed\n# {\n# $context['_read_attributes'] = false;\n\ # \n# if (!isset($context['cache_key'])) {\n# $context['cache_key'] = $this->getCacheKey($format,\ \ $context);\n# }\n# \n# $this->validateCallbackContext($context);\n# \n# if (null\ \ === $data && isset($context['value_type']) && ($context['value_type'] instanceof\ \ Type || $context['value_type'] instanceof LegacyType) && $context['value_type']->isNullable())\ \ {\n# return null;\n# }\n# \n# if (XmlEncoder::FORMAT === $format && !\\is_array($data))\ \ {\n# $data = ['#' => $data];\n# }\n# \n# $allowedAttributes = $this->getAllowedAttributes($type,\ \ $context, true);\n# $normalizedData = $this->prepareForDenormalization($data);\n\ # $extraAttributes = [];\n# \n# $mappedClass = $this->getMappedClass($normalizedData,\ \ $type, $context);\n# \n# $nestedAttributes = $this->getNestedAttributes($mappedClass);\n\ # $nestedData = $originalNestedData = [];\n# $propertyAccessor = PropertyAccess::createPropertyAccessor();\n\ # foreach ($nestedAttributes as $property => $serializedPath) {\n# if (null ===\ \ $value = $propertyAccessor->getValue($normalizedData, $serializedPath)) {\n\ # continue;\n# }\n# $convertedProperty = $this->nameConverter ? $this->nameConverter->normalize($property,\ \ $mappedClass, $format, $context) : $property;\n# $nestedData[$convertedProperty]\ \ = $value;\n# $originalNestedData[$property] = $value;\n# $normalizedData = $this->removeNestedValue($serializedPath->getElements(),\ \ $normalizedData);\n# }\n# \n# $normalizedData = $nestedData + $normalizedData;\n\ # \n# $object = $this->instantiateObject($normalizedData, $mappedClass, $context,\ \ new \\ReflectionClass($mappedClass), $allowedAttributes, $format);\n# $resolvedClass\ \ = ($this->objectClassResolver)($object);\n# \n# foreach ($normalizedData as\ \ $attribute => $value) {\n# if ($this->nameConverter) {\n# $notConverted = $attribute;\n\ # $attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format,\ \ $context);\n# if (isset($nestedData[$notConverted]) && !isset($originalNestedData[$attribute]))\ \ {\n# throw new LogicException(\\sprintf('Duplicate values for key \"%s\" found.\ \ One value is set via the SerializedPath attribute: \"%s\", the other one is\ \ set via the SerializedName attribute: \"%s\".', $notConverted, implode('->',\ \ $nestedAttributes[$notConverted]->getElements()), $attribute));\n# }\n# }\n\ # \n# $attributeContext = $this->getAttributeDenormalizationContext($resolvedClass,\ \ $attribute, $context);\n# \n# if ((false !== $allowedAttributes && !\\in_array($attribute,\ \ $allowedAttributes, true)) || !$this->isAllowedAttribute($resolvedClass, $attribute,\ \ $format, $context)) {\n# if (!($context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES]))\ \ {\n# $extraAttributes[] = $attribute;\n# }\n# \n# continue;\n# }\n# \n# if ($attributeContext[self::DEEP_OBJECT_TO_POPULATE]\ \ ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? false) {\n# $discriminatorMapping\ \ = $this->classDiscriminatorResolver?->getMappingForMappedObject($object);\n\ # \n# try {\n# $attributeContext[self::OBJECT_TO_POPULATE] = $attribute === $discriminatorMapping?->getTypeProperty()\n\ # ? $discriminatorMapping\n# : $this->getAttributeValue($object, $attribute, $format,\ \ $attributeContext);\n# } catch (NoSuchPropertyException) {\n# } catch (UninitializedPropertyException|\\\ Error $e) {\n# if (!(($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES]\ \ ?? true) && $this->isUninitializedValueError($e))) {\n# throw $e;\n# }\n# }\n\ # }\n# \n# if (null !== $type = $this->getType($resolvedClass, $attribute)) {\n\ # try {\n# // BC layer for PropertyTypeExtractorInterface::getTypes().\n# // Can\ \ be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed\ \ (8.0).\n# if (\\is_array($type)) {\n# $value = $this->validateAndDenormalizeLegacy($type,\ \ $resolvedClass, $attribute, $value, $format, $attributeContext);\n# } else {\n\ # $value = $this->validateAndDenormalize($type, $resolvedClass, $attribute, $value,\ \ $format, $attributeContext);\n# }\n# } catch (NotNormalizableValueException\ \ $exception) {\n# if (isset($context['not_normalizable_value_exceptions'])) {\n\ # $context['not_normalizable_value_exceptions'][] = $exception;\n# continue;\n\ # }\n# throw $exception;\n# }\n# }\n# \n# $value = $this->applyCallbacks($value,\ \ $resolvedClass, $attribute, $format, $attributeContext);\n# \n# try {\n# $this->setAttributeValue($object,\ \ $attribute, $value, $format, $attributeContext);\n# } catch (PropertyAccessInvalidArgumentException\ \ $e) {\n# $exception = NotNormalizableValueException::createForUnexpectedDataType(\n\ # \\sprintf('Failed to denormalize attribute \"%s\" value for class \"%s\": '.$e->getMessage(),\ \ $attribute, $resolvedClass),\n# $data,\n# $e instanceof InvalidTypeException\ \ ? [$e->expectedType] : ['unknown'],\n# $attributeContext['deserialization_path']\ \ ?? null,\n# false,\n# $e->getCode(),\n# $e\n# );\n# if (isset($context['not_normalizable_value_exceptions']))\ \ {\n# $context['not_normalizable_value_exceptions'][] = $exception;\n# continue;\n\ # }\n# throw $exception;\n# }\n# }\n# \n# if ($extraAttributes) {\n# throw new\ \ ExtraAttributesException($extraAttributes);\n# }\n# \n# return $object;\n# }\n\ # \n# abstract protected function setAttributeValue(object $object, string $attribute,\ \ mixed $value, ?string $format = null, array $context = []): void;\n# \n# /**\n\ # * Validates the submitted data and denormalizes it.\n# *\n# * BC layer for PropertyTypeExtractorInterface::getTypes().\n\ # * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed\ \ (8.0).\n# *\n# * @param LegacyType[] $types\n# *\n# * @throws NotNormalizableValueException\n\ # * @throws ExtraAttributesException\n# * @throws MissingConstructorArgumentsException\n\ # * @throws LogicException" - name: validateAndDenormalize visibility: private parameters: - name: type - name: currentClass - name: attribute - name: data - name: format - name: context comment: '# * Validates the submitted data and denormalizes it. # * # * @throws NotNormalizableValueException # * @throws ExtraAttributesException # * @throws MissingConstructorArgumentsException # * @throws LogicException' - name: denormalizeParameter visibility: protected parameters: - name: class - name: parameter - name: parameterName - name: parameterData - name: context - name: format default: 'null' comment: '# * @internal' - name: getType visibility: private parameters: - name: currentClass - name: attribute comment: '# * @return Type|list|null' - name: getPropertyType visibility: private parameters: - name: className - name: property comment: '# * BC layer for PropertyTypeExtractorInterface::getTypes(). # * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). # * # * @return Type|list|null' - name: updateData visibility: private parameters: - name: data - name: attribute - name: attributeValue - name: class - name: format - name: context - name: attributesMetadata - name: classMetadata comment: '# * Sets an attribute and apply the name converter if necessary.' - name: isMaxDepthReached visibility: private parameters: - name: attributesMetadata - name: class - name: attribute - name: '&$context' comment: '# * Is the max depth reached for the given attribute? # * # * @param AttributeMetadataInterface[] $attributesMetadata' - name: createChildContext visibility: protected parameters: - name: parentContext - name: attribute - name: format comment: '# * Overwritten to update the cache key for the child. # * # * We must not mix up the attribute cache between parent and children. # * # * @internal' - name: getCacheKey visibility: private parameters: - name: format - name: context comment: '# * Builds the cache key for the attributes cache. # * # * The key must be different for every option in the context that could change which attributes should be handled.' - name: isUninitializedValueError visibility: private parameters: - name: e comment: '# * This error may occur when specific object normalizer implementation gets attribute value # * by accessing a public uninitialized property or by calling a method accessing such property.' - name: getNestedAttributes visibility: private parameters: - name: class comment: '# * Returns all attributes with a SerializedPath attribute and the respective path.' - name: removeNestedValue visibility: private parameters: - name: path - name: data comment: null - name: getMappedClass visibility: private parameters: - name: data - name: class - name: context comment: '# * @return class-string' traits: - Symfony\Component\PropertyAccess\Exception\InvalidTypeException - Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException - Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException - Symfony\Component\PropertyAccess\PropertyAccess - Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface - Symfony\Component\Serializer\Encoder\CsvEncoder - Symfony\Component\Serializer\Encoder\JsonEncoder - Symfony\Component\Serializer\Encoder\XmlEncoder - Symfony\Component\Serializer\Exception\ExtraAttributesException - Symfony\Component\Serializer\Exception\InvalidArgumentException - Symfony\Component\Serializer\Exception\LogicException - Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException - Symfony\Component\Serializer\Exception\NotNormalizableValueException - Symfony\Component\Serializer\Mapping\AttributeMetadataInterface - Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata - Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface - Symfony\Component\Serializer\Mapping\ClassMetadataInterface - Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface - Symfony\Component\Serializer\NameConverter\NameConverterInterface - Symfony\Component\TypeInfo\Type - Symfony\Component\TypeInfo\Type\CollectionType - Symfony\Component\TypeInfo\Type\IntersectionType - Symfony\Component\TypeInfo\Type\ObjectType - Symfony\Component\TypeInfo\Type\UnionType - Symfony\Component\TypeInfo\TypeIdentifier interfaces: []