Completed
Pull Request — master (#1)
by Karsten
03:23
created

getPropertyMarkersRecursive()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 31
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 31
c 0
b 0
f 0
ccs 13
cts 13
cp 1
rs 8.439
cc 6
eloc 13
nc 5
nop 1
crap 6
1
<?php
2
/**
3
 * File was created 06.10.2015 06:22
4
 */
5
6
namespace PeekAndPoke\Component\Slumber\Core\LookUp;
7
8
use Doctrine\Common\Annotations\Reader;
9
use PeekAndPoke\Component\Creator\Creator;
10
use PeekAndPoke\Component\Creator\CreatorFactory;
11
use PeekAndPoke\Component\Creator\CreatorFactoryImpl;
12
use PeekAndPoke\Component\PropertyAccess\PropertyAccessFactory;
13
use PeekAndPoke\Component\PropertyAccess\PropertyAccessFactoryImpl;
14
use PeekAndPoke\Component\Psi\Functions\Unary\Matcher\IsInstanceOf;
15
use PeekAndPoke\Component\Psi\Psi;
16
use PeekAndPoke\Component\Slumber\Annotation\ClassCreatorMarker;
17
use PeekAndPoke\Component\Slumber\Annotation\ClassMarker;
18
use PeekAndPoke\Component\Slumber\Annotation\PropertyMappingMarker;
19
use PeekAndPoke\Component\Slumber\Annotation\PropertyMarker;
20
use PeekAndPoke\Component\Slumber\Core\Exception\SlumberException;
21
use PeekAndPoke\Component\Slumber\Core\Validation\ClassAnnotationValidationContext;
22
use PeekAndPoke\Component\Slumber\Core\Validation\PropertyAnnotationValidationContext;
23
use Psr\Container\ContainerInterface;
24
25
/**
26
 * @author Karsten J. Gerber <[email protected]>
27
 */
28
class AnnotatedEntityConfigReader implements EntityConfigReader
29
{
30
    /** @var Reader */
31
    private $annotationReader;
32
    /** @var ContainerInterface */
33
    private $serviceProvider;
34
    /** @var PropertyMarker2Mapper */
35
    private $mappings;
36
    /** @var CreatorFactory */
37
    private $creatorFactory;
38
    /** @var PropertyAccessFactory */
39
    private $propertyAccessFactory;
40
41
    /**
42
     * @param ContainerInterface    $serviceProvider
43
     * @param Reader                $annotationReader
44
     * @param PropertyMarker2Mapper $mappings
45
     */
46 271
    public function __construct(ContainerInterface $serviceProvider, Reader $annotationReader, PropertyMarker2Mapper $mappings)
47
    {
48 271
        $this->annotationReader = $annotationReader;
49 271
        $this->serviceProvider  = $serviceProvider;
50 271
        $this->mappings         = $mappings;
51
52 271
        $this->creatorFactory        = new CreatorFactoryImpl();
53 271
        $this->propertyAccessFactory = new PropertyAccessFactoryImpl();
54 271
    }
55
56
    /**
57
     * @param \ReflectionClass $subject
58
     *
59
     * @return EntityConfig
60
     * @throws SlumberException
61
     */
62 6
    public function getEntityConfig(\ReflectionClass $subject)
63
    {
64 6
        $config = new EntityConfig(
65 6
            $subject->name,
66 6
            $this->getCreator($subject),
67 6
            $this->getClassMarkers($subject),
68 6
            $this->getPropertyMarkersRecursive($subject)
69
        );
70
71 6
        return $config;
72
    }
73
74
    /**
75
     * @param PropertyMarkedForSlumber $marked
76
     *
77
     * @return PropertyMarkedForSlumber
78
     */
79 6
    protected function enrich(PropertyMarkedForSlumber $marked)
80
    {
81 6
        $marked->mapper = $this->mappings->createMapper($marked->marker);
82
83 6
        return $marked;
84
    }
85
86
    /**
87
     * @param \ReflectionClass $subject
88
     *
89
     * @return Creator
90
     */
91 6
    private function getCreator(\ReflectionClass $subject)
92
    {
93 6
        $validationContext = new ClassAnnotationValidationContext($this->serviceProvider, $subject);
94
95 6
        $creatorAnnotation = Psi::it($this->annotationReader->getClassAnnotations($subject))
96 6
            ->filter(new IsInstanceOf(ClassCreatorMarker::class))
97 6
            ->each($validationContext)
98 6
            ->getFirst();
99
100 6
        if ($creatorAnnotation instanceof ClassCreatorMarker) {
101 1
            return $creatorAnnotation->getCreator($this->creatorFactory);
102
        }
103
104 6
        return $this->creatorFactory->create($subject);
105
    }
106
107
    /**
108
     * @param \ReflectionClass $subject
109
     *
110
     * @return ClassMarker[]
111
     */
112 6
    private function getClassMarkers(\ReflectionClass $subject)
113
    {
114 6
        $validationContext = new ClassAnnotationValidationContext($this->serviceProvider, $subject);
115
116 6
        return Psi::it($this->annotationReader->getClassAnnotations($subject))
117 6
            ->filter(new IsInstanceOf(ClassMarker::class))
118 6
            ->each($validationContext)
119 6
            ->toArray();
120
    }
121
122
    /**
123
     * @param \ReflectionClass $subjectClass
124
     *
125
     * @return PropertyMarkedForSlumber[]
126
     */
127 6
    private function getPropertyMarkersRecursive(\ReflectionClass $subjectClass)
128
    {
129
        /** @var PropertyMarkedForSlumber[] $result */
130 6
        $result = [];
131
132
        // We climb up the inheritance ladder to also capture private properties that are not shadowed
133
        // by other properties on derived classes.
134 6
        while ($subjectClass instanceof \ReflectionClass && $subjectClass->isUserDefined()) {
135
136 6
            $properties = $subjectClass->getProperties();
137
138 6
            foreach ($properties as $property) {
139
140 6
                $propertyName = $property->getName();
141
142 6
                if (! isset($result[$propertyName])) {
143
144 6
                    $context = $this->getPropertyValidationContext($subjectClass, $property);
145 6
                    $marker  = $this->getPropertyAnnotationsOfType($context);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marker is correct as $this->getPropertyAnnotationsOfType($context) (which targets PeekAndPoke\Component\Sl...ertyAnnotationsOfType()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
146
147 6
                    if ($marker) {
148 6
                        $result[$propertyName] = $this->enrich($marker);
149
                    }
150
                }
151
            }
152
153 6
            $subjectClass = $subjectClass->getParentClass();
154
        }
155
156 6
        return array_values($result);
157
    }
158
159
    /**
160
     * @param \ReflectionClass    $cls
161
     * @param \ReflectionProperty $property
162
     *
163
     * @return PropertyAnnotationValidationContext
164
     */
165 6
    private function getPropertyValidationContext(\ReflectionClass $cls, \ReflectionProperty $property)
166
    {
167 6
        return new PropertyAnnotationValidationContext($this->serviceProvider, $cls, $property);
168
    }
169
170
    /**
171
     * @param PropertyAnnotationValidationContext $context
172
     *
173
     * @return PropertyMarkedForSlumber
174
     */
175 6
    private function getPropertyAnnotationsOfType(PropertyAnnotationValidationContext $context)
176
    {
177 6
        $annotations = $this->annotationReader->getPropertyAnnotations($context->property);
178
179
        // get the mapping marker like AsString() or AsObject() ...
180 6
        $marker = Psi::it($annotations)
181 6
            ->filter(new IsInstanceOf(PropertyMappingMarker::class))
182 6
            ->each($context)
183 6
            ->getFirst();
184
185 6
        if ($marker === null) {
186
            return null;
187
        }
188
189 6
        $newEntry = new PropertyMarkedForSlumber();
190
191 6
        $newEntry->name           = $context->property->getName();
192 6
        $newEntry->alias          = $marker->hasAlias() ? $marker->getAlias() : $context->property->getName();
193 6
        $newEntry->marker         = $marker;
194 6
        $newEntry->allMarkers     = Psi::it($annotations)
195 6
            ->filter(new IsInstanceOf(PropertyMarker::class))
196 6
            ->each($context)
197 6
            ->toArray();
198 6
        $newEntry->mapper         = $this->mappings->createMapper($marker);
199 6
        $newEntry->propertyAccess = $this->propertyAccessFactory->create($context->cls, $context->property);
200
201 6
        return $newEntry;
202
    }
203
}
204