Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7ff98ea

Browse files
committed
Add AbstractObjectNormalizer composition with PropertyValueNormalizer for code reusability
1 parentfb508d5 commit7ff98ea

File tree

4 files changed

+456
-221
lines changed

4 files changed

+456
-221
lines changed

‎UPGRADE-6.2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ Serializer
100100
* Deprecate calling`AttributeMetadata::setSerializedName()`,`ClassMetadata::setClassDiscriminatorMapping()` without arguments
101101
* Change the signature of`AttributeMetadataInterface::setSerializedName()` to`setSerializedName(?string)`
102102
* Change the signature of`ClassMetadataInterface::setClassDiscriminatorMapping()` to`setClassDiscriminatorMapping(?ClassDiscriminatorMapping)`
103+
* Add`PropertyValueNormalizer` to`AbstractObjectNormalizer` to enabled code reusability in custom`ObjectNormalizer`
103104

104105
Translation
105106
-----------

‎src/Symfony/Component/Serializer/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ CHANGELOG
1212
* Change the signature of`ClassMetadataInterface::setClassDiscriminatorMapping()` to`setClassDiscriminatorMapping(?ClassDiscriminatorMapping)`
1313
* Add option YamlEncoder::YAML_INDENTATION to YamlEncoder constructor options to configure additional indentation for each level of nesting. This allows configuring indentation in the service configuration.
1414
* Add`SerializedPath` annotation to flatten nested attributes
15+
* Add`PropertyValueNormalizer` to`AbstractObjectNormalizer` to enabled code reusability in custom`ObjectNormalizer`
1516

1617
6.1
1718
---

‎src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 17 additions & 221 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
106106
publicconstPRESERVE_EMPTY_OBJECTS ='preserve_empty_objects';
107107

108108
private$propertyTypeExtractor;
109-
private$typesCache = [];
110109
private$attributesCache = [];
111110

112111
private$objectClassResolver;
@@ -116,6 +115,8 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
116115
*/
117116
protected$classDiscriminatorResolver;
118117

118+
protectedPropertyValueNormalizer$propertyValueNormalizer;
119+
119120
publicfunction__construct(ClassMetadataFactoryInterface$classMetadataFactory =null,NameConverterInterface$nameConverter =null,PropertyTypeExtractorInterface$propertyTypeExtractor =null,ClassDiscriminatorResolverInterface$classDiscriminatorResolver =null,callable$objectClassResolver =null,array$defaultContext = [])
120121
{
121122
parent::__construct($classMetadataFactory,$nameConverter,$defaultContext);
@@ -133,6 +134,7 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory
133134
}
134135
$this->classDiscriminatorResolver =$classDiscriminatorResolver;
135136
$this->objectClassResolver =$objectClassResolver;
137+
$this->propertyValueNormalizer =newPropertyValueNormalizer($this->serializer,$this->propertyTypeExtractor,$this->classDiscriminatorResolver);
136138
}
137139

138140
/**
@@ -362,11 +364,11 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
362364
}
363365
}
364366

365-
$types =$this->getTypes($resolvedClass,$attribute);
367+
$types =$this->propertyValueNormalizer->getTypes($resolvedClass,$attribute);
366368

367369
if (null !==$types) {
368370
try {
369-
$value =$this->validateAndDenormalize($types,$resolvedClass,$attribute,$value,$format,$attributeContext);
371+
$value =$this->propertyValueNormalizer->validateAndDenormalize($types,$resolvedClass,$attribute,$value,$format,$attributeContext,$this->shouldDisableTypeEnforcement($context), [$this,'createPropertyValueChildContext']);
370372
}catch (NotNormalizableValueException$exception) {
371373
if (isset($context['not_normalizable_value_exceptions'])) {
372374
$context['not_normalizable_value_exceptions'][] =$exception;
@@ -410,240 +412,29 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
410412
*/
411413
abstractprotectedfunctionsetAttributeValue(object$object,string$attribute,mixed$value,string$format =null,array$context = []);
412414

413-
/**
414-
* Validates the submitted data and denormalizes it.
415-
*
416-
* @param Type[] $types
417-
*
418-
* @throws NotNormalizableValueException
419-
* @throws ExtraAttributesException
420-
* @throws MissingConstructorArgumentsException
421-
* @throws LogicException
422-
*/
423-
privatefunctionvalidateAndDenormalize(array$types,string$currentClass,string$attribute,mixed$data, ?string$format,array$context):mixed
424-
{
425-
$expectedTypes = [];
426-
$isUnionType =\count($types) >1;
427-
$extraAttributesException =null;
428-
$missingConstructorArgumentException =null;
429-
foreach ($typesas$type) {
430-
if (null ===$data &&$type->isNullable()) {
431-
returnnull;
432-
}
433-
434-
$collectionValueType =$type->isCollection() ?$type->getCollectionValueTypes()[0] ??null :null;
435-
436-
// Fix a collection that contains the only one element
437-
// This is special to xml format only
438-
if ('xml' ===$format &&null !==$collectionValueType && (!\is_array($data) || !\is_int(key($data)))) {
439-
$data = [$data];
440-
}
441-
442-
// This try-catch should cover all NotNormalizableValueException (and all return branches after the first
443-
// exception) so we could try denormalizing all types of an union type. If the target type is not an union
444-
// type, we will just re-throw the catched exception.
445-
// In the case of no denormalization succeeds with an union type, it will fall back to the default exception
446-
// with the acceptable types list.
447-
try {
448-
// In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine,
449-
// if a value is meant to be a string, float, int or a boolean value from the serialized representation.
450-
// That's why we have to transform the values, if one of these non-string basic datatypes is expected.
451-
if (\is_string($data) && (XmlEncoder::FORMAT ===$format || CsvEncoder::FORMAT ===$format)) {
452-
if ('' ===$data) {
453-
if (Type::BUILTIN_TYPE_ARRAY ===$builtinType =$type->getBuiltinType()) {
454-
return [];
455-
}
456-
457-
if ($type->isNullable() &&\in_array($builtinType, [Type::BUILTIN_TYPE_BOOL, Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT],true)) {
458-
returnnull;
459-
}
460-
}
461-
462-
switch ($builtinType ??$type->getBuiltinType()) {
463-
case Type::BUILTIN_TYPE_BOOL:
464-
// according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1"
465-
if ('false' ===$data ||'0' ===$data) {
466-
$data =false;
467-
}elseif ('true' ===$data ||'1' ===$data) {
468-
$data =true;
469-
}else {
470-
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the "%s" attribute for class "%s" must be bool ("%s" given).',$attribute,$currentClass,$data),$data, [Type::BUILTIN_TYPE_BOOL],$context['deserialization_path'] ??null);
471-
}
472-
break;
473-
case Type::BUILTIN_TYPE_INT:
474-
if (ctype_digit('-' ===$data[0] ?substr($data,1) :$data)) {
475-
$data = (int)$data;
476-
}else {
477-
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the "%s" attribute for class "%s" must be int ("%s" given).',$attribute,$currentClass,$data),$data, [Type::BUILTIN_TYPE_INT],$context['deserialization_path'] ??null);
478-
}
479-
break;
480-
case Type::BUILTIN_TYPE_FLOAT:
481-
if (is_numeric($data)) {
482-
return (float)$data;
483-
}
484-
485-
returnmatch ($data) {
486-
'NaN' => \NAN,
487-
'INF' => \INF,
488-
'-INF' => -\INF,
489-
default =>throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the "%s" attribute for class "%s" must be float ("%s" given).',$attribute,$currentClass,$data),$data, [Type::BUILTIN_TYPE_FLOAT],$context['deserialization_path'] ??null),
490-
};
491-
}
492-
}
493-
494-
if (null !==$collectionValueType && Type::BUILTIN_TYPE_OBJECT ===$collectionValueType->getBuiltinType()) {
495-
$builtinType = Type::BUILTIN_TYPE_OBJECT;
496-
$class =$collectionValueType->getClassName().'[]';
497-
498-
if (\count($collectionKeyType =$type->getCollectionKeyTypes()) >0) {
499-
[$context['key_type']] =$collectionKeyType;
500-
}
501-
502-
$context['value_type'] =$collectionValueType;
503-
}elseif ($type->isCollection() &&\count($collectionValueType =$type->getCollectionValueTypes()) >0 && Type::BUILTIN_TYPE_ARRAY ===$collectionValueType[0]->getBuiltinType()) {
504-
// get inner type for any nested array
505-
[$innerType] =$collectionValueType;
506-
507-
// note that it will break for any other builtinType
508-
$dimensions ='[]';
509-
while (\count($innerType->getCollectionValueTypes()) >0 && Type::BUILTIN_TYPE_ARRAY ===$innerType->getBuiltinType()) {
510-
$dimensions .='[]';
511-
[$innerType] =$innerType->getCollectionValueTypes();
512-
}
513-
514-
if (null !==$innerType->getClassName()) {
515-
// the builtinType is the inner one and the class is the class followed by []...[]
516-
$builtinType =$innerType->getBuiltinType();
517-
$class =$innerType->getClassName().$dimensions;
518-
}else {
519-
// default fallback (keep it as array)
520-
$builtinType =$type->getBuiltinType();
521-
$class =$type->getClassName();
522-
}
523-
}else {
524-
$builtinType =$type->getBuiltinType();
525-
$class =$type->getClassName();
526-
}
527-
528-
$expectedTypes[Type::BUILTIN_TYPE_OBJECT ===$builtinType &&$class ?$class :$builtinType] =true;
529-
530-
if (Type::BUILTIN_TYPE_OBJECT ===$builtinType) {
531-
if (!$this->serializerinstanceof DenormalizerInterface) {
532-
thrownewLogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer.',$attribute,$class));
533-
}
534-
535-
$childContext =$this->createChildContext($context,$attribute,$format);
536-
if ($this->serializer->supportsDenormalization($data,$class,$format,$childContext)) {
537-
return$this->serializer->denormalize($data,$class,$format,$childContext);
538-
}
539-
}
540-
541-
// JSON only has a Number type corresponding to both int and float PHP types.
542-
// PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert
543-
// floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible).
544-
// PHP's json_decode automatically converts Numbers without a decimal part to integers.
545-
// To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when
546-
// a float is expected.
547-
if (Type::BUILTIN_TYPE_FLOAT ===$builtinType &&\is_int($data) &&null !==$format &&str_contains($format, JsonEncoder::FORMAT)) {
548-
return (float)$data;
549-
}
550-
551-
if ((Type::BUILTIN_TYPE_FALSE ===$builtinType &&false ===$data) || (Type::BUILTIN_TYPE_TRUE ===$builtinType &&true ===$data)) {
552-
return$data;
553-
}
554-
555-
if (('is_'.$builtinType)($data)) {
556-
return$data;
557-
}
558-
}catch (NotNormalizableValueException$e) {
559-
if (!$isUnionType) {
560-
throw$e;
561-
}
562-
}catch (ExtraAttributesException$e) {
563-
if (!$isUnionType) {
564-
throw$e;
565-
}
566-
567-
$extraAttributesException ??=$e;
568-
}catch (MissingConstructorArgumentsException$e) {
569-
if (!$isUnionType) {
570-
throw$e;
571-
}
572-
573-
$missingConstructorArgumentException ??=$e;
574-
}
575-
}
576-
577-
if ($extraAttributesException) {
578-
throw$extraAttributesException;
579-
}
580-
581-
if ($missingConstructorArgumentException) {
582-
throw$missingConstructorArgumentException;
583-
}
584-
585-
if ($context[self::DISABLE_TYPE_ENFORCEMENT] ??$this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ??false) {
586-
return$data;
587-
}
588-
589-
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);
590-
}
591-
592415
/**
593416
* @internal
594417
*/
595418
protectedfunctiondenormalizeParameter(\ReflectionClass$class,\ReflectionParameter$parameter,string$parameterName,mixed$parameterData,array$context,string$format =null):mixed
596419
{
597-
if ($parameter->isVariadic() ||null ===$this->propertyTypeExtractor ||null ===$types =$this->getTypes($class->getName(),$parameterName)) {
420+
if ($parameter->isVariadic() ||null ===$this->propertyTypeExtractor ||null ===$types =$this->propertyValueNormalizer->getTypes($class->getName(),$parameterName)) {
598421
returnparent::denormalizeParameter($class,$parameter,$parameterName,$parameterData,$context,$format);
599422
}
600423

601-
$parameterData =$this->validateAndDenormalize($types,$class->getName(),$parameterName,$parameterData,$format,$context);
424+
$parameterData =$this->propertyValueNormalizer->validateAndDenormalize($types,$class->getName(),$parameterName,$parameterData,$format,$context,$this->shouldDisableTypeEnforcement($context), [$this,'createPropertyValueChildContext']);
602425

603426
return$this->applyCallbacks($parameterData,$class->getName(),$parameterName,$format,$context);
604427
}
605428

606-
/**
607-
* @return Type[]|null
608-
*/
609-
privatefunctiongetTypes(string$currentClass,string$attribute): ?array
429+
finalprotectedfunctionshouldDisableTypeEnforcement():bool
610430
{
611-
if (null ===$this->propertyTypeExtractor) {
612-
returnnull;
613-
}
614-
615-
$key =$currentClass.'::'.$attribute;
616-
if (isset($this->typesCache[$key])) {
617-
returnfalse ===$this->typesCache[$key] ?null :$this->typesCache[$key];
618-
}
619-
620-
if (null !==$types =$this->propertyTypeExtractor->getTypes($currentClass,$attribute)) {
621-
return$this->typesCache[$key] =$types;
622-
}
623-
624-
if (null !==$this->classDiscriminatorResolver &&null !==$discriminatorMapping =$this->classDiscriminatorResolver->getMappingForClass($currentClass)) {
625-
if ($discriminatorMapping->getTypeProperty() ===$attribute) {
626-
return$this->typesCache[$key] = [
627-
newType(Type::BUILTIN_TYPE_STRING),
628-
];
629-
}
630-
631-
foreach ($discriminatorMapping->getTypesMapping()as$mappedClass) {
632-
if (null !==$types =$this->propertyTypeExtractor->getTypes($mappedClass,$attribute)) {
633-
return$this->typesCache[$key] =$types;
634-
}
635-
}
636-
}
637-
638-
$this->typesCache[$key] =false;
639-
640-
returnnull;
431+
return$context[self::DISABLE_TYPE_ENFORCEMENT] ??$this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ??false;
641432
}
642433

643434
/**
644435
* Sets an attribute and apply the name converter if necessary.
645436
*/
646-
privatefunctionupdateData(array$data,string$attribute,mixed$attributeValue,string$class, ?string$format,array$context, ?array$attributesMetadata, ?ClassMetadataInterface$classMetadata):array
437+
finalprotectedfunctionupdateData(array$data,string$attribute,mixed$attributeValue,string$class, ?string$format,array$context, ?array$attributesMetadata, ?ClassMetadataInterface$classMetadata):array
647438
{
648439
if (null ===$attributeValue && ($context[self::SKIP_NULL_VALUES] ??$this->defaultContext[self::SKIP_NULL_VALUES] ??false)) {
649440
return$data;
@@ -700,6 +491,11 @@ private function isMaxDepthReached(array $attributesMetadata, string $class, str
700491
returnfalse;
701492
}
702493

494+
finalpublicfunctioncreatePropertyValueChildContext(array$parentContext,string$attribute, ?string$format):array
495+
{
496+
return$this->createChildContext($parentContext,$attribute,$format);
497+
}
498+
703499
/**
704500
* Overwritten to update the cache key for the child.
705501
*
@@ -720,7 +516,7 @@ protected function createChildContext(array $parentContext, string $attribute, ?
720516
*
721517
* The key must be different for every option in the context that could change which attributes should be handled.
722518
*/
723-
privatefunctiongetCacheKey(?string$format,array$context):bool|string
519+
finalprotectedfunctiongetCacheKey(?string$format,array$context):bool|string
724520
{
725521
foreach ($context[self::EXCLUDE_FROM_CACHE_KEY] ??$this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY]as$key) {
726522
unset($context[$key]);
@@ -744,7 +540,7 @@ private function getCacheKey(?string $format, array $context): bool|string
744540
* This error may occur when specific object normalizer implementation gets attribute value
745541
* by accessing a public uninitialized property or by calling a method accessing such property.
746542
*/
747-
privatefunctionisUninitializedValueError(\Error$e):bool
543+
finalprotectedfunctionisUninitializedValueError(\Error$e):bool
748544
{
749545
returnstr_starts_with($e->getMessage(),'Typed property')
750546
&&str_ends_with($e->getMessage(),'must not be accessed before initialization');

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp