Completed
Push — master ( 091865...ba2679 )
by Antoine
16s
created

getFilterProperties()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 16
nc 7
nop 3
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
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\Core\Util;
15
16
use ApiPlatform\Core\Annotation\ApiFilter;
17
use Doctrine\Common\Annotations\Reader;
18
use Doctrine\Common\Inflector\Inflector;
19
20
/**
21
 * Generates a service id for a generic filter.
22
 *
23
 * @internal
24
 *
25
 * @author Antoine Bluchet <[email protected]>
26
 */
27
trait AnnotationFilterExtractorTrait
28
{
29
    /**
30
     * Filters annotations to get back only ApiFilter annotations.
31
     *
32
     * @param array $miscAnnotations class or property annotations
33
     *
34
     * @return array only ApiFilter annotations
35
     */
36
    private function getFilterAnnotations(array $miscAnnotations): \Iterator
37
    {
38
        foreach ($miscAnnotations as $miscAnnotation) {
39
            if (ApiFilter::class === get_class($miscAnnotation)) {
40
                yield $miscAnnotation;
41
            }
42
        }
43
    }
44
45
    /**
46
     * Given a filter annotation and reflection elements, find out the properties where the filter is applied.
47
     */
48
    private function getFilterProperties(ApiFilter $filterAnnotation, \ReflectionClass $reflectionClass, \ReflectionProperty $reflectionProperty = null): array
49
    {
50
        $properties = [];
51
52
        if ($filterAnnotation->properties) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filterAnnotation->properties of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
53
            foreach ($filterAnnotation->properties as $property => $strategy) {
54
                if (is_int($property)) {
55
                    $properties[$strategy] = null;
56
                } else {
57
                    $properties[$property] = $strategy;
58
                }
59
            }
60
61
            return $properties;
62
        }
63
64
        if (null !== $reflectionProperty) {
65
            $properties[$reflectionProperty->getName()] = $filterAnnotation->strategy ?: null;
66
67
            return $properties;
68
        }
69
70
        if ($filterAnnotation->strategy) {
71
            foreach ($reflectionClass->getProperties() as $reflectionProperty) {
72
                $properties[$reflectionProperty->getName()] = $filterAnnotation->strategy;
73
            }
74
        }
75
76
        return $properties;
77
    }
78
79
    /**
80
     * Reads filter annotations from a ReflectionClass.
81
     *
82
     * @return array Key is the filter id. It has two values, properties and the ApiFilter instance
83
     */
84
    private function readFilterAnnotations(\ReflectionClass $reflectionClass, Reader $reader): array
85
    {
86
        $filters = [];
87
88
        foreach ($this->getFilterAnnotations($reader->getClassAnnotations($reflectionClass)) as $filterAnnotation) {
89
            $filterClass = $filterAnnotation->filterClass;
90
            $id = $this->generateFilterId($reflectionClass, $filterClass);
91
92 View Code Duplication
            if (!isset($filters[$id])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
                $filters[$id] = [$filterAnnotation->arguments, $filterClass];
94
            }
95
96
            if ($properties = $this->getFilterProperties($filterAnnotation, $reflectionClass)) {
97
                $filters[$id][0]['properties'] = $properties;
98
            }
99
        }
100
101
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
102
            foreach ($this->getFilterAnnotations($reader->getPropertyAnnotations($reflectionProperty)) as $filterAnnotation) {
103
                $filterClass = $filterAnnotation->filterClass;
104
                $id = $this->generateFilterId($reflectionClass, $filterClass);
105
106 View Code Duplication
                if (!isset($filters[$id])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
                    $filters[$id] = [$filterAnnotation->arguments, $filterClass];
108
                }
109
110
                if ($properties = $this->getFilterProperties($filterAnnotation, $reflectionClass, $reflectionProperty)) {
111
                    if (isset($filters[$id][0]['properties'])) {
112
                        $filters[$id][0]['properties'] = array_merge($filters[$id][0]['properties'], $properties);
113
                    } else {
114
                        $filters[$id][0]['properties'] = $properties;
115
                    }
116
                }
117
            }
118
        }
119
120
        $parent = $reflectionClass->getParentClass();
121
122
        if (false !== $parent) {
123
            return array_merge($filters, $this->readFilterAnnotations($parent, $reader));
124
        }
125
126
        return $filters;
127
    }
128
129
    /**
130
     * Generates a unique, per-class and per-filter identifier prefixed by `annotated_`.
131
     *
132
     * @param \ReflectionClass $reflectionClass the reflection class of a Resource
133
     * @param string           $filterClass     the filter class
134
     *
135
     * @return string
136
     */
137
    private function generateFilterId(\ReflectionClass $reflectionClass, string $filterClass): string
138
    {
139
        return 'annotated_'.Inflector::tableize(str_replace('\\', '', $reflectionClass->getName().(new \ReflectionClass($filterClass))->getName()));
0 ignored issues
show
Bug introduced by
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
140
    }
141
}
142