• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 20543155423

27 Dec 2025 07:05PM UTC coverage: 0.0%. First build
20543155423

Pull #7623

github

web-flow
Merge 8a1641ef0 into 38d474d1b
Pull Request #7623: ci: remove usage of soyuka/phpunit

0 of 75 new or added lines in 4 files covered. (0.0%)

0 of 57456 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Serializer/Mapping/Loader/PropertyMetadataLoader.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Serializer\Mapping\Loader;
15

16
use ApiPlatform\Metadata\ApiProperty;
17
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
18
use Illuminate\Database\Eloquent\Model;
19
use Symfony\Component\Serializer\Attribute\Context;
20
use Symfony\Component\Serializer\Attribute\DiscriminatorMap;
21
use Symfony\Component\Serializer\Attribute\Groups;
22
use Symfony\Component\Serializer\Attribute\Ignore;
23
use Symfony\Component\Serializer\Attribute\MaxDepth;
24
use Symfony\Component\Serializer\Attribute\SerializedName;
25
use Symfony\Component\Serializer\Attribute\SerializedPath;
26
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
27
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
28
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
29
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
30
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
31
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
32

33
/**
34
 * Loader for PHP attributes using ApiProperty.
35
 */
36
final class PropertyMetadataLoader implements LoaderInterface
37
{
38
    public function __construct(
39
        private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory,
40
        private readonly ?NameConverterInterface $nameConverter = null,
41
    ) {
42
    }
×
43

44
    public function loadClassMetadata(ClassMetadataInterface $classMetadata): bool
45
    {
46
        // It's very weird to grab Eloquent's properties in that case as they're never serialized
47
        // the Serializer makes a call on the abstract class, let's save some unneeded work with a condition
48
        if (Model::class === $classMetadata->getName()) {
×
49
            return false;
×
50
        }
51

52
        $refl = $classMetadata->getReflectionClass();
×
53
        $attributes = [];
×
54
        $classGroups = [];
×
55
        $classContextAnnotation = null;
×
56

57
        foreach ($refl->getAttributes(ApiProperty::class) as $clAttr) {
×
58
            $this->addAttributeMetadata($clAttr->newInstance(), $attributes);
×
59
        }
60

61
        $attributesMetadata = $classMetadata->getAttributesMetadata();
×
62

63
        foreach ($refl->getAttributes() as $a) {
×
64
            // Skip attributes whose classes don't exist (e.g., optional dependencies like MongoDB ODM)
NEW
65
            if (!class_exists($a->getName()) && !interface_exists($a->getName())) {
×
NEW
66
                continue;
×
67
            }
68

69
            $attribute = $a->newInstance();
×
70

71
            if ($attribute instanceof DiscriminatorMap) {
×
72
                $classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping(
×
73
                    method_exists($attribute, 'getTypeProperty') ? $attribute->getTypeProperty() : $attribute->typeProperty,
×
74
                    method_exists($attribute, 'getMapping') ? $attribute->getMapping() : $attribute->mapping
×
75
                ));
×
76
                continue;
×
77
            }
78

79
            if ($attribute instanceof Groups) {
×
80
                $classGroups = method_exists($attribute, 'getGroups') ? $attribute->getGroups() : $attribute->groups;
×
81

82
                continue;
×
83
            }
84

85
            if ($attribute instanceof Context) {
×
86
                $classContextAnnotation = $attribute;
×
87
            }
88
        }
89

90
        foreach ($refl->getProperties() as $reflProperty) {
×
91
            foreach ($reflProperty->getAttributes(ApiProperty::class) as $propAttr) {
×
92
                $this->addAttributeMetadata($propAttr->newInstance()->withProperty($reflProperty->name), $attributes);
×
93
            }
94
        }
95

96
        foreach ($refl->getMethods() as $reflMethod) {
×
97
            foreach ($reflMethod->getAttributes(ApiProperty::class) as $methodAttr) {
×
98
                $this->addAttributeMetadata($methodAttr->newInstance()->withProperty($reflMethod->getName()), $attributes);
×
99
            }
100
        }
101

102
        foreach ($this->propertyNameCollectionFactory->create($classMetadata->getName()) as $propertyName) {
×
103
            if (!isset($attributesMetadata[$propertyName])) {
×
104
                $attributesMetadata[$propertyName] = new AttributeMetadata($propertyName);
×
105
                $classMetadata->addAttributeMetadata($attributesMetadata[$propertyName]);
×
106
            }
107

108
            foreach ($classGroups as $group) {
×
109
                $attributesMetadata[$propertyName]->addGroup($group);
×
110
            }
111

112
            if ($classContextAnnotation) {
×
113
                $this->setAttributeContextsForGroups($classContextAnnotation, $attributesMetadata[$propertyName]);
×
114
            }
115

116
            if (!isset($attributes[$propertyName])) {
×
117
                continue;
×
118
            }
119

120
            $attributeMetadata = $attributesMetadata[$propertyName];
×
121

122
            // This code is adapted from Symfony\Component\Serializer\Mapping\Loader\AttributeLoader
123
            foreach ($attributes[$propertyName] as $attr) {
×
124
                if ($attr instanceof Groups) {
×
125
                    $groups = method_exists($attr, 'getGroups') ? $attr->getGroups() : $attr->groups;
×
126
                    foreach ($groups as $group) {
×
127
                        $attributeMetadata->addGroup($group);
×
128
                    }
129
                    continue;
×
130
                }
131

132
                match (true) {
×
133
                    $attr instanceof MaxDepth => $attributeMetadata->setMaxDepth(method_exists($attr, 'getMaxDepth') ? $attr->getMaxDepth() : $attr->maxDepth),
×
134
                    $attr instanceof SerializedName => $attributeMetadata->setSerializedName(method_exists($attr, 'getSerializedName') ? $attr->getSerializedName() : $attr->serializedName),
×
135
                    $attr instanceof SerializedPath => $attributeMetadata->setSerializedPath(method_exists($attr, 'getSerializedPath') ? $attr->getSerializedPath() : $attr->serializedPath),
×
136
                    $attr instanceof Ignore => $attributeMetadata->setIgnore(true),
×
137
                    $attr instanceof Context => $this->setAttributeContextsForGroups($attr, $attributeMetadata),
×
138
                    default => null,
×
139
                };
×
140
            }
141
        }
142

143
        return true;
×
144
    }
145

146
    /**
147
     * @param array<string, array<mixed>> $attributes
148
     */
149
    private function addAttributeMetadata(ApiProperty $attribute, array &$attributes): void
150
    {
151
        if (($prop = $this->nameConverter?->denormalize($attribute->getProperty()) ?? $attribute->getProperty()) && ($value = $attribute->getSerialize())) {
×
152
            $attributes[$prop] = $value;
×
153
        }
154
    }
155

156
    private function setAttributeContextsForGroups(Context $annotation, AttributeMetadataInterface $attributeMetadata): void
157
    {
158
        $context = method_exists($annotation, 'getContext') ? $annotation->getContext() : $annotation->context;
×
159
        $groups = method_exists($annotation, 'getGroups') ? $annotation->getGroups() : $annotation->groups;
×
160
        $normalizationContext = method_exists($annotation, 'getNormalizationContext') ? $annotation->getNormalizationContext() : $annotation->normalizationContext;
×
161
        $denormalizationContext = method_exists($annotation, 'getDenormalizationContext') ? $annotation->getDenormalizationContext() : $annotation->denormalizationContext;
×
162

163
        if ($normalizationContext || $context) {
×
164
            $attributeMetadata->setNormalizationContextForGroups($normalizationContext ?: $context, $groups);
×
165
        }
166

167
        if ($denormalizationContext || $context) {
×
168
            $attributeMetadata->setDenormalizationContextForGroups($denormalizationContext ?: $context, $groups);
×
169
        }
170
    }
171
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc