Submit
Path:
~
/
/
proc
/
thread-self
/
root
/
opt
/
psa
/
admin
/
plib
/
modules
/
wp-toolkit
/
vendor
/
symfony
/
serializer
/
Normalizer
/
File Content:
AbstractObjectNormalizer.php
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Normalizer; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\PropertyAccess\Exception\InvalidArgumentException as PropertyAccessInvalidArgumentException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\PropertyAccess\Exception\InvalidTypeException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\PropertyAccess\PropertyAccess; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\PropertyInfo\Type as LegacyType; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Encoder\CsvEncoder; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Encoder\JsonEncoder; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Encoder\XmlEncoder; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Exception\ExtraAttributesException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Exception\InvalidArgumentException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Exception\LogicException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Exception\NotNormalizableValueException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Mapping\ClassMetadataInterface; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\Serializer\NameConverter\NameConverterInterface; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Exception\LogicException as TypeInfoLogicException; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type\BuiltinType; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type\CollectionType; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type\IntersectionType; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type\NullableType; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type\ObjectType; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type\UnionType; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\Type\WrappingTypeInterface; use WPToolkitDependenciesIsolationPrefix\Symfony\Component\TypeInfo\TypeIdentifier; /** * Base class for a normalizer dealing with objects. * * @author Kévin Dunglas <dunglas@gmail.com> */ abstract class AbstractObjectNormalizer extends AbstractNormalizer { /** * Set to true to respect the max depth metadata on fields. */ public const ENABLE_MAX_DEPTH = 'enable_max_depth'; /** * How to track the current depth in the context. */ public const DEPTH_KEY_PATTERN = 'depth_%s::%s'; /** * While denormalizing, we can verify that type matches. * * You can disable this by setting this flag to true. */ public const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement'; /** * Flag to control whether fields with the value `null` should be output * when normalizing or omitted. */ public const SKIP_NULL_VALUES = 'skip_null_values'; /** * Flag to control whether uninitialized PHP>=7.4 typed class properties * should be excluded when normalizing. */ public const SKIP_UNINITIALIZED_VALUES = 'skip_uninitialized_values'; /** * Callback to allow to set a value for an attribute when the max depth has * been reached. * * If no callback is given, the attribute is skipped. If a callable is * given, its return value is used (even if null). * * The arguments are: * * - mixed $attributeValue value of this field * - object $object the whole object being normalized * - string $attributeName name of the attribute being normalized * - string $format the requested format * - array $context the serialization context */ public const MAX_DEPTH_HANDLER = 'max_depth_handler'; /** * Specify which context key are not relevant to determine which attributes * of an object to (de)normalize. */ public const EXCLUDE_FROM_CACHE_KEY = 'exclude_from_cache_key'; /** * Flag to tell the denormalizer to also populate existing objects on * attributes of the main object. * * Setting this to true is only useful if you also specify the root object * in OBJECT_TO_POPULATE. */ public const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate'; /** * Flag to control whether an empty object should be kept as an object (in * JSON: {}) or converted to a list (in JSON: []). */ public const PRESERVE_EMPTY_OBJECTS = 'preserve_empty_objects'; protected ?ClassDiscriminatorResolverInterface $classDiscriminatorResolver; /** * @var array<string, Type|list<LegacyType>|false> */ private array $typeCache = []; private array $attributesCache = []; private readonly \Closure $objectClassResolver; public function __construct(?ClassMetadataFactoryInterface $classMetadataFactory = null, ?NameConverterInterface $nameConverter = null, private ?PropertyTypeExtractorInterface $propertyTypeExtractor = null, ?ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, ?callable $objectClassResolver = null, array $defaultContext = []) { parent::__construct($classMetadataFactory, $nameConverter, $defaultContext); if (isset($this->defaultContext[self::MAX_DEPTH_HANDLER]) && !\is_callable($this->defaultContext[self::MAX_DEPTH_HANDLER])) { throw new InvalidArgumentException(\sprintf('The "%s" given in the default context is not callable.', self::MAX_DEPTH_HANDLER)); } $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = \array_merge($this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] ?? [], [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS]); if ($classMetadataFactory) { $classDiscriminatorResolver ??= new ClassDiscriminatorFromClassMetadata($classMetadataFactory); } $this->classDiscriminatorResolver = $classDiscriminatorResolver; $this->objectClassResolver = ($objectClassResolver ?? 'get_class')(...); } public function supportsNormalization(mixed $data, ?string $format = null, array $context = []) : bool { return \is_object($data) && !$data instanceof \Traversable; } public function normalize(mixed $object, ?string $format = null, array $context = []) : array|string|int|float|bool|\ArrayObject|null { $context['_read_attributes'] = \true; if (!isset($context['cache_key'])) { $context['cache_key'] = $this->getCacheKey($format, $context); } $this->validateCallbackContext($context); if ($this->isCircularReference($object, $context)) { return $this->handleCircularReference($object, $format, $context); } $data = []; $stack = []; $attributes = $this->getAttributes($object, $format, $context); $class = ($this->objectClassResolver)($object); $classMetadata = $this->classMetadataFactory?->getMetadataFor($class); $attributesMetadata = $this->classMetadataFactory?->getMetadataFor($class)->getAttributesMetadata(); if (isset($context[self::MAX_DEPTH_HANDLER])) { $maxDepthHandler = $context[self::MAX_DEPTH_HANDLER]; if (!\is_callable($maxDepthHandler)) { throw new InvalidArgumentException(\sprintf('The "%s" given in the context is not callable.', self::MAX_DEPTH_HANDLER)); } } else { $maxDepthHandler = null; } foreach ($attributes as $attribute) { $maxDepthReached = \false; if (null !== $attributesMetadata && ($maxDepthReached = $this->isMaxDepthReached($attributesMetadata, $class, $attribute, $context)) && !$maxDepthHandler) { continue; } $attributeContext = $this->getAttributeNormalizationContext($object, $attribute, $context); try { $attributeValue = $attribute === $this->classDiscriminatorResolver?->getMappingForMappedObject($object)?->getTypeProperty() ? $this->classDiscriminatorResolver?->getTypeForMappedObject($object) : $this->getAttributeValue($object, $attribute, $format, $attributeContext); } catch (UninitializedPropertyException|\Error $e) { if (($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? \true) && $this->isUninitializedValueError($e)) { continue; } throw $e; } if ($maxDepthReached) { $attributeValue = $maxDepthHandler($attributeValue, $object, $attribute, $format, $attributeContext); } $stack[$attribute] = $this->applyCallbacks($attributeValue, $object, $attribute, $format, $attributeContext); } foreach ($stack as $attribute => $attributeValue) { $attributeContext = $this->getAttributeNormalizationContext($object, $attribute, $context); if (null === $attributeValue || \is_scalar($attributeValue)) { $data = $this->updateData($data, $attribute, $attributeValue, $class, $format, $attributeContext, $attributesMetadata, $classMetadata); continue; } if (!$this->serializer instanceof NormalizerInterface) { throw new LogicException(\sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer.', $attribute)); } $childContext = $this->createChildContext($attributeContext, $attribute, $format); $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $childContext), $class, $format, $attributeContext, $attributesMetadata, $classMetadata); } $preserveEmptyObjects = $context[self::PRESERVE_EMPTY_OBJECTS] ?? $this->defaultContext[self::PRESERVE_EMPTY_OBJECTS] ?? \false; if ($preserveEmptyObjects && !$data) { return new \ArrayObject(); } return $data; } protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, ?string $format = null) : object { if ($class !== ($mappedClass = $this->getMappedClass($data, $class, $context))) { return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format); } return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format); } /** * Gets and caches attributes for the given object, format and context. * * @return string[] */ protected function getAttributes(object $object, ?string $format, array $context) : array { $class = ($this->objectClassResolver)($object); $key = $class . '-' . $context['cache_key']; if (isset($this->attributesCache[$key])) { return $this->attributesCache[$key]; } $allowedAttributes = $this->getAllowedAttributes($object, $context, \true); if (\false !== $allowedAttributes) { if ($context['cache_key']) { $this->attributesCache[$key] = $allowedAttributes; } return $allowedAttributes; } $attributes = $this->extractAttributes($object, $format, $context); if ($mapping = $this->classDiscriminatorResolver?->getMappingForMappedObject($object)) { \array_unshift($attributes, $mapping->getTypeProperty()); } if ($context['cache_key'] && \stdClass::class !== $class) { $this->attributesCache[$key] = $attributes; } return $attributes; } /** * Extracts attributes to normalize from the class of the given object, format and context. * * @return string[] */ protected abstract function extractAttributes(object $object, ?string $format = null, array $context = []) : array; /** * Gets the attribute value. */ protected abstract function getAttributeValue(object $object, string $attribute, ?string $format = null, array $context = []) : mixed; public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []) : bool { return \class_exists($type) || \interface_exists($type, \false) && null !== $this->classDiscriminatorResolver?->getMappingForClass($type); } public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []) : mixed { $context['_read_attributes'] = \false; if (!isset($context['cache_key'])) { $context['cache_key'] = $this->getCacheKey($format, $context); } $this->validateCallbackContext($context); if (null === $data && isset($context['value_type']) && ($context['value_type'] instanceof Type || $context['value_type'] instanceof LegacyType) && $context['value_type']->isNullable()) { return null; } if (XmlEncoder::FORMAT === $format && !\is_array($data)) { $data = ['#' => $data]; } $allowedAttributes = $this->getAllowedAttributes($type, $context, \true); $normalizedData = $this->prepareForDenormalization($data); $extraAttributes = []; $mappedClass = $this->getMappedClass($normalizedData, $type, $context); $nestedAttributes = $this->getNestedAttributes($mappedClass); $nestedData = $originalNestedData = []; $propertyAccessor = PropertyAccess::createPropertyAccessor(); foreach ($nestedAttributes as $property => $serializedPath) { if (null === ($value = $propertyAccessor->getValue($normalizedData, $serializedPath))) { continue; } $convertedProperty = $this->nameConverter ? $this->nameConverter->normalize($property, $mappedClass, $format, $context) : $property; $nestedData[$convertedProperty] = $value; $originalNestedData[$property] = $value; $normalizedData = $this->removeNestedValue($serializedPath->getElements(), $normalizedData); } $normalizedData = $nestedData + $normalizedData; $object = $this->instantiateObject($normalizedData, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format); $resolvedClass = ($this->objectClassResolver)($object); foreach ($normalizedData as $attribute => $value) { if ($this->nameConverter) { $notConverted = $attribute; $attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context); if (isset($nestedData[$notConverted]) && !isset($originalNestedData[$attribute])) { 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)); } } $attributeContext = $this->getAttributeDenormalizationContext($resolvedClass, $attribute, $context); if (\false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes, \true) || !$this->isAllowedAttribute($resolvedClass, $attribute, $format, $context)) { if (!($context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES])) { $extraAttributes[] = $attribute; } continue; } if ($attributeContext[self::DEEP_OBJECT_TO_POPULATE] ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? \false) { $discriminatorMapping = $this->classDiscriminatorResolver?->getMappingForMappedObject($object); try { $attributeContext[self::OBJECT_TO_POPULATE] = $attribute === $discriminatorMapping?->getTypeProperty() ? $discriminatorMapping : $this->getAttributeValue($object, $attribute, $format, $attributeContext); } catch (NoSuchPropertyException) { } catch (UninitializedPropertyException|\Error $e) { if (!(($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? \true) && $this->isUninitializedValueError($e))) { throw $e; } } } if (null !== ($type = $this->getType($resolvedClass, $attribute))) { try { // BC layer for PropertyTypeExtractorInterface::getTypes(). // Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). if (\is_array($type)) { $value = $this->validateAndDenormalizeLegacy($type, $resolvedClass, $attribute, $value, $format, $attributeContext); } else { $value = $this->validateAndDenormalize($type, $resolvedClass, $attribute, $value, $format, $attributeContext); } } catch (NotNormalizableValueException $exception) { if (isset($context['not_normalizable_value_exceptions'])) { $context['not_normalizable_value_exceptions'][] = $exception; continue; } throw $exception; } } $value = $this->applyCallbacks($value, $resolvedClass, $attribute, $format, $attributeContext); try { $this->setAttributeValue($object, $attribute, $value, $format, $attributeContext); } catch (PropertyAccessInvalidArgumentException $e) { $exception = NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Failed to denormalize attribute "%s" value for class "%s": ' . $e->getMessage(), $attribute, $resolvedClass), $data, $e instanceof InvalidTypeException ? [$e->expectedType] : ['unknown'], $attributeContext['deserialization_path'] ?? null, \false, $e->getCode(), $e); if (isset($context['not_normalizable_value_exceptions'])) { $context['not_normalizable_value_exceptions'][] = $exception; continue; } throw $exception; } } if ($extraAttributes) { throw new ExtraAttributesException($extraAttributes); } return $object; } protected abstract function setAttributeValue(object $object, string $attribute, mixed $value, ?string $format = null, array $context = []) : void; /** * Validates the submitted data and denormalizes it. * * BC layer for PropertyTypeExtractorInterface::getTypes(). * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). * * @param LegacyType[] $types * * @throws NotNormalizableValueException * @throws ExtraAttributesException * @throws MissingConstructorArgumentsException * @throws LogicException */ private function validateAndDenormalizeLegacy(array $types, string $currentClass, string $attribute, mixed $data, ?string $format, array $context) : mixed { $expectedTypes = []; $isUnionType = \count($types) > 1; $e = null; $extraAttributesException = null; $missingConstructorArgumentsException = null; $isNullable = \false; foreach ($types as $type) { if (null === $data && $type->isNullable()) { return null; } $collectionValueType = $type->isCollection() ? $type->getCollectionValueTypes()[0] ?? null : null; // Fix a collection that contains the only one element // This is special to xml format only if ('xml' === $format && null !== $collectionValueType && (!\is_array($data) || !\is_int(\key($data)))) { $data = [$data]; } // This try-catch should cover all NotNormalizableValueException (and all return branches after the first // exception) so we could try denormalizing all types of an union type. If the target type is not an union // type, we will just re-throw the catched exception. // In the case of no denormalization succeeds with an union type, it will fall back to the default exception // with the acceptable types list. try { // In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine, // if a value is meant to be a string, float, int or a boolean value from the serialized representation. // That's why we have to transform the values, if one of these non-string basic datatypes is expected. $builtinType = $type->getBuiltinType(); if (\is_string($data) && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) { if ('' === $data) { if (LegacyType::BUILTIN_TYPE_ARRAY === $builtinType) { return []; } if (LegacyType::BUILTIN_TYPE_STRING === $builtinType) { return ''; } // Don't return null yet because Object-types that come first may accept empty-string too $isNullable = $isNullable ?: $type->isNullable(); } switch ($builtinType) { case LegacyType::BUILTIN_TYPE_BOOL: // according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1" if ('false' === $data || '0' === $data) { $data = \false; } elseif ('true' === $data || '1' === $data) { $data = \true; } else { throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be bool ("%s" given).', $attribute, $currentClass, $data), $data, [LegacyType::BUILTIN_TYPE_BOOL], $context['deserialization_path'] ?? null); } break; case LegacyType::BUILTIN_TYPE_INT: if (\ctype_digit(isset($data[0]) && '-' === $data[0] ? \substr($data, 1) : $data)) { $data = (int) $data; } else { throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be int ("%s" given).', $attribute, $currentClass, $data), $data, [LegacyType::BUILTIN_TYPE_INT], $context['deserialization_path'] ?? null); } break; case LegacyType::BUILTIN_TYPE_FLOAT: if (\is_numeric($data)) { return (float) $data; } return match ($data) { 'NaN' => \NAN, 'INF' => \INF, '-INF' => -\INF, default => throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be float ("%s" given).', $attribute, $currentClass, $data), $data, [LegacyType::BUILTIN_TYPE_FLOAT], $context['deserialization_path'] ?? null), }; } } if (null !== $collectionValueType && LegacyType::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) { $builtinType = LegacyType::BUILTIN_TYPE_OBJECT; $class = $collectionValueType->getClassName() . '[]'; if (\count($collectionKeyType = $type->getCollectionKeyTypes()) > 0) { $context['key_type'] = \count($collectionKeyType) > 1 ? $collectionKeyType : $collectionKeyType[0]; } $context['value_type'] = $collectionValueType; } elseif ($type->isCollection() && \count($collectionValueType = $type->getCollectionValueTypes()) > 0 && LegacyType::BUILTIN_TYPE_ARRAY === $collectionValueType[0]->getBuiltinType()) { // get inner type for any nested array [$innerType] = $collectionValueType; // note that it will break for any other builtinType $dimensions = '[]'; while (\count($innerType->getCollectionValueTypes()) > 0 && LegacyType::BUILTIN_TYPE_ARRAY === $innerType->getBuiltinType()) { $dimensions .= '[]'; [$innerType] = $innerType->getCollectionValueTypes(); } if (null !== $innerType->getClassName()) { // the builtinType is the inner one and the class is the class followed by []...[] $builtinType = $innerType->getBuiltinType(); $class = $innerType->getClassName() . $dimensions; } else { // default fallback (keep it as array) $builtinType = $type->getBuiltinType(); $class = $type->getClassName(); } } else { $builtinType = $type->getBuiltinType(); $class = $type->getClassName(); } $expectedTypes[LegacyType::BUILTIN_TYPE_OBJECT === $builtinType && $class ? $class : $builtinType] = \true; if (LegacyType::BUILTIN_TYPE_OBJECT === $builtinType && null !== $class) { if (!$this->serializer instanceof DenormalizerInterface) { throw new LogicException(\sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer.', $attribute, $class)); } $childContext = $this->createChildContext($context, $attribute, $format); if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) { return $this->serializer->denormalize($data, $class, $format, $childContext); } } // JSON only has a Number type corresponding to both int and float PHP types. // PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert // floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible). // PHP's json_decode automatically converts Numbers without a decimal part to integers. // To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when // a float is expected. if (LegacyType::BUILTIN_TYPE_FLOAT === $builtinType && \is_int($data) && null !== $format && \str_contains($format, JsonEncoder::FORMAT)) { return (float) $data; } if (LegacyType::BUILTIN_TYPE_BOOL === $builtinType && (\is_string($data) || \is_int($data)) && ($context[self::FILTER_BOOL] ?? \false)) { return \filter_var($data, \FILTER_VALIDATE_BOOL, \FILTER_NULL_ON_FAILURE); } if (LegacyType::BUILTIN_TYPE_FALSE === $builtinType && \false === $data || LegacyType::BUILTIN_TYPE_TRUE === $builtinType && \true === $data) { return $data; } switch ($builtinType) { case LegacyType::BUILTIN_TYPE_ARRAY: case LegacyType::BUILTIN_TYPE_BOOL: case LegacyType::BUILTIN_TYPE_CALLABLE: case LegacyType::BUILTIN_TYPE_FLOAT: case LegacyType::BUILTIN_TYPE_INT: case LegacyType::BUILTIN_TYPE_ITERABLE: case LegacyType::BUILTIN_TYPE_NULL: case LegacyType::BUILTIN_TYPE_OBJECT: case LegacyType::BUILTIN_TYPE_RESOURCE: case LegacyType::BUILTIN_TYPE_STRING: if (('is_' . $builtinType)($data)) { return $data; } break; } } catch (NotNormalizableValueException|InvalidArgumentException $e) { if (!$isUnionType && !$isNullable) { throw $e; } } catch (ExtraAttributesException $e) { if (!$isUnionType && !$isNullable) { throw $e; } $extraAttributesException ??= $e; } catch (MissingConstructorArgumentsException $e) { if (!$isUnionType && !$isNullable) { throw $e; } $missingConstructorArgumentsException ??= $e; } } if ($isNullable) { return null; } if ($extraAttributesException) { throw $extraAttributesException; } if ($missingConstructorArgumentsException) { throw $missingConstructorArgumentsException; } if (!$isUnionType && $e) { throw $e; } if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? \false) { return $data; } throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, \implode('", "', \array_keys($expectedTypes)), \get_debug_type($data)), $data, \array_keys($expectedTypes), $context['deserialization_path'] ?? $attribute); } /** * Validates the submitted data and denormalizes it. * * @throws NotNormalizableValueException * @throws ExtraAttributesException * @throws MissingConstructorArgumentsException * @throws LogicException */ private function validateAndDenormalize(Type $type, string $currentClass, string $attribute, mixed $data, ?string $format, array $context) : mixed { $expectedTypes = []; // BC layer for type-info < 7.2 if (\method_exists(Type::class, 'asNonNullable')) { $isUnionType = $type->asNonNullable() instanceof UnionType; } else { $isUnionType = $type instanceof UnionType; } $e = null; $extraAttributesException = null; $missingConstructorArgumentsException = null; $types = match (\true) { $type instanceof IntersectionType => throw new LogicException('Unable to handle intersection type.'), $type instanceof UnionType => $type->getTypes(), default => [$type], }; foreach ($types as $t) { if (null === $data && $type->isNullable()) { return null; } $collectionKeyType = $collectionValueType = null; if ($t instanceof CollectionType) { $collectionKeyType = $t->getCollectionKeyType(); $collectionValueType = $t->getCollectionValueType(); } // BC layer for type-info < 7.2 if (\method_exists(Type::class, 'getBaseType')) { $t = $t->getBaseType(); } else { while ($t instanceof WrappingTypeInterface) { $t = $t->getWrappedType(); } } // Fix a collection that contains the only one element // This is special to xml format only if ('xml' === $format && $collectionValueType && (!\is_array($data) || !\is_int(\key($data)))) { // BC layer for type-info < 7.2 $isMixedType = \method_exists(Type::class, 'isA') ? $collectionValueType->isA(TypeIdentifier::MIXED) : $collectionValueType->isIdentifiedBy(TypeIdentifier::MIXED); if (!$isMixedType) { $data = [$data]; } } // This try-catch should cover all NotNormalizableValueException (and all return branches after the first // exception) so we could try denormalizing all types of an union type. If the target type is not an union // type, we will just re-throw the catched exception. // In the case of no denormalization succeeds with an union type, it will fall back to the default exception // with the acceptable types list. try { // In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine, // if a value is meant to be a string, float, int or a boolean value from the serialized representation. // That's why we have to transform the values, if one of these non-string basic datatypes is expected. $typeIdentifier = $t->getTypeIdentifier(); if (\is_string($data) && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) { if ('' === $data) { if (TypeIdentifier::ARRAY === $typeIdentifier) { return []; } if (TypeIdentifier::STRING === $typeIdentifier) { return ''; } } switch ($typeIdentifier) { case TypeIdentifier::BOOL: // according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1" if ('false' === $data || '0' === $data) { $data = \false; } elseif ('true' === $data || '1' === $data) { $data = \true; } else { throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be bool ("%s" given).', $attribute, $currentClass, $data), $data, [Type::bool()], $context['deserialization_path'] ?? null); } break; case TypeIdentifier::INT: if (\ctype_digit(isset($data[0]) && '-' === $data[0] ? \substr($data, 1) : $data)) { $data = (int) $data; } else { throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be int ("%s" given).', $attribute, $currentClass, $data), $data, [Type::int()], $context['deserialization_path'] ?? null); } break; case TypeIdentifier::FLOAT: if (\is_numeric($data)) { return (float) $data; } return match ($data) { 'NaN' => \NAN, 'INF' => \INF, '-INF' => -\INF, default => throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be float ("%s" given).', $attribute, $currentClass, $data), $data, [Type::float()], $context['deserialization_path'] ?? null), }; } } if ($collectionValueType) { try { $collectionValueBaseType = $collectionValueType; // BC layer for type-info < 7.2 if (!\interface_exists(WrappingTypeInterface::class)) { $collectionValueBaseType = $collectionValueType->getBaseType(); } else { while ($collectionValueBaseType instanceof WrappingTypeInterface) { $collectionValueBaseType = $collectionValueBaseType->getWrappedType(); } } } catch (TypeInfoLogicException) { $collectionValueBaseType = Type::mixed(); } if ($collectionValueBaseType instanceof ObjectType) { $typeIdentifier = TypeIdentifier::OBJECT; $class = $collectionValueBaseType->getClassName() . '[]'; $context['key_type'] = $collectionKeyType; $context['value_type'] = $collectionValueType; } elseif (!\class_exists(NullableType::class) && TypeIdentifier::ARRAY === $collectionValueBaseType->getTypeIdentifier() || $collectionValueBaseType instanceof BuiltinType && TypeIdentifier::ARRAY === $collectionValueBaseType->getTypeIdentifier()) { // get inner type for any nested array $innerType = $collectionValueType; if ($innerType instanceof NullableType) { $innerType = $innerType->getWrappedType(); } // note that it will break for any other builtinType $dimensions = '[]'; while ($innerType instanceof CollectionType) { $dimensions .= '[]'; $innerType = $innerType->getCollectionValueType(); if ($innerType instanceof NullableType) { $innerType = $innerType->getWrappedType(); } } while ($innerType instanceof WrappingTypeInterface) { $innerType = $innerType->getWrappedType(); } if ($innerType instanceof ObjectType) { // the builtinType is the inner one and the class is the class followed by []...[] $typeIdentifier = TypeIdentifier::OBJECT; $class = $innerType->getClassName() . $dimensions; } else { // default fallback (keep it as array) if ($t instanceof ObjectType) { $typeIdentifier = TypeIdentifier::OBJECT; $class = $t->getClassName(); } else { $typeIdentifier = $t->getTypeIdentifier(); $class = null; } } } elseif ($t instanceof ObjectType) { $typeIdentifier = TypeIdentifier::OBJECT; $class = $t->getClassName(); } else { $typeIdentifier = $t->getTypeIdentifier(); $class = null; } } else { if ($t instanceof ObjectType) { $typeIdentifier = TypeIdentifier::OBJECT; $class = $t->getClassName(); } else { $typeIdentifier = $t->getTypeIdentifier(); $class = null; } } $expectedTypes[TypeIdentifier::OBJECT === $typeIdentifier && $class ? $class : $typeIdentifier->value] = \true; if (TypeIdentifier::OBJECT === $typeIdentifier && null !== $class) { if (!$this->serializer instanceof DenormalizerInterface) { throw new LogicException(\sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer.', $attribute, $class)); } $childContext = $this->createChildContext($context, $attribute, $format); if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) { return $this->serializer->denormalize($data, $class, $format, $childContext); } } // JSON only has a Number type corresponding to both int and float PHP types. // PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert // floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible). // PHP's json_decode automatically converts Numbers without a decimal part to integers. // To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when // a float is expected. if (TypeIdentifier::FLOAT === $typeIdentifier && \is_int($data) && null !== $format && \str_contains($format, JsonEncoder::FORMAT)) { return (float) $data; } if (TypeIdentifier::BOOL === $typeIdentifier && (\is_string($data) || \is_int($data)) && ($context[self::FILTER_BOOL] ?? \false)) { return \filter_var($data, \FILTER_VALIDATE_BOOL, \FILTER_NULL_ON_FAILURE); } $dataMatchesExpectedType = match ($typeIdentifier) { TypeIdentifier::ARRAY => \is_array($data), TypeIdentifier::BOOL => \is_bool($data), TypeIdentifier::CALLABLE => \is_callable($data), TypeIdentifier::FALSE => \false === $data, TypeIdentifier::FLOAT => \is_float($data), TypeIdentifier::INT => \is_int($data), TypeIdentifier::ITERABLE => \is_iterable($data), TypeIdentifier::MIXED => \true, TypeIdentifier::NULL => null === $data, TypeIdentifier::OBJECT => \is_object($data), TypeIdentifier::RESOURCE => \is_resource($data), TypeIdentifier::STRING => \is_string($data), TypeIdentifier::TRUE => \true === $data, default => \false, }; if ($dataMatchesExpectedType) { return $data; } } catch (NotNormalizableValueException|InvalidArgumentException $e) { if (!$type instanceof UnionType) { throw $e; } } catch (ExtraAttributesException $e) { if (!$type instanceof UnionType) { throw $e; } $extraAttributesException ??= $e; } catch (MissingConstructorArgumentsException $e) { if (!$type instanceof UnionType) { throw $e; } $missingConstructorArgumentsException ??= $e; } } if ('' === $data && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format) && $type->isNullable()) { return null; } if ($extraAttributesException) { throw $extraAttributesException; } if ($missingConstructorArgumentsException) { throw $missingConstructorArgumentsException; } // BC layer for type-info < 7.2 if (!\class_exists(NullableType::class)) { if (!$isUnionType && $e) { throw $e; } } else { if ($e && !($type instanceof UnionType && !$type instanceof NullableType)) { throw $e; } } if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? \false) { return $data; } throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, \implode('", "', \array_keys($expectedTypes)), \get_debug_type($data)), $data, \array_keys($expectedTypes), $context['deserialization_path'] ?? $attribute); } /** * @internal */ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, string $parameterName, mixed $parameterData, array $context, ?string $format = null) : mixed { if ($parameter->isVariadic() || null === $this->propertyTypeExtractor || null === ($type = $this->getType($class->getName(), $parameterName))) { return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format); } // BC layer for PropertyTypeExtractorInterface::getTypes(). // Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). if (\is_array($type)) { $parameterData = $this->validateAndDenormalizeLegacy($type, $class->getName(), $parameterName, $parameterData, $format, $context); } else { $parameterData = $this->validateAndDenormalize($type, $class->getName(), $parameterName, $parameterData, $format, $context); } $parameterData = $this->applyCallbacks($parameterData, $class->getName(), $parameterName, $format, $context); return $this->applyFilterBool($parameter, $parameterData, $context); } /** * @return Type|list<LegacyType>|null */ private function getType(string $currentClass, string $attribute) : Type|array|null { if (null === $this->propertyTypeExtractor) { return null; } $key = $currentClass . '::' . $attribute; if (isset($this->typeCache[$key])) { return \false === $this->typeCache[$key] ? null : $this->typeCache[$key]; } if (null !== ($type = $this->getPropertyType($currentClass, $attribute))) { return $this->typeCache[$key] = $type; } if ($discriminatorMapping = $this->classDiscriminatorResolver?->getMappingForClass($currentClass)) { if ($discriminatorMapping->getTypeProperty() === $attribute) { return $this->typeCache[$key] = Type::string(); } foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) { if (null !== ($type = $this->getPropertyType($mappedClass, $attribute))) { return $this->typeCache[$key] = $type; } } } $this->typeCache[$key] = \false; return null; } /** * BC layer for PropertyTypeExtractorInterface::getTypes(). * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). * * @return Type|list<LegacyType>|null */ private function getPropertyType(string $className, string $property) : Type|array|null { if (\class_exists(Type::class) && \method_exists($this->propertyTypeExtractor, 'getType')) { return $this->propertyTypeExtractor->getType($className, $property); } return $this->propertyTypeExtractor->getTypes($className, $property); } /** * Sets an attribute and apply the name converter if necessary. */ private function updateData(array $data, string $attribute, mixed $attributeValue, string $class, ?string $format, array $context, ?array $attributesMetadata, ?ClassMetadataInterface $classMetadata) : array { if (null === $attributeValue && ($context[self::SKIP_NULL_VALUES] ?? $this->defaultContext[self::SKIP_NULL_VALUES] ?? \false)) { return $data; } if (null !== $classMetadata && null !== ($serializedPath = ($attributesMetadata[$attribute] ?? null)?->getSerializedPath())) { $propertyAccessor = PropertyAccess::createPropertyAccessor(); if ($propertyAccessor->isReadable($data, $serializedPath) && null !== $propertyAccessor->getValue($data, $serializedPath)) { throw new LogicException(\sprintf('The element you are trying to set is already populated: "%s".', (string) $serializedPath)); } $propertyAccessor->setValue($data, $serializedPath, $attributeValue); return $data; } if ($this->nameConverter) { $attribute = $this->nameConverter->normalize($attribute, $class, $format, $context); } $data[$attribute] = $attributeValue; return $data; } /** * Is the max depth reached for the given attribute? * * @param AttributeMetadataInterface[] $attributesMetadata */ private function isMaxDepthReached(array $attributesMetadata, string $class, string $attribute, array &$context) : bool { if (!($enableMaxDepth = $context[self::ENABLE_MAX_DEPTH] ?? $this->defaultContext[self::ENABLE_MAX_DEPTH] ?? \false) || !isset($attributesMetadata[$attribute]) || null === ($maxDepth = $attributesMetadata[$attribute]?->getMaxDepth())) { return \false; } $key = \sprintf(self::DEPTH_KEY_PATTERN, $class, $attribute); if (!isset($context[$key])) { $context[$key] = 1; return \false; } if ($context[$key] === $maxDepth) { return \true; } ++$context[$key]; return \false; } /** * Overwritten to update the cache key for the child. * * We must not mix up the attribute cache between parent and children. * * @internal */ protected function createChildContext(array $parentContext, string $attribute, ?string $format) : array { $context = parent::createChildContext($parentContext, $attribute, $format); if ($context['cache_key'] ?? \false) { $context['cache_key'] .= '-' . $attribute; } elseif (\false !== ($context['cache_key'] ?? null)) { $context['cache_key'] = $this->getCacheKey($format, $context); } return $context; } /** * 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. */ private function getCacheKey(?string $format, array $context) : bool|string { foreach ($context[self::EXCLUDE_FROM_CACHE_KEY] ?? $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] as $key) { unset($context[$key]); } unset($context[self::EXCLUDE_FROM_CACHE_KEY]); unset($context[self::OBJECT_TO_POPULATE]); unset($context['cache_key']); // avoid artificially different keys try { return \hash('xxh128', $format . \serialize(['context' => $context, 'ignored' => $context[self::IGNORED_ATTRIBUTES] ?? $this->defaultContext[self::IGNORED_ATTRIBUTES]])); } catch (\Exception) { // The context cannot be serialized, skip the cache return \false; } } /** * 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. */ private function isUninitializedValueError(\Error|UninitializedPropertyException $e) : bool { return $e instanceof UninitializedPropertyException || \str_starts_with($e->getMessage(), 'Typed property') && \str_ends_with($e->getMessage(), 'must not be accessed before initialization'); } /** * Returns all attributes with a SerializedPath attribute and the respective path. */ private function getNestedAttributes(string $class) : array { if (!$this->classMetadataFactory?->hasMetadataFor($class)) { return []; } $properties = []; $serializedPaths = []; $classMetadata = $this->classMetadataFactory->getMetadataFor($class); foreach ($classMetadata->getAttributesMetadata() as $name => $metadata) { if (!($serializedPath = $metadata->getSerializedPath())) { continue; } $pathIdentifier = \implode(',', $serializedPath->getElements()); if (isset($serializedPaths[$pathIdentifier])) { throw new LogicException(\sprintf('Duplicate serialized path: "%s" used for properties "%s" and "%s".', $pathIdentifier, $serializedPaths[$pathIdentifier], $name)); } $serializedPaths[$pathIdentifier] = $name; $properties[$name] = $serializedPath; } return $properties; } private function removeNestedValue(array $path, array $data) : array { $element = \array_shift($path); if (!$path || !($data[$element] = $this->removeNestedValue($path, $data[$element]))) { unset($data[$element]); } return $data; } /** * @return class-string */ private function getMappedClass(array $data, string $class, array $context) : string { if (null !== ($object = $this->extractObjectToPopulate($class, $context, self::OBJECT_TO_POPULATE))) { return $object::class; } if (!($mapping = $this->classDiscriminatorResolver?->getMappingForClass($class))) { return $class; } if (null === ($type = $data[$mapping->getTypeProperty()] ?? null)) { throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'] . '.' . $mapping->getTypeProperty() : $mapping->getTypeProperty(), \false); } if (null === ($mappedClass = $mapping->getClassForType($type))) { throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'] . '.' . $mapping->getTypeProperty() : $mapping->getTypeProperty(), \true); } return $mappedClass; } }
Edit
Rename
Chmod
Delete
FILE
FOLDER
INFO
Name
Size
Permission
Action
AbstractNormalizer.php
25629 bytes
0644
AbstractObjectNormalizer.php
56447 bytes
0644
ArrayDenormalizer.php
4769 bytes
0644
BackedEnumNormalizer.php
3250 bytes
0644
ConstraintViolationListNormalizer.php
4139 bytes
0644
CustomNormalizer.php
2358 bytes
0644
DataUriNormalizer.php
5322 bytes
0644
DateIntervalNormalizer.php
4536 bytes
0644
DateTimeNormalizer.php
6224 bytes
0644
DateTimeZoneNormalizer.php
2391 bytes
0644
DenormalizableInterface.php
1656 bytes
0644
DenormalizerAwareInterface.php
561 bytes
0644
DenormalizerAwareTrait.php
600 bytes
0644
DenormalizerInterface.php
3564 bytes
0644
FormErrorNormalizer.php
2200 bytes
0644
GetSetMethodNormalizer.php
7690 bytes
0644
JsonSerializableNormalizer.php
2165 bytes
0644
MimeMessageNormalizer.php
4916 bytes
0644
NormalizableInterface.php
1493 bytes
0644
NormalizerAwareInterface.php
551 bytes
0644
NormalizerAwareTrait.php
584 bytes
0644
NormalizerInterface.php
2991 bytes
0644
ObjectNormalizer.php
9883 bytes
0644
ObjectToPopulateTrait.php
1091 bytes
0644
ProblemNormalizer.php
4597 bytes
0644
PropertyNormalizer.php
7934 bytes
0644
TranslatableNormalizer.php
2052 bytes
0644
UidNormalizer.php
3363 bytes
0644
UnwrappingDenormalizer.php
2356 bytes
0644
N4ST4R_ID | Naxtarrr