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

api-platform / core / 14635100171

24 Apr 2025 06:39AM UTC coverage: 8.271% (+0.02%) from 8.252%
14635100171

Pull #6904

github

web-flow
Merge c9cefd82e into a3e5e53ea
Pull Request #6904: feat(graphql): added support for graphql subscriptions to work for actions

0 of 73 new or added lines in 3 files covered. (0.0%)

1999 existing lines in 144 files now uncovered.

13129 of 158728 relevant lines covered (8.27%)

13.6 hits per line

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

70.0
/src/Metadata/Property/Factory/ExtractorPropertyMetadataFactory.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\Metadata\Property\Factory;
15

16
use ApiPlatform\JsonSchema\Metadata\Property\Factory\SchemaPropertyMetadataFactory;
17
use ApiPlatform\Metadata\ApiProperty;
18
use ApiPlatform\Metadata\Exception\PropertyNotFoundException;
19
use ApiPlatform\Metadata\Exception\RuntimeException;
20
use ApiPlatform\Metadata\Extractor\PropertyExtractorInterface;
21
use PHPStan\PhpDocParser\Parser\PhpDocParser;
22
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
23
use Symfony\Component\PropertyInfo\Type as LegacyType;
24
use Symfony\Component\TypeInfo\Type;
25
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
26

27
/**
28
 * Creates properties's metadata using an extractor.
29
 *
30
 * @author Kévin Dunglas <dunglas@gmail.com>
31
 * @author Vincent Chalamon <vincentchalamon@gmail.com>
32
 */
33
final class ExtractorPropertyMetadataFactory implements PropertyMetadataFactoryInterface
34
{
35
    public function __construct(private readonly PropertyExtractorInterface $extractor, private readonly ?PropertyMetadataFactoryInterface $decorated = null)
36
    {
37
    }
1,216✔
38

39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function create(string $resourceClass, string $property, array $options = []): ApiProperty
43
    {
44
        $parentPropertyMetadata = null;
304✔
45
        if ($this->decorated) {
304✔
46
            try {
47
                $parentPropertyMetadata = $this->decorated->create($resourceClass, $property, $options);
304✔
48
            } catch (PropertyNotFoundException) {
×
49
                // Ignore not found exception from decorated factories
50
            }
51
        }
52

53
        if (
54
            !property_exists($resourceClass, $property) && !interface_exists($resourceClass)
304✔
55
            || null === ($propertyMetadata = $this->extractor->getProperties()[$resourceClass][$property] ?? null)
304✔
56
        ) {
57
            return $this->handleNotFound($parentPropertyMetadata, $resourceClass, $property);
304✔
58
        }
59

60
        if ($parentPropertyMetadata) {
11✔
61
            return $this->handleUserDefinedSchema($this->update($parentPropertyMetadata, $propertyMetadata));
10✔
62
        }
63

UNCOV
64
        $apiProperty = new ApiProperty();
4✔
65

UNCOV
66
        foreach ($propertyMetadata as $key => $value) {
4✔
UNCOV
67
            if ('builtinTypes' === $key && null !== $value) {
4✔
UNCOV
68
                if (method_exists(PropertyInfoExtractor::class, 'getType')) {
4✔
UNCOV
69
                    continue;
4✔
70
                }
71

72
                $apiProperty = $apiProperty->withBuiltinTypes(array_map(static fn (string $builtinType): LegacyType => new LegacyType($builtinType), $value));
×
73

74
                continue;
×
75
            }
76

UNCOV
77
            if ('nativeType' === $key && null !== $value) {
4✔
78
                if (class_exists(PhpDocParser::class)) {
×
79
                    $apiProperty = $apiProperty->withNativeType((new StringTypeResolver())->resolve($value));
×
80

81
                    continue;
×
82
                }
83

84
                try {
85
                    $apiProperty = $apiProperty->withNativeType(Type::builtin($value));
×
86
                } catch (\ValueError) {
×
87
                    throw new RuntimeException(\sprintf('Cannot create a type from "%s". Try running "composer require phpstan/phpdoc-parser" to support all types.', $value));
×
88
                }
89

90
                continue;
×
91
            }
92

UNCOV
93
            $methodName = 'with'.ucfirst($key);
4✔
94

UNCOV
95
            if (method_exists($apiProperty, $methodName) && null !== $value) {
4✔
UNCOV
96
                $apiProperty = $apiProperty->{$methodName}($value);
4✔
97
            }
98
        }
99

UNCOV
100
        return $this->handleUserDefinedSchema($apiProperty);
4✔
101
    }
102

103
    /**
104
     * Returns the metadata from the decorated factory if available or throws an exception.
105
     *
106
     * @throws PropertyNotFoundException
107
     */
108
    private function handleNotFound(?ApiProperty $parentPropertyMetadata, string $resourceClass, string $property): ApiProperty
109
    {
110
        if ($parentPropertyMetadata) {
304✔
111
            return $parentPropertyMetadata;
304✔
112
        }
113

114
        throw new PropertyNotFoundException(\sprintf('Property "%s" of the resource class "%s" not found.', $property, $resourceClass));
303✔
115
    }
116

117
    /**
118
     * Creates a new instance of metadata if the property is not already set.
119
     */
120
    private function update(ApiProperty $propertyMetadata, array $metadata): ApiProperty
121
    {
122
        foreach (get_class_methods(ApiProperty::class) as $method) {
10✔
123
            if (preg_match('/^(?:get|is)(.*)/', (string) $method, $matches) && null !== ($val = $metadata[lcfirst($matches[1])] ?? null) && method_exists($propertyMetadata, "with{$matches[1]}")) {
10✔
124
                $propertyMetadata = $propertyMetadata->{"with{$matches[1]}"}($val);
10✔
125
            }
126
        }
127

128
        return $propertyMetadata;
10✔
129
    }
130

131
    private function handleUserDefinedSchema(ApiProperty $propertyMetadata): ApiProperty
132
    {
133
        // can't know later if the schema has been defined by the user or by API Platform
134
        // store extra key to make this difference
135
        if (null !== $propertyMetadata->getSchema()) {
11✔
136
            $extraProperties = $propertyMetadata->getExtraProperties() ?? [];
×
137
            $propertyMetadata = $propertyMetadata->withExtraProperties([SchemaPropertyMetadataFactory::JSON_SCHEMA_USER_DEFINED => true] + $extraProperties);
×
138
        }
139

140
        return $propertyMetadata;
11✔
141
    }
142
}
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

© 2025 Coveralls, Inc