Test Setup Failed
Push — main ( c2f1dd...c584c3 )
by Fractal
11:13
created

ConstraintExtractor   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 57
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 25
c 1
b 0
f 0
dl 0
loc 57
rs 10
ccs 11
cts 11
cp 1
wmc 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getPropertyMetadataFor() 0 3 2
A __construct() 0 3 1
A getClassMetadataFor() 0 3 1
A extract() 0 6 2
A getPropertyReflectionMember() 0 3 1
A extractConstraints() 0 23 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FRZB\Component\RequestMapper\Extractor;
6
7
use Fp\Collections\ArrayList;
8
use FRZB\Component\DependencyInjection\Attribute\AsService;
9
use FRZB\Component\RequestMapper\Helper\ClassHelper;
10
use FRZB\Component\RequestMapper\Helper\ConstraintsHelper;
11
use FRZB\Component\RequestMapper\Helper\SerializerHelper;
12
use Symfony\Component\Validator\Constraints\All;
13
use Symfony\Component\Validator\Constraints\Collection;
14
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
15
use Symfony\Component\Validator\Mapping\ClassMetadata as ClassMetadataImpl;
16 15
use Symfony\Component\Validator\Mapping\ClassMetadataInterface as ClassMetadata;
17
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface as MetadataFactory;
18
use Symfony\Component\Validator\Mapping\PropertyMetadata as PropertyMetadataImpl;
19 15
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface as PropertyMetadata;
20 1
use Symfony\Component\Validator\Validator\ValidatorInterface as Validator;
21 1
22
#[AsService(arguments: ['$metadataFactory' => Validator::class])]
23
class ConstraintExtractor
24
{
25
    public function __construct(
26 15
        private readonly MetadataFactory $metadataFactory,
27
    ) {
28 15
    }
29
30 15
    public function extract(string $class, array $parameters = []): ?Collection
31 1
    {
32
        try {
33
            return ConstraintsHelper::createCollection($this->extractConstraints($class, $parameters));
34 15
        } catch (NoSuchMetadataException|\ReflectionException) {
35 15
            return null;
36 15
        }
37 15
    }
38 1
39 15
    /** @throws NoSuchMetadataException|\ReflectionException */
40
    public function extractConstraints(string $class, array $parameters = []): array
41
    {
42 15
        $constraints = [];
43
        $classMetadata = $this->getClassMetadataFor($class);
44
        $reflectionClass = new \ReflectionClass($class);
45
46
        foreach ($reflectionClass->getProperties() as $property) {
47
            $propertyName = $property->getName();
48
            $propertyMetadata = $this->getPropertyMetadataFor($classMetadata, $propertyName);
49
            $propertyReflectionMember = $this->getPropertyReflectionMember($class, $propertyMetadata);
50
            $propertySerializedName = SerializerHelper::getSerializedNameAttribute($propertyReflectionMember)?->getSerializedName();
0 ignored issues
show
Bug introduced by
It seems like $propertyReflectionMember can also be of type ReflectionMethod; however, parameter $rProperty of FRZB\Component\RequestMa...rializedNameAttribute() does only seem to accept ReflectionProperty, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

50
            $propertySerializedName = SerializerHelper::getSerializedNameAttribute(/** @scrutinizer ignore-type */ $propertyReflectionMember)?->getSerializedName();
Loading history...
51
            $propertyTypeName = $propertyReflectionMember->getType()?->getName();
0 ignored issues
show
Bug introduced by
The method getType() does not exist on ReflectionMethod. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

51
            $propertyTypeName = $propertyReflectionMember->/** @scrutinizer ignore-call */ getType()?->getName();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

51
            $propertyTypeName = $propertyReflectionMember->getType()?->/** @scrutinizer ignore-call */ getName();
Loading history...
52
            $propertyValue = $parameters[$propertySerializedName] ?? $parameters[$propertyName] ?? [];
53
            $arrayTypeName = ConstraintsHelper::getArrayTypeAttribute($propertyReflectionMember)?->typeName;
0 ignored issues
show
Bug introduced by
It seems like $propertyReflectionMember can also be of type ReflectionMethod; however, parameter $rProperty of FRZB\Component\RequestMa...getArrayTypeAttribute() does only seem to accept ReflectionProperty, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
            $arrayTypeName = ConstraintsHelper::getArrayTypeAttribute(/** @scrutinizer ignore-type */ $propertyReflectionMember)?->typeName;
Loading history...
54
55
            $constraints[$propertySerializedName] = match (true) {
56
                ConstraintsHelper::hasArrayTypeAttribute($propertyReflectionMember) => ArrayList::collect($propertyValue)->map(fn () => new All($this->extract($arrayTypeName, $propertyValue)))->toArray(),
0 ignored issues
show
Bug introduced by
It seems like $propertyReflectionMember can also be of type ReflectionMethod; however, parameter $rProperty of FRZB\Component\RequestMa...hasArrayTypeAttribute() does only seem to accept ReflectionProperty, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

56
                ConstraintsHelper::hasArrayTypeAttribute(/** @scrutinizer ignore-type */ $propertyReflectionMember) => ArrayList::collect($propertyValue)->map(fn () => new All($this->extract($arrayTypeName, $propertyValue)))->toArray(),
Loading history...
57
                ClassHelper::isNotBuiltinAndExists($propertyTypeName) => $this->extract($propertyTypeName),
58
                default => $propertyMetadata->getConstraints(),
59
            };
60
        }
61
62
        return $constraints;
63
    }
64
65
    /** @noinspection PhpIncompatibleReturnTypeInspection */
66
    private function getClassMetadataFor(string $class): ClassMetadata
67
    {
68
        return $this->metadataFactory->getMetadataFor($class) ?? new ClassMetadataImpl($class);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->metadataFa...g\ClassMetadata($class) could return the type Symfony\Component\Valida...pping\MetadataInterface which includes types incompatible with the type-hinted return Symfony\Component\Valida...\ClassMetadataInterface. Consider adding an additional type-check to rule them out.
Loading history...
69
    }
70
71
    private function getPropertyMetadataFor(ClassMetadata $classMetadata, string $propertyName): PropertyMetadata
72
    {
73
        return current($classMetadata->getPropertyMetadata($propertyName)) ?: new PropertyMetadataImpl($classMetadata->getClassName(), $propertyName);
74
    }
75
76
    private function getPropertyReflectionMember(string $class, PropertyMetadata $propertyMetadata): \ReflectionMethod|\ReflectionProperty
77
    {
78
        return $propertyMetadata->getReflectionMember($class);
79
    }
80
}
81