Completed
Push — master ( 5ed87e...7a34bc )
by Karsten
02:48
created

getPropertyMarkersRecursive()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 2
nop 1
crap 3
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\AnnotationRegistry;
9
use Doctrine\Common\Annotations\Reader;
10
use PeekAndPoke\Component\Creator\Creator;
11
use PeekAndPoke\Component\Creator\CreatorFactory;
12
use PeekAndPoke\Component\Creator\CreatorFactoryImpl;
13
use PeekAndPoke\Component\PropertyAccess\PropertyAccessFactory;
14
use PeekAndPoke\Component\PropertyAccess\PropertyAccessFactoryImpl;
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 336
    public function __construct(ContainerInterface $serviceProvider, Reader $annotationReader, PropertyMarker2Mapper $mappings)
47
    {
48 336
        static $autoloader = false;
49
50 336
        if ($autoloader === false) {
51 1
            $autoloader = true;
52 1
            AnnotationRegistry::registerLoader('class_exists');
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\Common\Annotati...istry::registerLoader() has been deprecated with message: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists')

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
53
        }
54
55 336
        $this->annotationReader = $annotationReader;
56 336
        $this->serviceProvider  = $serviceProvider;
57 336
        $this->mappings         = $mappings;
58
59 336
        $this->creatorFactory        = new CreatorFactoryImpl();
60 336
        $this->propertyAccessFactory = new PropertyAccessFactoryImpl();
61 336
    }
62
63
    /**
64
     * @param \ReflectionClass $subject
65
     *
66
     * @return EntityConfig
67
     * @throws SlumberException
68
     */
69 23
    public function getEntityConfig(\ReflectionClass $subject)
70
    {
71 23
        $config = new EntityConfig(
72 23
            $subject->name,
73 23
            $this->getCreator($subject),
74 23
            $this->getClassMarkers($subject),
75 23
            $this->getPropertyMarkersRecursive($subject)
76
        );
77
78 23
        return $config;
79
    }
80
81
    /**
82
     * @param PropertyMarkedForSlumber $marked
83
     *
84
     * @return PropertyMarkedForSlumber
85
     */
86 22
    protected function enrich(PropertyMarkedForSlumber $marked)
87
    {
88 22
        $marked->mapper = $this->mappings->createMapper($marked->marker);
89
90 22
        return $marked;
91
    }
92
93
    /**
94
     * @param \ReflectionClass $subject
95
     *
96
     * @return Creator
97
     */
98 23
    private function getCreator(\ReflectionClass $subject)
99
    {
100 23
        $validationContext = new ClassAnnotationValidationContext($this->serviceProvider, $subject);
101
102 23
        $creatorAnnotation = Psi::it($this->annotationReader->getClassAnnotations($subject))
103 23
            ->filter(new Psi\IsInstanceOf(ClassCreatorMarker::class))
104 23
            ->each($validationContext)
105 23
            ->getFirst();
106
107 23
        if ($creatorAnnotation instanceof ClassCreatorMarker) {
108 4
            return $creatorAnnotation->getCreator($this->creatorFactory);
109
        }
110
111 23
        return $this->creatorFactory->create($subject);
112
    }
113
114
    /**
115
     * @param \ReflectionClass $subject
116
     *
117
     * @return ClassMarker[]
118
     */
119 23
    private function getClassMarkers(\ReflectionClass $subject)
120
    {
121 23
        $validationContext = new ClassAnnotationValidationContext($this->serviceProvider, $subject);
122
123 23
        return Psi::it($this->annotationReader->getClassAnnotations($subject))
124 23
            ->filter(new Psi\IsInstanceOf(ClassMarker::class))
125 23
            ->each($validationContext)
126 23
            ->toArray();
127
    }
128
129
    /**
130
     * @param \ReflectionClass $subjectClass
131
     *
132
     * @return PropertyMarkedForSlumber[]
133
     */
134 23
    private function getPropertyMarkersRecursive(\ReflectionClass $subjectClass)
135
    {
136
        /** @var PropertyMarkedForSlumber[] $result */
137 23
        $result = [];
138
139
        // We climb up the inheritance ladder to also capture private properties that are not shadowed
140
        // by other properties on derived classes.
141 23
        while ($subjectClass instanceof \ReflectionClass && $subjectClass->isUserDefined()) {
142
143 22
            $this->getPropertyMarkersForClass($subjectClass, $result);
144
145 22
            $subjectClass = $subjectClass->getParentClass();
146
        }
147
148 23
        return array_values($result);
149
    }
150
151 22
    private function getPropertyMarkersForClass(\ReflectionClass $class, array &$result): void
152
    {
153 22
        $properties = $class->getProperties();
154
155 22
        foreach ($properties as $property) {
156
157 22
            $propertyName = $property->getName();
158
159 22
            if (! isset($result[$propertyName])) {
160
161 22
                $context = $this->getPropertyValidationContext($class, $property);
162 22
                $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...
163
164 22
                if ($marker) {
165 22
                    $result[$propertyName] = $this->enrich($marker);
166
                }
167
            }
168
        }
169 22
    }
170
171
    /**
172
     * @param \ReflectionClass    $cls
173
     * @param \ReflectionProperty $property
174
     *
175
     * @return PropertyAnnotationValidationContext
176
     */
177 22
    private function getPropertyValidationContext(\ReflectionClass $cls, \ReflectionProperty $property)
178
    {
179 22
        return new PropertyAnnotationValidationContext($this->serviceProvider, $cls, $property);
180
    }
181
182
    /**
183
     * @param PropertyAnnotationValidationContext $context
184
     *
185
     * @return PropertyMarkedForSlumber|null
186
     */
187 22
    private function getPropertyAnnotationsOfType(PropertyAnnotationValidationContext $context)
188
    {
189 22
        $annotations = $this->annotationReader->getPropertyAnnotations($context->property);
190
191
        // get the mapping marker like AsString() or AsObject() ...
192 22
        $marker = Psi::it($annotations)
193 22
            ->filter(new Psi\IsInstanceOf(PropertyMappingMarker::class))
194 22
            ->each($context)
195 22
            ->getFirst();
196
197 22
        if ($marker === null) {
198
            return null;
199
        }
200
201 22
        $newEntry = new PropertyMarkedForSlumber();
202
203 22
        $newEntry->name           = $context->property->getName();
204 22
        $newEntry->alias          = $marker->hasAlias() ? $marker->getAlias() : $context->property->getName();
205 22
        $newEntry->marker         = $marker;
206 22
        $newEntry->allMarkers     = Psi::it($annotations)
207 22
            ->filter(new Psi\IsInstanceOf(PropertyMarker::class))
208 22
            ->each($context)
209 22
            ->toArray();
210 22
        $newEntry->mapper         = $this->mappings->createMapper($marker);
211 22
        $newEntry->propertyAccess = $this->propertyAccessFactory->create($context->cls, $context->property);
212
213 22
        return $newEntry;
214
    }
215
}
216